diff --git a/Mage/src/mage/Constants.java b/Mage/src/mage/Constants.java index e1214cdd3fd..7ed26c66a14 100644 --- a/Mage/src/mage/Constants.java +++ b/Mage/src/mage/Constants.java @@ -256,6 +256,39 @@ public final class Constants { ANY, YOU, NOT_YOU, OPPONENT } + public enum RangeOfInfluence { + ONE(1), + TWO(2), + ALL(0); + + private int range; + + RangeOfInfluence(int range) { + this.range = range; + } + + public int getRange() { + return range; + } + } + + public enum MultiplayerAttackOption { + MULITPLE("Attack Multiple Players"), + LEFT("Attack Left"), + RIGHT("Attack Right"); + + private String text; + + MultiplayerAttackOption(String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } + } + public static List PlaneswalkerTypes = new ArrayList() {{add("Ajani"); add("Bolas"); add("Chandra"); add("Elspeth");add("Garruk"); add("Jace"); add("Liliana"); add("Nissa"); add("Sarkhan"); add("Sorin"); add("Tezzeret");}}; diff --git a/Mage/src/mage/abilities/ActivatedAbilityImpl.java b/Mage/src/mage/abilities/ActivatedAbilityImpl.java index b1700bcfdea..bd8b4cf9a9f 100644 --- a/Mage/src/mage/abilities/ActivatedAbilityImpl.java +++ b/Mage/src/mage/abilities/ActivatedAbilityImpl.java @@ -131,7 +131,7 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa if (!controlsAbility(playerId, game)) return false; //20091005 - 602.5d/602.5e - if ((timing == TimingRule.INSTANT || game.canPlaySorcery(playerId)) && costs.canPay(playerId, game) && targets.canChoose(sourceId, game)) { + if ((timing == TimingRule.INSTANT || game.canPlaySorcery(playerId)) && costs.canPay(playerId, game) && targets.canChoose(sourceId, playerId, game)) { return true; } return false; diff --git a/Mage/src/mage/abilities/SpellAbility.java b/Mage/src/mage/abilities/SpellAbility.java index d831b618ad5..da31f873c18 100644 --- a/Mage/src/mage/abilities/SpellAbility.java +++ b/Mage/src/mage/abilities/SpellAbility.java @@ -49,7 +49,7 @@ public class SpellAbility extends ActivatedAbilityImpl { @Override public boolean canActivate(UUID playerId, Game game) { if ((game.getObject(sourceId).getCardType().contains(CardType.INSTANT) || game.canPlaySorcery(playerId)) && - costs.canPay(playerId, game) && targets.canChoose(sourceId, game)) { + costs.canPay(playerId, game) && targets.canChoose(sourceId, playerId, game)) { return true; } return false; diff --git a/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java b/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java index b23f4700682..63fd3e4dfae 100644 --- a/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java +++ b/Mage/src/mage/abilities/costs/common/SacrificeTargetCost.java @@ -30,11 +30,9 @@ package mage.abilities.costs.common; import java.util.UUID; import mage.Constants.Outcome; -import mage.Constants.TargetController; import mage.abilities.costs.CostImpl; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.target.TargetPermanent; import mage.target.common.TargetSacrificePermanent; /** @@ -64,7 +62,7 @@ public class SacrificeTargetCost extends CostImpl { @Override public boolean canPay(UUID playerId, Game game) { - return target.canChoose(playerId, game); + return target.canChoose(playerId, playerId, game); } } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffect.java b/Mage/src/mage/abilities/effects/ContinuousEffect.java index f9a49b23269..30c4e8c358a 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffect.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffect.java @@ -41,8 +41,8 @@ import mage.game.Game; public interface ContinuousEffect extends Effect { public Duration getDuration(); -// public Layer getLayer(); -// public SubLayer getSubLayer(); public Date getTimestamp(); public boolean apply(Layer layer, SubLayer sublayer, Game game); + public boolean hasLayer(Layer layer); + } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java b/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java index 66d8d75029f..fd62414fbc9 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffectImpl.java @@ -76,5 +76,9 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu return timestamp; } + @Override + public boolean hasLayer(Layer layer) { + return this.layer == layer; + } } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index d92192e36cf..139638ea9de 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -92,47 +92,6 @@ public class ContinuousEffects implements Serializable { } -// private List getLayer(Layer layer, SubLayer sublayer) { -// List layerEffects = new ArrayList(); -// for (ContinuousEffect effect: effects) { -// if (effect.getLayer() == layer && effect.getSubLayer() == sublayer) { -// layerEffects.add(effect); -// } -// } -// Collections.sort(layerEffects, new TimestampSorter()); -// return layerEffects; -// } - -// private List GetApplicablePreventionEffects(GameEvent event, Game game) { -// List effects = new ArrayList(); -// for (IEffect effect: this) { -// if (effect instanceof PreventionEffect && ((PreventionEffect)effect).Applies(event, game)) { -// effects.add((PreventionEffect)effect); -// } -// } -// return effects; -// } - -// public List GetApplicablePreventionEffects(IMageObject source, IPermanent target, Game game) { -// List effects = new ArrayList(); -// for (IEffect effect: this) { -// if (effect instanceof PreventionEffect && ((PreventionEffect)effect).Applies(source, target, game)) { -// effects.add((PreventionEffect)effect); -// } -// } -// return effects; -// } -// -// public List GetApplicablePreventionEffects(IMageObject source, IPlayer target, Game game) { -// List effects = new ArrayList(); -// for (IEffect effect: this) { -// if (effect instanceof PreventionEffect && ((PreventionEffect)effect).Applies(source, target, game)) { -// effects.add((PreventionEffect)effect); -// } -// } -// return effects; -// } - private List getApplicableReplacementEffects(GameEvent event, Game game) { List replacementEffects = new ArrayList(); for (Effect effect: effects) { @@ -180,18 +139,6 @@ public class ContinuousEffects implements Serializable { } } -// if (!caught) { -// List pEffects = GetApplicablePreventionEffects(event, game); -// if (pEffects.size() > 0) { -// if (pEffects.size() == 1) { -// caught = pEffects.get(0).ReplaceEvent(event, game); -// } -// else { -// //TODO: handle multiple -// } -// } -// } - return caught; } @@ -260,7 +207,7 @@ public class ContinuousEffects implements Serializable { } protected void applyCounters(Game game) { - for (Permanent permanent: game.getBattlefield().getActivePermanents(CardType.CREATURE)) { + for (Permanent permanent: game.getBattlefield().getAllActivePermanents(CardType.CREATURE)) { for (BoostCounter counter: permanent.getCounters().getBoostCounters()) { permanent.addPower(counter.getPower() * counter.getCount()); permanent.addToughness(counter.getToughness() * counter.getCount()); diff --git a/Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java b/Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java index b84e0cba4ed..6e59253862e 100644 --- a/Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java +++ b/Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java @@ -32,7 +32,6 @@ import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.Outcome; import mage.Constants.SubLayer; -import mage.abilities.effects.ContinuousEffectImpl; import mage.filter.FilterPermanent; import mage.game.Game; @@ -53,9 +52,7 @@ public abstract class WhileControlsContinuousEffect extends ContinuousEffectImpl @Override public boolean apply(Game game) { - filter.getControllerId().clear(); - filter.getControllerId().add(this.source.getControllerId()); - if (game.getBattlefield().count(filter) > 0) { + if (game.getBattlefield().countAll(filter, this.source.getControllerId()) > 0) { return applyEffect(game); } return false; diff --git a/Mage/src/mage/abilities/effects/common/BecomesCreatureSourceEOTEffect.java b/Mage/src/mage/abilities/effects/common/BecomesCreatureSourceEOTEffect.java index 4a3e9a8a144..af7c73652a3 100644 --- a/Mage/src/mage/abilities/effects/common/BecomesCreatureSourceEOTEffect.java +++ b/Mage/src/mage/abilities/effects/common/BecomesCreatureSourceEOTEffect.java @@ -100,4 +100,9 @@ public class BecomesCreatureSourceEOTEffect extends ContinuousEffectImpl { return "Until end of turn {this} becomes a " + token.getDescription() + ". It's still a land"; } + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.PTChangingEffects_7 || layer == Layer.AbilityAddingRemovingEffects_6 || layer == Layer.ColorChangingEffects_5 || layer == layer.TypeChangingEffects_4; + } + } diff --git a/Mage/src/mage/abilities/effects/common/BoostControlledEffect.java b/Mage/src/mage/abilities/effects/common/BoostControlledEffect.java index c37db128852..be2e64e207a 100644 --- a/Mage/src/mage/abilities/effects/common/BoostControlledEffect.java +++ b/Mage/src/mage/abilities/effects/common/BoostControlledEffect.java @@ -61,9 +61,7 @@ public class BoostControlledEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game) { - filter.getControllerId().clear(); - filter.getControllerId().add(this.source.getControllerId()); - for (Permanent perm: game.getBattlefield().getActivePermanents(filter)) { + for (Permanent perm: game.getBattlefield().getAllActivePermanents(filter, this.source.getControllerId())) { perm.addPower(power); perm.addToughness(toughness); } diff --git a/Mage/src/mage/abilities/effects/common/DestroyAllControlledTargetEffect.java b/Mage/src/mage/abilities/effects/common/DestroyAllControlledTargetEffect.java index b416a35c695..4f196703e41 100644 --- a/Mage/src/mage/abilities/effects/common/DestroyAllControlledTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/DestroyAllControlledTargetEffect.java @@ -47,11 +47,9 @@ public class DestroyAllControlledTargetEffect extends OneShotEffect { this.filter = filter; } + @Override public boolean apply(Game game) { - filter.getControllerId().clear(); - filter.getControllerId().add(this.source.getFirstTarget()); - filter.setNotController(false); - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter)) { + for (Permanent permanent: game.getBattlefield().getAllActivePermanents(filter, this.source.getFirstTarget())) { permanent.destroy(this.source.getSourceId(), game, false); } return true; diff --git a/Mage/src/mage/abilities/effects/common/DestroyAllEffect.java b/Mage/src/mage/abilities/effects/common/DestroyAllEffect.java index 1359d671882..1ad1dbd12db 100644 --- a/Mage/src/mage/abilities/effects/common/DestroyAllEffect.java +++ b/Mage/src/mage/abilities/effects/common/DestroyAllEffect.java @@ -47,8 +47,9 @@ public class DestroyAllEffect extends OneShotEffect { this.filter = filter; } + @Override public boolean apply(Game game) { - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter)) { + for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, this.source.getControllerId(), game)) { permanent.destroy(this.source.getSourceId(), game, false); } return true; diff --git a/Mage/src/mage/abilities/effects/common/DestroyAllNamedPermanentsEffect.java b/Mage/src/mage/abilities/effects/common/DestroyAllNamedPermanentsEffect.java index 1036fb0a207..417c0374d7b 100644 --- a/Mage/src/mage/abilities/effects/common/DestroyAllNamedPermanentsEffect.java +++ b/Mage/src/mage/abilities/effects/common/DestroyAllNamedPermanentsEffect.java @@ -49,7 +49,7 @@ public class DestroyAllNamedPermanentsEffect extends OneShotEffect { String name = permanent.getName(); permanent.destroy(this.source.getSourceId(), game, false); - for (Permanent perm: game.getBattlefield().getActivePermanents()) { + for (Permanent perm: game.getBattlefield().getActivePermanents(this.source.getControllerId(), game)) { if (perm.getName().equals(name)) perm.destroy(this.source.getSourceId(), game, false); } diff --git a/Mage/src/mage/abilities/effects/common/EntersBattlefieldTappedUnlessControlsEffect.java b/Mage/src/mage/abilities/effects/common/EntersBattlefieldTappedUnlessControlsEffect.java new file mode 100644 index 00000000000..dbec15a5736 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/EntersBattlefieldTappedUnlessControlsEffect.java @@ -0,0 +1,59 @@ +/* + * 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.filter.FilterPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class EntersBattlefieldTappedUnlessControlsEffect extends EntersBattlefieldTappedEffect { + + FilterPermanent filter; + + public EntersBattlefieldTappedUnlessControlsEffect(FilterPermanent filter) { + this.filter = filter; + } + + @Override + public boolean replaceEvent(GameEvent event, Game game) { + if (game.getBattlefield().countAll(filter, this.source.getControllerId()) == 0) + return apply(game); + return false; + } + + @Override + public String getText() { + return super.getText() + " unless you control a " + filter.getMessage(); + } + +} diff --git a/Mage/src/mage/abilities/effects/common/GainAbilityControlledEffect.java b/Mage/src/mage/abilities/effects/common/GainAbilityControlledEffect.java index 574557f855f..7de93b2ec2b 100644 --- a/Mage/src/mage/abilities/effects/common/GainAbilityControlledEffect.java +++ b/Mage/src/mage/abilities/effects/common/GainAbilityControlledEffect.java @@ -59,9 +59,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game) { - permanentFilter.getControllerId().clear(); - permanentFilter.getControllerId().add(this.source.getControllerId()); - for (Permanent perm: game.getBattlefield().getActivePermanents(permanentFilter)) { + for (Permanent perm: game.getBattlefield().getAllActivePermanents(permanentFilter, this.source.getControllerId())) { perm.addAbility(ability); } return true; diff --git a/Mage/src/mage/abilities/effects/common/GainControlTargetEOTEffect.java b/Mage/src/mage/abilities/effects/common/GainControlTargetEOTEffect.java index c54a364f4dd..7e6482285a2 100644 --- a/Mage/src/mage/abilities/effects/common/GainControlTargetEOTEffect.java +++ b/Mage/src/mage/abilities/effects/common/GainControlTargetEOTEffect.java @@ -50,8 +50,7 @@ public class GainControlTargetEOTEffect extends ContinuousEffectImpl { public boolean apply(Game game) { Permanent permanent = game.getPermanent(this.source.getFirstTarget()); if (permanent != null) { - permanent.setControllerId(this.source.getControllerId()); - return true; + return permanent.changeControllerId(this.source.getControllerId(), game); } return false; } diff --git a/Mage/src/mage/filter/common/FilterCreaturePermanent.java b/Mage/src/mage/filter/common/FilterCreaturePermanent.java index 4d2ab6a8e0d..70fbf09388b 100644 --- a/Mage/src/mage/filter/common/FilterCreaturePermanent.java +++ b/Mage/src/mage/filter/common/FilterCreaturePermanent.java @@ -28,7 +28,6 @@ package mage.filter.common; -import java.util.UUID; import mage.Constants.CardType; import mage.filter.FilterPermanent; import mage.game.permanent.Permanent; diff --git a/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java b/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java index 95e865048dd..1145e2f6aa8 100644 --- a/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java +++ b/Mage/src/mage/filter/common/FilterPlaneswalkerOrPlayer.java @@ -29,6 +29,7 @@ package mage.filter.common; import java.util.List; +import java.util.Set; import java.util.UUID; import mage.filter.Filter; import mage.filter.FilterImpl; @@ -45,7 +46,7 @@ public class FilterPlaneswalkerOrPlayer extends FilterImpl implements Fi protected FilterPlaneswalkerPermanent planeswalkerFilter = new FilterPlaneswalkerPermanent(); protected FilterPlayer playerFilter = new FilterPlayer(); - public FilterPlaneswalkerOrPlayer(List defenders) { + public FilterPlaneswalkerOrPlayer(Set defenders) { super("planeswalker or player"); planeswalkerFilter.getControllerId().addAll(defenders); playerFilter.getPlayerId().addAll(defenders); diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index e48f34218f8..05a0b0e9b08 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -32,8 +32,10 @@ import mage.game.stack.SpellStack; import mage.MageObject; import java.io.Serializable; import java.util.Collection; -import java.util.List; +import java.util.Set; import java.util.UUID; +import mage.Constants.MultiplayerAttackOption; +import mage.Constants.RangeOfInfluence; import mage.MageItem; import mage.abilities.ActivatedAbility; import mage.abilities.TriggeredAbilities; @@ -56,9 +58,11 @@ import mage.players.Players; public interface Game extends MageItem, Serializable { - public String getGameType(); + public GameType getGameType(); public int getNumPlayers(); public int getLife(); + public RangeOfInfluence getRangeOfInfluence(); + public MultiplayerAttackOption getAttackOption(); //game data methods public MageObject getObject(UUID objectId); @@ -67,7 +71,7 @@ public interface Game extends MageItem, Serializable { public Player getPlayer(UUID playerId); public Players getPlayers(); public PlayerList getPlayerList(UUID playerId); - public List getOpponents(UUID controllerId); + public Set getOpponents(UUID controllerId); public Turn getTurn(); public int getTurnNum(); public boolean isMainPhase(); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 93eaa381e99..c0d3ec3e2bf 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -31,13 +31,15 @@ package mage.game; import mage.game.stack.SpellStack; import java.io.Serializable; import java.util.Collection; +import java.util.List; import java.util.Random; import java.util.Stack; import java.util.UUID; -import mage.Constants; import mage.Constants.CardType; +import mage.Constants.MultiplayerAttackOption; import mage.Constants.Outcome; import mage.Constants.PhaseStep; +import mage.Constants.RangeOfInfluence; import mage.Constants.Zone; import mage.MageObject; import mage.abilities.ActivatedAbility; @@ -83,9 +85,13 @@ public abstract class GameImpl implements Game, Serializable { protected UUID choosingPlayerId; protected Player winner; protected GameStates gameStates; + protected RangeOfInfluence range; + protected MultiplayerAttackOption attackOption; - public GameImpl() { + public GameImpl(MultiplayerAttackOption attackOption, RangeOfInfluence range) { id = UUID.randomUUID(); + this.range = range; + this.attackOption = attackOption; state = new GameState(); gameStates = new GameStates(); } @@ -100,6 +106,16 @@ public abstract class GameImpl implements Game, Serializable { state.addPlayer(player); } + @Override + public RangeOfInfluence getRangeOfInfluence() { + return range; + } + + @Override + public MultiplayerAttackOption getAttackOption() { + return attackOption; + } + @Override public Player getPlayer(UUID playerId) { return state.getPlayer(playerId); @@ -279,22 +295,20 @@ public abstract class GameImpl implements Game, Serializable { } @Override - public void quit(UUID playerId) { + public synchronized void quit(UUID playerId) { Player player = state.getPlayer(playerId); if (player != null) { - player.leaveGame(); + player.leaveGame(this); fireInformEvent(player.getName() + " has left the game."); - player.abort(); } } @Override - public void concede(UUID playerId) { + public synchronized void concede(UUID playerId) { Player player = state.getPlayer(playerId); if (player != null) { - player.concede(); + player.concede(this); fireInformEvent(player.getName() + " has conceded."); - player.abort(); } } @@ -304,7 +318,7 @@ public abstract class GameImpl implements Game, Serializable { while (!isGameOver()) { for (Player player: getPlayerList(activePlayerId)) { state.setPriorityPlayerId(player.getId()); - while (!player.isPassed() && !isGameOver()) { + while (!player.isPassed() && !player.hasLost() && !player.hasLeft()&& !isGameOver()) { checkStateAndTriggered(); if (isGameOver()) return; // resetPassed should be called if player performs any action @@ -320,6 +334,7 @@ public abstract class GameImpl implements Game, Serializable { state.getStack().resolve(this); applyEffects(); state.getPlayers().resetPassed(); + fireUpdatePlayersEvent(); saveState(); break; } @@ -331,7 +346,7 @@ public abstract class GameImpl implements Game, Serializable { protected boolean allPassed() { for (Player player: state.getPlayers().values()) { - if (!player.isPassed()) + if (!player.isPassed() && !player.hasLost() && !player.hasLeft()) return false; } return true; @@ -347,7 +362,7 @@ public abstract class GameImpl implements Game, Serializable { } @Override - public void applyEffects() { + public synchronized void applyEffects() { state.applyEffects(this); } @@ -406,7 +421,7 @@ public abstract class GameImpl implements Game, Serializable { somethingHappened = true; } } - for (Permanent perm: getBattlefield().getActivePermanents(CardType.CREATURE)) { + for (Permanent perm: getBattlefield().getAllActivePermanents(CardType.CREATURE)) { //20091005 - 704.5f if (perm.getToughness().getValue() == 0) { perm.moveToZone(Zone.GRAVEYARD, this, false); @@ -419,35 +434,37 @@ public abstract class GameImpl implements Game, Serializable { } } //20091005 - 704.5i - for (Permanent perm: getBattlefield().getActivePermanents(CardType.PLANESWALKER)) { + for (Permanent perm: getBattlefield().getAllActivePermanents(CardType.PLANESWALKER)) { if (perm.getLoyalty().getValue() == 0) { perm.moveToZone(Zone.GRAVEYARD, this, false); return true; } } - //20091005 - 704.5j + //20091005 - 704.5j, 801.14 FilterPlaneswalkerPermanent filterPlaneswalker = new FilterPlaneswalkerPermanent(); - if (getBattlefield().count(filterPlaneswalker) > 1) { //don't bother checking if less than 2 planeswalkers in play - for (String planeswalkerType: Constants.PlaneswalkerTypes) { - filterPlaneswalker.getSubtype().clear(); - filterPlaneswalker.getSubtype().add(planeswalkerType); - filterPlaneswalker.setScopeSubtype(ComparisonScope.Any); - if (getBattlefield().count(filterPlaneswalker) > 1) { - for (Permanent perm: getBattlefield().getActivePermanents(filterPlaneswalker)) { - perm.moveToZone(Zone.GRAVEYARD, this, false); + if (getBattlefield().countAll(filterPlaneswalker) > 1) { //don't bother checking if less than 2 planeswalkers in play + for (Permanent planeswalker: getBattlefield().getAllActivePermanents(CardType.PLANESWALKER)) { + for (String planeswalkertype: planeswalker.getSubtype()) { + filterPlaneswalker.getSubtype().clear(); + filterPlaneswalker.getSubtype().add(planeswalkertype); + filterPlaneswalker.setScopeSubtype(ComparisonScope.Any); + if (getBattlefield().count(filterPlaneswalker, planeswalker.getControllerId(), this) > 1) { + for (Permanent perm: getBattlefield().getActivePermanents(filterPlaneswalker, planeswalker.getControllerId(), this)) { + perm.moveToZone(Zone.GRAVEYARD, this, false); + } + return true; } - somethingHappened = true; } } } - //20091005 - 704.5k + //20091005 - 704.5k, 801.12 FilterLegendaryPermanent filterLegendary = new FilterLegendaryPermanent(); - if (getBattlefield().count(filterPlaneswalker) > 1) { //don't bother checking if less than 2 legends in play - for (Permanent legend: getBattlefield().getActivePermanents(filterLegendary)) { + if (getBattlefield().countAll(filterLegendary) > 1) { //don't bother checking if less than 2 legends in play + for (Permanent legend: getBattlefield().getAllActivePermanents(filterLegendary)) { FilterLegendaryPermanent filterLegendName = new FilterLegendaryPermanent(); filterLegendName.getName().add(legend.getName()); - if (getBattlefield().count(filterLegendName) > 1) { - for (Permanent dupLegend: getBattlefield().getActivePermanents(filterLegendName)) { + if (getBattlefield().count(filterLegendName, legend.getControllerId(), this) > 1) { + for (Permanent dupLegend: getBattlefield().getActivePermanents(filterLegendName, legend.getControllerId(), this)) { dupLegend.moveToZone(Zone.GRAVEYARD, this, false); } return true; @@ -455,7 +472,7 @@ public abstract class GameImpl implements Game, Serializable { } } //20091005 - 704.5p - for (Permanent perm: getBattlefield().getActivePermanents(new FilterEquipment())) { + for (Permanent perm: getBattlefield().getAllActivePermanents(new FilterEquipment())) { if (perm.getAttachedTo() != null) { Permanent creature = getPermanent(perm.getAttachedTo()); if (creature == null) { @@ -467,7 +484,7 @@ public abstract class GameImpl implements Game, Serializable { } } } - for (Permanent perm: getBattlefield().getActivePermanents(new FilterFortification())) { + for (Permanent perm: getBattlefield().getAllActivePermanents(new FilterFortification())) { if (perm.getAttachedTo() != null) { Permanent land = getPermanent(perm.getAttachedTo()); if (land == null) { @@ -480,7 +497,7 @@ public abstract class GameImpl implements Game, Serializable { } } //20091005 - 704.5q - for (Permanent perm: getBattlefield().getActivePermanents()) { + for (Permanent perm: getBattlefield().getAllActivePermanents()) { if (perm.getAttachments().size() > 0) { for (UUID attachmentId: perm.getAttachments()) { Permanent attachment = getPermanent(attachmentId); @@ -556,10 +573,7 @@ public abstract class GameImpl implements Game, Serializable { //20091005 - 507.1 state.getCombat().clear(); state.getCombat().setAttacker(activePlayerId); - for (Player player: state.getPlayers().values()) { - if (!player.getId().equals(state.getActivePlayerId())) - state.getCombat().getDefenders().add(player.getId()); - } + state.getCombat().setDefenders(this); fireEvent(new GameEvent(GameEvent.EventType.BEGIN_COMBAT_STEP_PRE, null, null, activePlayerId)); playPriority(activePlayerId); fireEvent(new GameEvent(GameEvent.EventType.PRECOMBAT_MAIN_STEP_POST, null, null, activePlayerId)); @@ -647,7 +661,8 @@ public abstract class GameImpl implements Game, Serializable { fireEvent(new GameEvent(GameEvent.EventType.CLEANUP_STEP_PRE, null, null, activePlayerId)); //20091005 - 514.1 Player player = getPlayer(activePlayerId); - player.discardToMax(this); + if (!player.hasLeft() && !player.hasLost()) + player.discardToMax(this); state.getBattlefield().endOfTurn(activePlayerId, this); state.removeEotEffects(this); if (checkStateAndTriggered()) { diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index c1a9c2f628e..dea952d83bf 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -184,7 +184,7 @@ public class GameState implements Serializable { public PlayerList getPlayerList(UUID playerId) { PlayerList playerList = new PlayerList(); for (Player player: players.values()) { - if (!player.hasLeft()) + if (!player.hasLeft() && !player.hasLost()) playerList.add(player); } playerList.setCurrent(playerId); diff --git a/Mage/src/mage/game/GameType.java b/Mage/src/mage/game/GameType.java new file mode 100644 index 00000000000..4670e3da2e3 --- /dev/null +++ b/Mage/src/mage/game/GameType.java @@ -0,0 +1,80 @@ +/* + * 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.io.Serializable; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public abstract class GameType implements Serializable { + + protected String name; + protected int minPlayers; + protected int maxPlayers; + protected int numTeams; + protected int playersPerTeam; + protected boolean useRange; + protected boolean useAttackOption; + + @Override + public String toString() { + return name; + } + + public String getName() { + return name; + } + + public int getMinPlayers() { + return minPlayers; + } + + public int getMaxPlayers() { + return maxPlayers; + } + + public int getNumTeams() { + return numTeams; + } + + public int getPlayersPerTeam() { + return playersPerTeam; + } + + public boolean isUseRange() { + return useRange; + } + + public boolean isUseAttackOption() { + return useAttackOption; + } + +} diff --git a/Mage/src/mage/game/Table.java b/Mage/src/mage/game/Table.java index a2b700fb021..990de8b3713 100644 --- a/Mage/src/mage/game/Table.java +++ b/Mage/src/mage/game/Table.java @@ -70,10 +70,6 @@ public class Table implements Serializable { return tableId; } -// public void setGame(Game game) { -// this.game = game; -// } - public void initGame(Game game) throws GameException { for (int i = 0; i < numSeats; i++ ) { game.addPlayer(seats[i].getPlayer()); diff --git a/Mage/src/mage/game/combat/Combat.java b/Mage/src/mage/game/combat/Combat.java index f95d3dbd5e6..a451ad56e49 100644 --- a/Mage/src/mage/game/combat/Combat.java +++ b/Mage/src/mage/game/combat/Combat.java @@ -30,12 +30,16 @@ package mage.game.combat; import java.io.Serializable; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; import mage.abilities.keyword.VigilanceAbility; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.players.PlayerList; /** @@ -45,14 +49,14 @@ import mage.game.permanent.Permanent; public class Combat implements Serializable { protected List groups = new ArrayList(); - protected List defenders = new ArrayList(); + protected Set defenders = new HashSet(); protected UUID attackerId; public List getGroups() { return groups; } - public List getDefenders() { + public Set getDefenders() { return defenders; } @@ -86,19 +90,55 @@ public class Combat implements Serializable { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, attackerId, attackerId))) { game.getPlayer(attackerId).selectAttackers(game); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_ATTACKERS, attackerId, attackerId)); + game.fireInformEvent(game.getPlayer(attackerId).getName() + " attacks with " + groups.size() + " creatures"); } } public void selectBlockers(Game game) { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, attackerId, attackerId))) { for (UUID defenderId: defenders) { - game.getPlayer(defenderId).selectBlockers(game); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, defenderId, defenderId)); + //check if defender is being attacked + if (isAttacked(defenderId, game)) { + game.getPlayer(defenderId).selectBlockers(game); + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, defenderId, defenderId)); + } } } } + public void setDefenders(Game game) { + Set opponents = game.getOpponents(attackerId); + PlayerList players; + switch (game.getAttackOption()) { + case LEFT: + players = game.getPlayerList(attackerId); + while (true) { + Player opponent = players.getNext(); + if (opponents.contains(opponent.getId())) { + defenders.add(opponent.getId()); + break; + } + } + break; + case RIGHT: + players = game.getPlayerList(attackerId); + while (true) { + Player opponent = players.getPrevious(); + if (opponents.contains(opponent.getId())) { + defenders.add(opponent.getId()); + break; + } + } + break; + case MULITPLE: + defenders.addAll(game.getOpponents(attackerId)); + break; + } + } + public void declareAttacker(UUID attackerId, UUID defenderId, Game game) { + if (!defenders.contains(defenderId)) + return; Permanent defender = game.getPermanent(defenderId); CombatGroup newGroup = new CombatGroup(defenderId, defender != null); newGroup.attackers.add(attackerId); @@ -177,5 +217,17 @@ public class Combat implements Serializable { return false; } + protected boolean isAttacked(UUID defenderId, Game game) { + for (CombatGroup group: groups) { + if (group.getDefenderId().equals(defenderId)) + return true; + if (group.defenderIsPlaneswalker) { + Permanent permanent = game.getPermanent(group.getDefenderId()); + if (permanent.getControllerId().equals(defenderId)) + return true; + } + } + return false; + } } diff --git a/Mage/src/mage/game/events/GameEvent.java b/Mage/src/mage/game/events/GameEvent.java index f2417353023..5e860df2a20 100644 --- a/Mage/src/mage/game/events/GameEvent.java +++ b/Mage/src/mage/game/events/GameEvent.java @@ -78,7 +78,7 @@ public class GameEvent { PLAY_LAND, LAND_PLAYED, CAST_SPELL, SPELL_CAST, ACTIVATE_ABILITY, ACTIVATED_ABILITY, - LOSES, WINS, + LOSES, LOST, WINS, TARGET, TARGETED, COUNTER, COUNTERED, DECLARING_ATTACKERS, DECLARED_ATTACKERS, diff --git a/Mage/src/mage/game/permanent/Battlefield.java b/Mage/src/mage/game/permanent/Battlefield.java index 8853eaa4eac..8abb0af1068 100644 --- a/Mage/src/mage/game/permanent/Battlefield.java +++ b/Mage/src/mage/game/permanent/Battlefield.java @@ -34,9 +34,10 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import mage.Constants.CardType; -import mage.Constants.Zone; +import mage.Constants.RangeOfInfluence; import mage.abilities.keyword.PhasingAbility; import mage.filter.FilterPermanent; import mage.game.Game; @@ -60,7 +61,7 @@ public class Battlefield implements Serializable { field.clear(); } - public int count(FilterPermanent filter) { + public int countAll(FilterPermanent filter) { int count = 0; for (Permanent permanent: field.values()) { if (filter.match(permanent)) { @@ -70,6 +71,37 @@ public class Battlefield implements Serializable { return count; } + public int countAll(FilterPermanent filter, UUID controllerId) { + int count = 0; + for (Permanent permanent: field.values()) { + if (permanent.getControllerId().equals(controllerId) && filter.match(permanent)) { + count++; + } + } + return count; + } + + + public int count(FilterPermanent filter, UUID sourcePlayerId, Game game) { + int count = 0; + if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) { + for (Permanent permanent: field.values()) { + if (filter.match(permanent)) { + count++; + } + } + } + else { + Set range = game.getPlayer(sourcePlayerId).getInRange(); + for (Permanent permanent: field.values()) { + if (range.contains(permanent.getControllerId()) && filter.match(permanent)) { + count++; + } + } + } + return count; + } + public void addPermanent(Permanent permanent) { field.put(permanent.getId(), permanent); } @@ -87,9 +119,19 @@ public class Battlefield implements Serializable { } public void checkTriggers(GameEvent event, Game game) { - for (Permanent perm: field.values()) { - if (perm.isPhasedIn()) - perm.checkTriggers(event, game); + if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) { + for (Permanent perm: field.values()) { + if (perm.isPhasedIn()) + perm.checkTriggers(event, game); + } + } + else { + //20100423 - 801.7 + Set range = game.getPlayer(event.getPlayerId()).getInRange(); + for (Permanent perm: field.values()) { + if (range.contains(perm.getControllerId()) && perm.isPhasedIn()) + perm.checkTriggers(event, game); + } } } @@ -113,7 +155,7 @@ public class Battlefield implements Serializable { return perms; } - public List getActivePermanents() { + public List getAllActivePermanents() { List active = new ArrayList(); for (Permanent perm: field.values()) { if (perm.isPhasedIn()) @@ -122,7 +164,7 @@ public class Battlefield implements Serializable { return active; } - public List getActivePermanents(UUID controllerId) { + public List getAllActivePermanents(UUID controllerId) { List active = new ArrayList(); for (Permanent perm: field.values()) { if (perm.isPhasedIn() && perm.getControllerId().equals(controllerId)) @@ -131,7 +173,7 @@ public class Battlefield implements Serializable { return active; } - public List getActivePermanents(CardType type) { + public List getAllActivePermanents(CardType type) { List active = new ArrayList(); for (Permanent perm: field.values()) { if (perm.isPhasedIn() && perm.getCardType().contains(type)) @@ -140,7 +182,7 @@ public class Battlefield implements Serializable { return active; } - public List getActivePermanents(FilterPermanent filter) { + public List getAllActivePermanents(FilterPermanent filter) { List active = new ArrayList(); for (Permanent perm: field.values()) { if (perm.isPhasedIn() && filter.match(perm)) @@ -149,15 +191,45 @@ public class Battlefield implements Serializable { return active; } - public List getActivePermanents(UUID controllerId, CardType type) { + public List getAllActivePermanents(FilterPermanent filter, UUID controllerId) { List active = new ArrayList(); for (Permanent perm: field.values()) { - if (perm.isPhasedIn() && perm.getCardType().contains(type) && perm.getControllerId().equals(controllerId)) + if (perm.isPhasedIn() && perm.getControllerId().equals(controllerId) && filter.match(perm)) active.add(perm); } return active; } + public List getActivePermanents(FilterPermanent filter, UUID sourcePlayerId, Game game) { + if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) { + return getAllActivePermanents(filter); + } + else { + List active = new ArrayList(); + Set range = game.getPlayer(sourcePlayerId).getInRange(); + for (Permanent perm: field.values()) { + if (perm.isPhasedIn() && range.contains(perm.getControllerId()) && filter.match(perm)) + active.add(perm); + } + return active; + } + } + + public List getActivePermanents(UUID sourcePlayerId, Game game) { + if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) { + return getAllActivePermanents(); + } + else { + List active = new ArrayList(); + Set range = game.getPlayer(sourcePlayerId).getInRange(); + for (Permanent perm: field.values()) { + if (perm.isPhasedIn() && range.contains(perm.getControllerId())) + active.add(perm); + } + return active; + } + } + public List getPhasedIn(UUID controllerId) { List phasedIn = new ArrayList(); for (Permanent perm: field.values()) { diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index 8bdede4048c..22b9a2f963c 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -65,8 +65,8 @@ public interface Permanent extends Card { public boolean removeAttachment(UUID permanentId, Game game); public UUID getControllerId(); - public void changeControllerId(UUID controllerId, Game game); - public boolean canTarget(MageObject source); + public boolean changeControllerId(UUID controllerId, Game game); + public boolean canBeTargetedBy(MageObject source); public int getDamage(); public int damage(int damage, UUID sourceId, Game game); public Counters getCounters(); diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 9d740d4d97b..0ebdda99de8 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -267,12 +267,17 @@ public abstract class PermanentImpl extends CardImpl implements Permanent } @Override - public void changeControllerId(UUID controllerId, Game game) { + public boolean changeControllerId(UUID controllerId, Game game) { if (!controllerId.equals(this.controllerId)) { - this.removeFromCombat(game); - this.controlledFromStartOfTurn = false; - this.controllerId = controllerId; + Player newController = game.getPlayer(controllerId); + if (newController != null && (!newController.hasLeft() || !newController.hasLost())) { + this.removeFromCombat(game); + this.controlledFromStartOfTurn = false; + this.controllerId = controllerId; + return true; + } } + return false; } @Override @@ -389,7 +394,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent } @Override - public boolean canTarget(MageObject source) { + public boolean canBeTargetedBy(MageObject source) { if (source != null) { if (abilities.containsKey(ShroudAbility.getInstance().getId())) return false; diff --git a/Mage/src/mage/game/turn/Turn.java b/Mage/src/mage/game/turn/Turn.java index 936341019da..44e694597f5 100644 --- a/Mage/src/mage/game/turn/Turn.java +++ b/Mage/src/mage/game/turn/Turn.java @@ -81,7 +81,7 @@ public class Turn implements Serializable { this.activePlayerId = activePlayerId; resetCounts(); - game.getPlayer(activePlayerId).beginTurn(); + game.getPlayer(activePlayerId).beginTurn(game); for (Phase phase: phases) { if (game.isGameOver()) return; diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index 05fef388096..8c8aae0e91c 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -29,6 +29,7 @@ package mage.players; import java.util.List; +import java.util.Set; import java.util.UUID; import mage.Constants.Outcome; import mage.MageItem; @@ -77,6 +78,7 @@ public interface Player extends MageItem { public boolean hasWon(); public boolean hasLeft(); public ManaPool getManaPool(); + public Set getInRange(); public void init(Game game); public void reset(); @@ -102,8 +104,8 @@ public interface Player extends MageItem { public boolean discard(Card card, Game game); public void lost(Game game); public void won(Game game); - public void leaveGame(); - public void concede(); + public void leaveGame(Game game); + public void concede(Game game); public void abort(); public void revealCards(Cards cards, Game game); @@ -135,7 +137,7 @@ public interface Player extends MageItem { public void declareBlocker(UUID blockerId, UUID attackerId, Game game); public boolean hasAvailableAttackers(Game game); - public void beginTurn(); + public void beginTurn(Game game); public void endOfTurn(Game game); public void phasing(Game game); public void untap(Game game); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index ebfc8561532..b813213b703 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -29,11 +29,16 @@ package mage.players; import java.io.Serializable; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import mage.Constants.Outcome; +import mage.Constants.RangeOfInfluence; import mage.Constants.Zone; import mage.MageObject; import mage.abilities.Abilities; @@ -63,6 +68,7 @@ import mage.game.permanent.PermanentToken; import mage.game.permanent.token.Token; import mage.game.stack.Spell; import mage.game.stack.StackAbility; +import mage.game.stack.StackObject; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetDiscard; import mage.util.Copier; @@ -88,10 +94,13 @@ public abstract class PlayerImpl implements Player, Serializable { protected boolean passed; protected boolean passedTurn; protected boolean left; + protected RangeOfInfluence range; + protected Set inRange = new HashSet(); - public PlayerImpl(String name, Deck deck) { + public PlayerImpl(String name, Deck deck, RangeOfInfluence range) { this.playerId = UUID.randomUUID(); this.name = name; + this.range = range; deck.setOwnerId(playerId); library = new Library(playerId); library.addAll(deck.getCards()); @@ -113,6 +122,7 @@ public abstract class PlayerImpl implements Player, Serializable { game.getState().getWatchers().add(watcher); } } + findRange(game); } @Override @@ -128,8 +138,50 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public void beginTurn() { + public void beginTurn(Game game) { this.landsPlayed = 0; + findRange(game); + } + + protected void findRange(Game game) { + //20100423 - 801.2c + inRange.clear(); + if (range == RangeOfInfluence.ALL) { + for (Player player: game.getPlayers().values()) { + if (!player.hasLeft()) + inRange.add(player.getId()); + } + } + else { + if ((range.getRange() * 2) + 1 >= game.getPlayers().size()) { + for (Player player: game.getPlayers().values()) { + if (!player.hasLeft()) + inRange.add(player.getId()); + } + } + else { + inRange.add(playerId); + PlayerList players = game.getPlayerList(playerId); + for (int i = 0; i < range.getRange(); i++) { + Player player = players.getNext(); + while (player.hasLeft()) + player = players.getNext(); + inRange.add(player.getId()); + } + players = game.getPlayerList(playerId); + for (int i = 0; i < range.getRange(); i++) { + Player player = players.getPrevious(); + while (player.hasLeft()) + player = players.getPrevious(); + inRange.add(player.getId()); + } + } + } + } + + @Override + public Set getInRange() { + return inRange; } @Override @@ -145,6 +197,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean canTarget(MageObject source) { + if (this.hasLost() || this.hasLeft()) + return false; if (source != null) { if (abilities.containsKey(ShroudAbility.getInstance().getId())) return false; @@ -296,6 +350,7 @@ public abstract class PlayerImpl implements Player, Serializable { ability.copy().activate(game, false); } game.fireEvent(GameEvent.getEvent(GameEvent.EventType.SPELL_CAST, card.getId(), playerId)); + game.fireInformEvent(name + " casts " + card.getName()); game.removeLastBookmark(); return true; } @@ -452,7 +507,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void untap(Game game) { //20091005 - 502.2 - for (Permanent permanent: game.getBattlefield().getAllPermanents(playerId)) { + for (Permanent permanent: game.getBattlefield().getAllActivePermanents(playerId)) { permanent.untap(game); } } @@ -569,7 +624,6 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void restore(Player player) { this.library = player.getLibrary(); -// this.deck = player.getDeck(); this.hand = player.getHand(); this.graveyard = player.getGraveyard(); this.abilities = player.getAbilities(); @@ -590,18 +644,45 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void resetPassed() { - passed = false; + if (!this.loses && !this.left) + passed = false; + else + passed = true; } @Override - public void concede() { - this.loses = true; + public void concede(Game game) { + leaveGame(game); } @Override - public void leaveGame() { + public void leaveGame(Game game) { + this.passed = true; + this.abort(); this.loses = true; this.left = true; + //20100423 - 800.4a + this.hand.clear(); + this.graveyard.clear(); + this.library.clear(); + for (Iterator it = game.getBattlefield().getAllPermanents().iterator(); it.hasNext();) { + Permanent perm = it.next(); + if (perm.getOwnerId().equals(playerId)) { + it.remove(); + } + } + for (Iterator it = game.getStack().iterator(); it.hasNext();) { + StackObject object = it.next(); + if (object.getControllerId().equals(playerId)) { + it.remove(); + } + } + for (Iterator it = game.getBattlefield().getAllPermanents().iterator(); it.hasNext();) { + Permanent perm = it.next(); + if (perm.getControllerId().equals(playerId)) { + perm.moveToExile(null, "", game); + } + } } @Override @@ -613,6 +694,10 @@ public abstract class PlayerImpl implements Player, Serializable { public void lost(Game game) { if (!game.replaceEvent(new GameEvent(GameEvent.EventType.LOSES, null, null, playerId))) { this.loses = true; + //20100423 - 603.9 + if (!this.wins) + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LOST, null, null, playerId)); + leaveGame(game); } } @@ -620,8 +705,16 @@ public abstract class PlayerImpl implements Player, Serializable { public void won(Game game) { if (!game.replaceEvent(new GameEvent(GameEvent.EventType.WINS, null, null, playerId))) { if (!this.loses) { - this.wins = true; - game.end(); + //20100423 - 800.6, 801.16 + if (game.getPlayers().size() > 2) { + for (UUID opponentId: game.getOpponents(playerId)) { + game.getPlayer(opponentId).lost(game); + } + } + else { + this.wins = true; + game.end(); + } } } } @@ -684,7 +777,7 @@ public abstract class PlayerImpl implements Player, Serializable { protected List getAvailableAttackers(Game game) { FilterCreatureForAttack attackFilter = new FilterCreatureForAttack(); attackFilter.getControllerId().add(playerId); - List attackers = game.getBattlefield().getActivePermanents(attackFilter); + List attackers = game.getBattlefield().getAllActivePermanents(attackFilter); return attackers; } diff --git a/Mage/src/mage/target/Target.java b/Mage/src/mage/target/Target.java index eaac193d9ab..c8b6c09c45e 100644 --- a/Mage/src/mage/target/Target.java +++ b/Mage/src/mage/target/Target.java @@ -46,7 +46,7 @@ public interface Target extends Serializable { public boolean isChosen(); public boolean doneChosing(); public void clearChosen(); - public boolean canChoose(UUID sourceId, Game game); + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game); public boolean choose(Outcome outcome, Game game); public String getMessage(); public String getTargetName(); diff --git a/Mage/src/mage/target/TargetCard.java b/Mage/src/mage/target/TargetCard.java index b17806e7b76..7df6afb9017 100644 --- a/Mage/src/mage/target/TargetCard.java +++ b/Mage/src/mage/target/TargetCard.java @@ -70,18 +70,21 @@ public class TargetCard extends TargetObject { } @Override - public boolean canChoose(UUID sourceId, Game game) { - for (Player player: game.getPlayers().values()) { - if (filter.matchOwner(player.getId())) { - switch (zone) { - case HAND: - if (player.getHand().getCards(filter).size() > this.minNumberOfTargets) - return true; - break; - case GRAVEYARD: - if (player.getGraveyard().getCards(filter).size() > this.minNumberOfTargets) - return true; - break; + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { + for (UUID playerId: game.getPlayer(sourceControllerId).getInRange()) { + if (filter.matchOwner(playerId)) { + Player player = game.getPlayer(playerId); + if (player != null) { + switch (zone) { + case HAND: + if (player.getHand().getCards(filter).size() > this.minNumberOfTargets) + return true; + break; + case GRAVEYARD: + if (player.getGraveyard().getCards(filter).size() > this.minNumberOfTargets) + return true; + break; + } } } } diff --git a/Mage/src/mage/target/TargetPermanent.java b/Mage/src/mage/target/TargetPermanent.java index 691a4b55217..7a78a58ed2b 100644 --- a/Mage/src/mage/target/TargetPermanent.java +++ b/Mage/src/mage/target/TargetPermanent.java @@ -78,7 +78,7 @@ public class TargetPermanent extends TargetObject { setController(controllerId, game); if (permanent != null) { if (this.source != null) - return permanent.canTarget(game.getObject(this.source.getSourceId())) && filter.match(permanent); + return permanent.canBeTargetedBy(game.getObject(this.source.getSourceId())) && filter.match(permanent); else return filter.match(permanent); } @@ -115,12 +115,12 @@ public class TargetPermanent extends TargetObject { } @Override - public boolean canChoose(UUID sourceId, Game game) { - List permanents = game.getBattlefield().getActivePermanents(filter); + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { + List permanents = game.getBattlefield().getActivePermanents(filter, sourceControllerId, game); Iterator it = permanents.iterator(); while(it.hasNext()) { Permanent permanent = it.next(); - if (!permanent.canTarget(game.getObject(sourceId))) { + if (!permanent.canBeTargetedBy(game.getObject(sourceId))) { it.remove(); } } diff --git a/Mage/src/mage/target/TargetPlayer.java b/Mage/src/mage/target/TargetPlayer.java index d9bc35b50eb..c7e16a190d4 100644 --- a/Mage/src/mage/target/TargetPlayer.java +++ b/Mage/src/mage/target/TargetPlayer.java @@ -63,10 +63,11 @@ public class TargetPlayer extends TargetImpl { } @Override - public boolean canChoose(UUID sourceId, Game game) { + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { int count = 0; - for (Player player: game.getPlayers().values()) { - if (filter.match(player)) { + for (UUID playerId: game.getPlayer(sourceControllerId).getInRange()) { + Player player = game.getPlayer(playerId); + if (player != null && !player.hasLeft() && filter.match(player)) { if (player.canTarget(game.getObject(sourceId))) count++; } diff --git a/Mage/src/mage/target/TargetSpell.java b/Mage/src/mage/target/TargetSpell.java index c40d5a38101..6e4a006d593 100644 --- a/Mage/src/mage/target/TargetSpell.java +++ b/Mage/src/mage/target/TargetSpell.java @@ -77,10 +77,11 @@ public class TargetSpell extends TargetObject { return false; } - public boolean canChoose(UUID sourceId, Game game) { + @Override + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { int count = 0; for (StackObject stackObject: game.getStack()) { - if (stackObject instanceof Spell && filter.match((Spell)stackObject)) { + if (stackObject instanceof Spell && game.getPlayer(sourceControllerId).getInRange().contains(stackObject.getControllerId()) && filter.match((Spell)stackObject)) { count++; } } diff --git a/Mage/src/mage/target/Targets.java b/Mage/src/mage/target/Targets.java index 0d8bed3c671..b750189d3af 100644 --- a/Mage/src/mage/target/Targets.java +++ b/Mage/src/mage/target/Targets.java @@ -101,9 +101,9 @@ public class Targets extends ArrayList { return true; } - public boolean canChoose(UUID sourceId, Game game) { + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { for (Target target: this) { - if (!target.canChoose(sourceId, game)) + if (!target.canChoose(sourceId, sourceControllerId, game)) return false; } return true; diff --git a/Mage/src/mage/target/common/TargetCreatureOrPlayer.java b/Mage/src/mage/target/common/TargetCreatureOrPlayer.java index 359d870a101..a95d0c93ab1 100644 --- a/Mage/src/mage/target/common/TargetCreatureOrPlayer.java +++ b/Mage/src/mage/target/common/TargetCreatureOrPlayer.java @@ -33,6 +33,7 @@ import mage.Constants.CardType; import mage.Constants.Zone; import mage.filter.Filter; import mage.filter.common.FilterCreatureOrPlayer; +import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -72,7 +73,7 @@ public class TargetCreatureOrPlayer extends TargetImpl { Permanent permanent = game.getPermanent(id); if (permanent != null) { if (this.source != null) - return permanent.canTarget(game.getObject(this.source.getSourceId())) && filter.match(permanent); + return permanent.canBeTargetedBy(game.getObject(this.source.getSourceId())) && filter.match(permanent); else return filter.match(permanent); } @@ -86,16 +87,17 @@ public class TargetCreatureOrPlayer extends TargetImpl { } @Override - public boolean canChoose(UUID sourceId, Game game) { + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { int count = 0; - for (Player player: game.getPlayers().values()) { - if (player.canTarget(game.getObject(this.source.getSourceId())) && filter.match(player)) + for (UUID playerId: game.getPlayer(sourceControllerId).getInRange()) { + Player player = game.getPlayer(playerId); + if (player != null && player.canTarget(game.getObject(this.source.getSourceId())) && filter.match(player)) count++; } if (count >= this.minNumberOfTargets) return true; - for (Permanent permanent: game.getBattlefield().getActivePermanents(CardType.CREATURE)) { - if (permanent.canTarget(game.getObject(this.source.getSourceId())) && filter.match(permanent)) + for (Permanent permanent: game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), sourceControllerId, game)) { + if (permanent.canBeTargetedBy(game.getObject(this.source.getSourceId())) && filter.match(permanent)) count++; } return count >= this.minNumberOfTargets; diff --git a/Mage/src/mage/target/common/TargetDefender.java b/Mage/src/mage/target/common/TargetDefender.java index 94e08d2219e..a1de8e947b7 100644 --- a/Mage/src/mage/target/common/TargetDefender.java +++ b/Mage/src/mage/target/common/TargetDefender.java @@ -29,11 +29,13 @@ package mage.target.common; import java.util.List; +import java.util.Set; import java.util.UUID; import mage.Constants.CardType; import mage.Constants.Zone; import mage.filter.Filter; import mage.filter.common.FilterPlaneswalkerOrPlayer; +import mage.filter.common.FilterPlaneswalkerPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -48,15 +50,15 @@ public class TargetDefender extends TargetImpl { protected FilterPlaneswalkerOrPlayer filter; protected UUID attackerId; - public TargetDefender(List defenders, UUID attackerId) { + public TargetDefender(Set defenders, UUID attackerId) { this(1, 1, defenders, attackerId); } - public TargetDefender(int numTargets, List defenders, UUID attackerId) { + public TargetDefender(int numTargets, Set defenders, UUID attackerId) { this(numTargets, numTargets, defenders, attackerId); } - public TargetDefender(int minNumTargets, int maxNumTargets, List defenders, UUID attackerId) { + public TargetDefender(int minNumTargets, int maxNumTargets, Set defenders, UUID attackerId) { this.minNumberOfTargets = minNumTargets; this.maxNumberOfTargets = maxNumTargets; this.zone = Zone.ALL; @@ -71,16 +73,17 @@ public class TargetDefender extends TargetImpl { } @Override - public boolean canChoose(UUID sourceId, Game game) { + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { int count = 0; - for (Player player: game.getPlayers().values()) { - if (player.canTarget(game.getObject(this.source.getSourceId())) && filter.match(player)) + for (UUID playerId: game.getPlayer(sourceControllerId).getInRange()) { + Player player = game.getPlayer(playerId); + if (player != null && player.canTarget(game.getObject(this.source.getSourceId())) && filter.match(player)) count++; } if (count >= this.minNumberOfTargets) return true; - for (Permanent permanent: game.getBattlefield().getActivePermanents(CardType.PLANESWALKER)) { - if (permanent.canTarget(game.getObject(this.source.getSourceId())) && filter.match(permanent)) + for (Permanent permanent: game.getBattlefield().getActivePermanents(new FilterPlaneswalkerPermanent(), sourceControllerId, game)) { + if (permanent.canBeTargetedBy(game.getObject(this.source.getSourceId())) && filter.match(permanent)) count++; } return count >= this.minNumberOfTargets; @@ -110,7 +113,7 @@ public class TargetDefender extends TargetImpl { } Permanent permanent = game.getPermanent(id); if (permanent != null) { - return permanent.canTarget(game.getObject(attackerId)) && filter.match(permanent); + return permanent.canBeTargetedBy(game.getObject(attackerId)) && filter.match(permanent); } return false; } diff --git a/Mage/src/mage/target/common/TargetOpponent.java b/Mage/src/mage/target/common/TargetOpponent.java index 6b3802406d5..d14e785d946 100644 --- a/Mage/src/mage/target/common/TargetOpponent.java +++ b/Mage/src/mage/target/common/TargetOpponent.java @@ -44,10 +44,10 @@ public class TargetOpponent extends TargetPlayer { } @Override - public boolean canChoose(UUID sourceId, Game game) { + public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { filter.getPlayerId().clear(); filter.getPlayerId().addAll(game.getOpponents(this.getAbility().getControllerId())); - return super.canChoose(sourceId, game); + return super.canChoose(sourceId, sourceControllerId, game); } @Override diff --git a/Mage/src/mage/util/CircularList.java b/Mage/src/mage/util/CircularList.java index cfe05846e0a..54df620d5e3 100644 --- a/Mage/src/mage/util/CircularList.java +++ b/Mage/src/mage/util/CircularList.java @@ -38,6 +38,7 @@ import java.util.ListIterator; import java.util.concurrent.locks.ReentrantLock; /** + * a thread-safe circular list * * @author BetaSteward_at_googlemail.com */ @@ -96,14 +97,29 @@ public class CircularList implements List, Iterable, Serializable { return list.get(index); } + /** + * Returns the next element in the list + * + * @return the next element in the list + */ public E getNext() { return list.get(incrementPointer()); } + /** + * Returns the previous element in the list + * + * @return the previous element in the list + */ public E getPrevious() { return list.get(decrementPointer()); } + /** + * Removes the current element from the list + * + * @return true is the item was successfully removed + */ public boolean remove() { return this.remove(get()); }