mirror of
https://github.com/magefree/mage.git
synced 2025-12-25 13:02:06 -08:00
Merge branch 'master' into master
This commit is contained in:
commit
6a114ac902
99 changed files with 3416 additions and 901 deletions
|
|
@ -30,7 +30,6 @@ package mage.abilities;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.Mana;
|
||||
|
|
@ -39,6 +38,7 @@ import mage.abilities.costs.AlternativeSourceCosts;
|
|||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
|
||||
import mage.abilities.costs.OptionalAdditionalSourceCosts;
|
||||
import mage.abilities.costs.VariableCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
|
|
@ -50,8 +50,8 @@ import mage.abilities.effects.ContinuousEffect;
|
|||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.Effects;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.BasicManaEffect;
|
||||
import mage.abilities.effects.common.DynamicManaEffect;
|
||||
import mage.abilities.effects.common.ManaEffect;
|
||||
import mage.abilities.keyword.FlashbackAbility;
|
||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||
import mage.cards.Card;
|
||||
|
|
@ -284,6 +284,9 @@ public abstract class AbilityImpl implements Ability {
|
|||
this.getManaCostsToPay().clear();
|
||||
}
|
||||
}
|
||||
if (modes.getAdditionalCost() != null) {
|
||||
((OptionalAdditionalModeSourceCosts) modes.getAdditionalCost()).addOptionalAdditionalModeCosts(this, game);
|
||||
}
|
||||
// 20130201 - 601.2b
|
||||
// If the spell has alternative or additional costs that will be paid as it's being cast such
|
||||
// as buyback, kicker, or convoke costs (see rules 117.8 and 117.9), the player announces his
|
||||
|
|
@ -415,8 +418,8 @@ public abstract class AbilityImpl implements Ability {
|
|||
Effect effect = getEffects().get(0);
|
||||
if (effect instanceof DynamicManaEffect) {
|
||||
mana = ((DynamicManaEffect) effect).getMana(game, this);
|
||||
} else if (effect instanceof BasicManaEffect) {
|
||||
mana = ((BasicManaEffect) effect).getMana(game, this);
|
||||
} else if (effect instanceof ManaEffect) {
|
||||
mana = ((ManaEffect) effect).getMana(game, this);
|
||||
}
|
||||
if (mana != null && mana.getAny() == 0) { // if mana == null or Any > 0 the event has to be fired in the mana effect to know which mana was produced
|
||||
ManaEvent event = new ManaEvent(GameEvent.EventType.TAPPED_FOR_MANA, sourceId, sourceId, controllerId, mana);
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
private boolean eachModeMoreThanOnce; // each mode can be selected multiple times during one choice
|
||||
private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists
|
||||
private final LinkedHashMap<UUID, Mode> duplicateModes = new LinkedHashMap<>();
|
||||
private OptionalAdditionalModeSourceCosts optionalAdditionalModeSourceCosts = null; // only set if costs have to be paid
|
||||
|
||||
public Modes() {
|
||||
this.currentMode = new Mode();
|
||||
|
|
@ -87,6 +88,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
this.modeChooser = modes.modeChooser;
|
||||
this.eachModeOnlyOnce = modes.eachModeOnlyOnce;
|
||||
this.eachModeMoreThanOnce = modes.eachModeMoreThanOnce;
|
||||
this.optionalAdditionalModeSourceCosts = modes.optionalAdditionalModeSourceCosts;
|
||||
}
|
||||
|
||||
public Modes copy() {
|
||||
|
|
@ -186,7 +188,7 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
if (card != null) {
|
||||
for (Ability modeModifyingAbility : card.getAbilities()) {
|
||||
if (modeModifyingAbility instanceof OptionalAdditionalModeSourceCosts) {
|
||||
((OptionalAdditionalModeSourceCosts) modeModifyingAbility).addOptionalAdditionalModeCosts(source, game);
|
||||
((OptionalAdditionalModeSourceCosts) modeModifyingAbility).changeModes(source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -385,4 +387,12 @@ public class Modes extends LinkedHashMap<UUID, Mode> {
|
|||
this.eachModeMoreThanOnce = eachModeMoreThanOnce;
|
||||
}
|
||||
|
||||
public OptionalAdditionalModeSourceCosts getAdditionalCost() {
|
||||
return optionalAdditionalModeSourceCosts;
|
||||
}
|
||||
|
||||
public void setAdditionalCost(OptionalAdditionalModeSourceCosts optionalAdditionalModeSourceCosts) {
|
||||
this.optionalAdditionalModeSourceCosts = optionalAdditionalModeSourceCosts;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@
|
|||
*/
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
|
@ -48,7 +48,7 @@ public class BlocksOrBecomesBlockedTriggeredAbility extends TriggeredAbilityImpl
|
|||
protected boolean setTargetPointer;
|
||||
|
||||
public BlocksOrBecomesBlockedTriggeredAbility(Effect effect, boolean optional) {
|
||||
this(effect, new FilterCreaturePermanent(), optional, null, false);
|
||||
this(effect, StaticFilters.FILTER_PERMANENT_CREATURE, optional, null, false);
|
||||
}
|
||||
|
||||
public BlocksOrBecomesBlockedTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author emerald000
|
||||
*/
|
||||
public class CantHaveMoreThanAmountCountersSourceAbility extends SimpleStaticAbility {
|
||||
|
||||
private final CounterType counterType;
|
||||
private final int amount;
|
||||
|
||||
public CantHaveMoreThanAmountCountersSourceAbility(CounterType counterType, int amount) {
|
||||
super(Zone.BATTLEFIELD, new CantHaveMoreThanAmountCountersSourceEffect(counterType, amount));
|
||||
this.counterType = counterType;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
private CantHaveMoreThanAmountCountersSourceAbility(CantHaveMoreThanAmountCountersSourceAbility ability) {
|
||||
super(ability);
|
||||
this.counterType = ability.counterType;
|
||||
this.amount = ability.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Rasputin can't have more than " + CardUtil.numberToText(this.amount) + " " + this.counterType.getName() + " counters on it.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CantHaveMoreThanAmountCountersSourceAbility copy() {
|
||||
return new CantHaveMoreThanAmountCountersSourceAbility(this);
|
||||
}
|
||||
|
||||
public CounterType getCounterType() {
|
||||
return this.counterType;
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
}
|
||||
|
||||
class CantHaveMoreThanAmountCountersSourceEffect extends ReplacementEffectImpl {
|
||||
|
||||
private final CounterType counterType;
|
||||
private final int amount;
|
||||
|
||||
CantHaveMoreThanAmountCountersSourceEffect(CounterType counterType, int amount) {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment, false);
|
||||
this.counterType = counterType;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
CantHaveMoreThanAmountCountersSourceEffect(final CantHaveMoreThanAmountCountersSourceEffect effect) {
|
||||
super(effect);
|
||||
this.counterType = effect.counterType;
|
||||
this.amount = effect.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == EventType.ADD_COUNTER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
if (permanent == null) {
|
||||
permanent = game.getPermanentEntering(event.getTargetId());
|
||||
}
|
||||
return permanent != null
|
||||
&& permanent.getId().equals(source.getSourceId())
|
||||
&& event.getData().equals(this.counterType.getName())
|
||||
&& permanent.getCounters(game).getCount(this.counterType) == this.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CantHaveMoreThanAmountCountersSourceEffect copy() {
|
||||
return new CantHaveMoreThanAmountCountersSourceEffect(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -34,9 +34,11 @@ import mage.game.Game;
|
|||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
||||
public interface OptionalAdditionalModeSourceCosts {
|
||||
|
||||
void addOptionalAdditionalModeCosts(Ability ability, Game game);
|
||||
|
||||
void changeModes(Ability ability, Game game);
|
||||
|
||||
String getCastMessageSuffix();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,11 +95,11 @@ public class ExileFromGraveCost extends CostImpl {
|
|||
}
|
||||
exiledCards.add(card);
|
||||
}
|
||||
Cards cardsToExile = new CardsImpl();
|
||||
cardsToExile.addAll(exiledCards);
|
||||
controller.moveCards(cardsToExile, Zone.EXILED, ability, game);
|
||||
paid = true;
|
||||
}
|
||||
Cards cardsToExile = new CardsImpl();
|
||||
cardsToExile.addAll(exiledCards);
|
||||
controller.moveCards(cardsToExile, Zone.EXILED, ability, game);
|
||||
paid = true;
|
||||
|
||||
}
|
||||
return paid;
|
||||
|
|
|
|||
|
|
@ -25,17 +25,18 @@
|
|||
* 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.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.PreventionEffectImpl;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.target.Target;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
/**
|
||||
* @author nantuko
|
||||
|
|
@ -71,8 +72,15 @@ public class PreventDamageByTargetEffect extends PreventionEffectImpl {
|
|||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!this.used && super.applies(event, source, game)) {
|
||||
MageObject mageObject = game.getObject(event.getSourceId());
|
||||
if (mageObject instanceof Spell){
|
||||
return this.getTargetPointer().getTargets(game, source).contains(mageObject.getId());
|
||||
if (mageObject != null
|
||||
&& (mageObject.getCardType().contains(CardType.INSTANT) || mageObject.getCardType().contains(CardType.SORCERY))) {
|
||||
for (Target target : source.getTargets()) {
|
||||
if (target instanceof TargetSpell) {
|
||||
if (((TargetSpell) target).getSourceIds().contains(event.getSourceId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.getTargetPointer().getTargets(game, source).contains(event.getSourceId());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public class BecomesSubtypeAllEffect extends ContinuousEffectImpl {
|
|||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source,
|
||||
Game game) {
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, game)) {
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
|
||||
if (permanent != null) {
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
|
|
@ -86,10 +86,8 @@ public class BecomesSubtypeAllEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (duration.equals(Duration.Custom)) {
|
||||
discard();
|
||||
}
|
||||
} else if (duration.equals(Duration.Custom)) {
|
||||
discard();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ import mage.target.common.TargetCardInLibrary;
|
|||
*/
|
||||
public class SearchLibraryWithLessCMCPutInPlayEffect extends OneShotEffect {
|
||||
|
||||
private FilterCard filter;
|
||||
private final FilterCard filter;
|
||||
|
||||
public SearchLibraryWithLessCMCPutInPlayEffect() {
|
||||
this(new FilterCard());
|
||||
|
|
@ -66,8 +66,9 @@ public class SearchLibraryWithLessCMCPutInPlayEffect extends OneShotEffect {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
filter.add(new ConvertedManaCostPredicate(Filter.ComparisonType.LessThan, source.getManaCostsToPay().getX() + 1));
|
||||
TargetCardInLibrary target = new TargetCardInLibrary(filter);
|
||||
FilterCard advancedFilter = filter.copy(); // never change static objects so copy the object here before
|
||||
advancedFilter.add(new ConvertedManaCostPredicate(Filter.ComparisonType.LessThan, source.getManaCostsToPay().getX() + 1));
|
||||
TargetCardInLibrary target = new TargetCardInLibrary(advancedFilter);
|
||||
if (controller.searchLibrary(target, game)) {
|
||||
if (!target.getTargets().isEmpty()) {
|
||||
Card card = controller.getLibrary().getCard(target.getFirstTarget(), game);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import mage.abilities.effects.OneShotEffect;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.SplitCard;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
|
|
@ -120,15 +121,13 @@ class CascadeEffect extends OneShotEffect {
|
|||
break;
|
||||
}
|
||||
controller.moveCardsToExile(card, source, game, true, exile.getId(), exile.getName());
|
||||
} while (controller.isInGame() && card.getCardType().contains(CardType.LAND) || card.getConvertedManaCost() >= sourceCost);
|
||||
} while (controller.isInGame() && (card.getCardType().contains(CardType.LAND) || !cardThatCostsLess(sourceCost, card, game)));
|
||||
|
||||
controller.getLibrary().reset(); // set back empty draw state if that caused an empty draw
|
||||
|
||||
if (card != null) {
|
||||
if (controller.chooseUse(outcome, "Use cascade effect on " + card.getLogName() + '?', source, game)) {
|
||||
if (controller.cast(card.getSpellAbility(), game, true)) {
|
||||
exile.remove(card.getId());
|
||||
}
|
||||
}
|
||||
if (controller.chooseUse(outcome, "Use cascade effect on " + card.getLogName() + "?", source, game)) {
|
||||
controller.cast(card.getSpellAbility(), game, true);
|
||||
}
|
||||
// Move the remaining cards to the buttom of the library in a random order
|
||||
Cards cardsFromExile = new CardsImpl();
|
||||
|
|
@ -148,4 +147,12 @@ class CascadeEffect extends OneShotEffect {
|
|||
return new CascadeEffect(this);
|
||||
}
|
||||
|
||||
private boolean cardThatCostsLess(int value, Card card, Game game) {
|
||||
if (card instanceof SplitCard) {
|
||||
return ((SplitCard) card).getLeftHalfCard().getConvertedManaCost() < value
|
||||
|| ((SplitCard) card).getRightHalfCard().getConvertedManaCost() < value;
|
||||
} else {
|
||||
return card.getConvertedManaCost() < value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,22 +105,17 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addOptionalAdditionalModeCosts(Ability ability, Game game) {
|
||||
public void changeModes(Ability ability, Game game) {
|
||||
if (ability instanceof SpellAbility) {
|
||||
Player player = game.getPlayer(controllerId);
|
||||
if (player != null) {
|
||||
this.resetCosts();
|
||||
if (additionalCost != null) {
|
||||
if (player.chooseUse(Outcome.Benefit, "Pay " + additionalCost.getText(false) + " ?", ability, game)) {
|
||||
if (additionalCost.canPay(ability, ability.getSourceId(), ability.getControllerId(), game)
|
||||
&& player.chooseUse(Outcome.Benefit, "Pay " + additionalCost.getText(false) + " ?", ability, game)) {
|
||||
|
||||
additionalCost.activate();
|
||||
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) {
|
||||
Cost cost = (Cost) it.next();
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
} else {
|
||||
ability.getCosts().add(cost.copy());
|
||||
}
|
||||
}
|
||||
ability.getModes().setAdditionalCost(this);
|
||||
ability.getModes().setMinModes(2);
|
||||
ability.getModes().setMaxModes(2);
|
||||
}
|
||||
|
|
@ -129,6 +124,20 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOptionalAdditionalModeCosts(Ability ability, Game game) {
|
||||
if (additionalCost.isActivated()) {
|
||||
for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext();) {
|
||||
Cost cost = (Cost) it.next();
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
} else {
|
||||
ability.getCosts().add(cost.copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
@Override
|
||||
public Abilities<Ability> getAbilities(Game game) {
|
||||
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(objectId);
|
||||
if (otherAbilities == null) {
|
||||
if (otherAbilities == null || otherAbilities.isEmpty()) {
|
||||
return abilities;
|
||||
}
|
||||
Abilities<Ability> all = new AbilitiesImpl<>();
|
||||
|
|
|
|||
|
|
@ -30,11 +30,16 @@ package mage.cards.repository;
|
|||
import com.j256.ormlite.field.DataType;
|
||||
import com.j256.ormlite.field.DatabaseField;
|
||||
import com.j256.ormlite.table.DatabaseTable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.common.PlanswalkerEntersWithLoyalityCountersAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardGraphicInfo;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.FrameStyle;
|
||||
|
|
@ -45,11 +50,6 @@ import mage.constants.Rarity;
|
|||
import mage.constants.SpellAbilityType;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
|
|
@ -205,7 +205,7 @@ public class CardInfo {
|
|||
}
|
||||
|
||||
public Card getCard() {
|
||||
return CardImpl.createCard(className, new CardSetInfo(name, setCode, cardNumber, rarity));
|
||||
return CardImpl.createCard(className, new CardSetInfo(name, setCode, cardNumber, rarity, new CardGraphicInfo(FrameStyle.valueOf(frameStyle), variousArt)));
|
||||
}
|
||||
|
||||
public Card getMockCard() {
|
||||
|
|
@ -216,7 +216,9 @@ public class CardInfo {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean usesVariousArt() { return variousArt; }
|
||||
public boolean usesVariousArt() {
|
||||
return variousArt;
|
||||
}
|
||||
|
||||
public ObjectColor getColor() {
|
||||
ObjectColor color = new ObjectColor();
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public enum CardRepository {
|
|||
// raise this if db structure was changed
|
||||
private static final long CARD_DB_VERSION = 50;
|
||||
// raise this if new cards were added to the server
|
||||
private static final long CARD_CONTENT_VERSION = 69;
|
||||
private static final long CARD_CONTENT_VERSION = 70;
|
||||
private final TreeSet<String> landTypes = new TreeSet();
|
||||
private Dao<CardInfo, Object> cardDao;
|
||||
private Set<String> classNames;
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ public enum CounterType {
|
|||
DEVOTION("devotion"),
|
||||
DIVINITY("divinity"),
|
||||
DOOM("doom"),
|
||||
DREAM("dream"),
|
||||
ELIXIR("elixir"),
|
||||
ENERGY("energy"),
|
||||
EON("eon"),
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ public class StaticFilters {
|
|||
public static final FilterNonlandCard FILTER_CARD_NON_LAND = new FilterNonlandCard();
|
||||
public static final FilterCard FILTER_CARD_ARTIFACT_OR_CREATURE = new FilterCard("artifact or creature card");
|
||||
|
||||
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE = new FilterCreaturePermanent();
|
||||
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_GOBLINS = new FilterCreaturePermanent("Goblin", "Goblin creatures");
|
||||
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_SLIVERS = new FilterCreaturePermanent("Sliver", "Sliver creatures");
|
||||
|
||||
|
|
|
|||
179
Mage/src/main/java/mage/game/GameCanadianHighlanderImpl.java
Normal file
179
Mage/src/main/java/mage/game/GameCanadianHighlanderImpl.java
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.game;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.constants.MultiplayerAttackOption;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import mage.game.turn.TurnMod;
|
||||
import mage.players.Player;
|
||||
|
||||
public abstract class GameCanadianHighlanderImpl extends GameImpl {
|
||||
|
||||
protected boolean startingPlayerSkipsDraw = true;
|
||||
protected Map<UUID, String> usedMulligans = new LinkedHashMap<>();
|
||||
|
||||
public GameCanadianHighlanderImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range, int freeMulligans, int startLife) {
|
||||
super(attackOption, range, 0, startLife);
|
||||
}
|
||||
|
||||
public GameCanadianHighlanderImpl(final GameCanadianHighlanderImpl game) {
|
||||
super(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init(UUID choosingPlayerId) {
|
||||
super.init(choosingPlayerId);
|
||||
state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW));
|
||||
}
|
||||
|
||||
private String getNextMulligan(String mulligan) {
|
||||
if (mulligan.equals("7")) {
|
||||
return "6a";
|
||||
} else if (mulligan.equals("6a")) {
|
||||
return "6b";
|
||||
} else if (mulligan.equals("6b")) {
|
||||
return "5a";
|
||||
} else if (mulligan.equals("5a")) {
|
||||
return "5b";
|
||||
} else if (mulligan.equals("5b")) {
|
||||
return "4a";
|
||||
} else if (mulligan.equals("4a")) {
|
||||
return "4b";
|
||||
} else if (mulligan.equals("4b")) {
|
||||
return "3a";
|
||||
} else if (mulligan.equals("3a")) {
|
||||
return "3b";
|
||||
} else if (mulligan.equals("3b")) {
|
||||
return "2a";
|
||||
} else if (mulligan.equals("2a")) {
|
||||
return "2b";
|
||||
} else if (mulligan.equals("2b")) {
|
||||
return "1a";
|
||||
} else if (mulligan.equals("1a")) {
|
||||
return "1b";
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
|
||||
private int getNextMulliganNum(String mulligan) {
|
||||
if (mulligan.equals("7")) {
|
||||
return 6;
|
||||
} else if (mulligan.equals("6a")) {
|
||||
return 6;
|
||||
} else if (mulligan.equals("6b")) {
|
||||
return 5;
|
||||
} else if (mulligan.equals("5a")) {
|
||||
return 5;
|
||||
} else if (mulligan.equals("5b")) {
|
||||
return 4;
|
||||
} else if (mulligan.equals("4a")) {
|
||||
return 4;
|
||||
} else if (mulligan.equals("4b")) {
|
||||
return 3;
|
||||
} else if (mulligan.equals("3a")) {
|
||||
return 3;
|
||||
} else if (mulligan.equals("3b")) {
|
||||
return 2;
|
||||
} else if (mulligan.equals("2a")) {
|
||||
return 2;
|
||||
} else if (mulligan.equals("2b")) {
|
||||
return 1;
|
||||
} else if (mulligan.equals("1a")) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int mulliganDownTo(UUID playerId) {
|
||||
Player player = getPlayer(playerId);
|
||||
int deduction = 1;
|
||||
int numToMulliganTo = -1;
|
||||
if (usedMulligans != null) {
|
||||
String mulliganCode = "7";
|
||||
if (usedMulligans.containsKey(player.getId())) {
|
||||
mulliganCode = usedMulligans.get(player.getId());
|
||||
}
|
||||
numToMulliganTo = getNextMulliganNum(mulliganCode);
|
||||
}
|
||||
if (numToMulliganTo == -1) {
|
||||
return player.getHand().size() - deduction;
|
||||
}
|
||||
return numToMulliganTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mulligan(UUID playerId) {
|
||||
Player player = getPlayer(playerId);
|
||||
int numCards = player.getHand().size();
|
||||
int numToMulliganTo = numCards;
|
||||
player.getLibrary().addAll(player.getHand().getCards(this), this);
|
||||
player.getHand().clear();
|
||||
player.shuffleLibrary(null, this);
|
||||
if (usedMulligans != null) {
|
||||
String mulliganCode = "7";
|
||||
if (usedMulligans.containsKey(player.getId())) {
|
||||
mulliganCode = usedMulligans.get(player.getId());
|
||||
}
|
||||
numToMulliganTo = getNextMulliganNum(mulliganCode);
|
||||
usedMulligans.put(player.getId(), getNextMulligan(mulliganCode));
|
||||
}
|
||||
fireInformEvent(new StringBuilder(player.getLogName())
|
||||
.append(" mulligans to ")
|
||||
.append(Integer.toString(numToMulliganTo))
|
||||
.append(numToMulliganTo == 1 ? " card" : " cards").toString());
|
||||
player.drawCards(numToMulliganTo, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endMulligan(UUID playerId) {
|
||||
super.endMulligan(playerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> getOpponents(UUID playerId) {
|
||||
Set<UUID> opponents = new HashSet<>();
|
||||
for (UUID opponentId : getState().getPlayersInRange(playerId, this)) {
|
||||
if (!opponentId.equals(playerId)) {
|
||||
opponents.add(opponentId);
|
||||
}
|
||||
}
|
||||
return opponents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpponent(Player player, UUID playerToCheck) {
|
||||
return !player.getId().equals(playerToCheck);
|
||||
}
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ import mage.abilities.OpeningHandAction;
|
|||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.common.AttachableToRestrictedAbility;
|
||||
import mage.abilities.common.CantHaveMoreThanAmountCountersSourceAbility;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.ContinuousEffects;
|
||||
import mage.abilities.effects.Effect;
|
||||
|
|
@ -1923,6 +1924,19 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
perm.getCounters(this).removeCounter(CounterType.M1M1, min);
|
||||
}
|
||||
|
||||
// 20170120 - 704.5s
|
||||
// If a permanent with an ability that says it can't have more than N counters of a certain kind on it
|
||||
// has more than N counters of that kind on it, all but N of those counters are removed from it.
|
||||
for (Ability ability : perm.getAbilities(this)) {
|
||||
if (ability instanceof CantHaveMoreThanAmountCountersSourceAbility) {
|
||||
CantHaveMoreThanAmountCountersSourceAbility counterAbility = (CantHaveMoreThanAmountCountersSourceAbility) ability;
|
||||
int count = perm.getCounters(this).getCount(counterAbility.getCounterType());
|
||||
if (count > counterAbility.getAmount()) {
|
||||
perm.removeCounters(counterAbility.getCounterType().getName(), count - counterAbility.getAmount(), this);
|
||||
somethingHappened = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//201300713 - 704.5j
|
||||
// If a player controls two or more planeswalkers that share a planeswalker type, that player
|
||||
|
|
|
|||
|
|
@ -399,7 +399,9 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
}
|
||||
} else {
|
||||
Player defender = game.getPlayer(defenderId);
|
||||
defender.damage(amount, attacker.getId(), game, true, true);
|
||||
if (defender.isInGame()) {
|
||||
defender.damage(amount, attacker.getId(), game, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public class UserData implements Serializable {
|
|||
protected boolean passPriorityActivation;
|
||||
protected boolean autoOrderTrigger;
|
||||
protected boolean useFirstManaAbility = false;
|
||||
private String userIdStr;
|
||||
|
||||
protected String matchHistory;
|
||||
protected int matchQuitRatio;
|
||||
|
|
@ -36,7 +37,7 @@ public class UserData implements Serializable {
|
|||
public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced,
|
||||
boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps,
|
||||
String flagName, boolean askMoveToGraveOrder, boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted,
|
||||
boolean passPriorityCast, boolean passPriorityActivation, boolean autoOrderTrigger, boolean useFirstManaAbility) {
|
||||
boolean passPriorityCast, boolean passPriorityActivation, boolean autoOrderTrigger, boolean useFirstManaAbility, String userIdStr) {
|
||||
this.groupId = userGroup.getGroupId();
|
||||
this.avatarId = avatarId;
|
||||
this.showAbilityPickerForced = showAbilityPickerForced;
|
||||
|
|
@ -55,6 +56,7 @@ public class UserData implements Serializable {
|
|||
this.matchQuitRatio = 0;
|
||||
this.tourneyHistory = "";
|
||||
this.tourneyQuitRatio = 0;
|
||||
this.userIdStr = userIdStr;
|
||||
}
|
||||
|
||||
public void update(UserData userData) {
|
||||
|
|
@ -72,11 +74,12 @@ public class UserData implements Serializable {
|
|||
this.passPriorityActivation = userData.passPriorityActivation;
|
||||
this.autoOrderTrigger = userData.autoOrderTrigger;
|
||||
this.useFirstManaAbility = userData.useFirstManaAbility;
|
||||
this.userIdStr = userData.userIdStr;
|
||||
// todo: why we don't copy user stats here?
|
||||
}
|
||||
|
||||
public static UserData getDefaultUserDataView() {
|
||||
return new UserData(UserGroup.DEFAULT, 0, false, false, true, null, getDefaultFlagName(), false, true, true, false, false, false, false);
|
||||
return new UserData(UserGroup.DEFAULT, 0, false, false, true, null, getDefaultFlagName(), false, true, true, false, false, false, false, "");
|
||||
}
|
||||
|
||||
public void setGroupId(int groupId) {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import mage.game.stack.StackObject;
|
|||
public class TargetSpell extends TargetObject {
|
||||
|
||||
protected final FilterSpell filter;
|
||||
private final Set<UUID> sourceIds = new HashSet<>();
|
||||
|
||||
public TargetSpell() {
|
||||
this(1, 1, new FilterSpell());
|
||||
|
|
@ -68,6 +69,7 @@ public class TargetSpell extends TargetObject {
|
|||
public TargetSpell(final TargetSpell target) {
|
||||
super(target);
|
||||
this.filter = target.filter.copy();
|
||||
this.sourceIds.addAll(target.sourceIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -134,4 +136,18 @@ public class TargetSpell extends TargetObject {
|
|||
&& game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId())
|
||||
&& filter.match(stackObject, sourceID, sourceControllerId, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTarget(UUID id, Ability source, Game game, boolean skipEvent) {
|
||||
Spell spell = game.getStack().getSpell(id);
|
||||
if (spell != null) { // remember the original sourceID
|
||||
sourceIds.add(spell.getSourceId());
|
||||
}
|
||||
super.addTarget(id, source, game, skipEvent);
|
||||
}
|
||||
|
||||
public Set<UUID> getSourceIds() {
|
||||
return sourceIds;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue