changes to support multiplayer

This commit is contained in:
BetaSteward 2010-05-17 03:08:18 +00:00
parent d6989797ba
commit 2d5af63cba
41 changed files with 586 additions and 204 deletions

View file

@ -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<String> PlaneswalkerTypes = new ArrayList<String>()
{{add("Ajani"); add("Bolas"); add("Chandra"); add("Elspeth");add("Garruk"); add("Jace"); add("Liliana"); add("Nissa"); add("Sarkhan"); add("Sorin"); add("Tezzeret");}};

View file

@ -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;

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -76,5 +76,9 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
return timestamp;
}
@Override
public boolean hasLayer(Layer layer) {
return this.layer == layer;
}
}

View file

@ -92,47 +92,6 @@ public class ContinuousEffects implements Serializable {
}
// private List<ContinuousEffect> getLayer(Layer layer, SubLayer sublayer) {
// List<ContinuousEffect> layerEffects = new ArrayList<ContinuousEffect>();
// for (ContinuousEffect effect: effects) {
// if (effect.getLayer() == layer && effect.getSubLayer() == sublayer) {
// layerEffects.add(effect);
// }
// }
// Collections.sort(layerEffects, new TimestampSorter());
// return layerEffects;
// }
// private List<PreventionEffect> GetApplicablePreventionEffects(GameEvent event, Game game) {
// List<PreventionEffect> effects = new ArrayList<PreventionEffect>();
// for (IEffect effect: this) {
// if (effect instanceof PreventionEffect && ((PreventionEffect)effect).Applies(event, game)) {
// effects.add((PreventionEffect)effect);
// }
// }
// return effects;
// }
// public List<PreventionEffect> GetApplicablePreventionEffects(IMageObject source, IPermanent target, Game game) {
// List<PreventionEffect> effects = new ArrayList<PreventionEffect>();
// for (IEffect effect: this) {
// if (effect instanceof PreventionEffect && ((PreventionEffect)effect).Applies(source, target, game)) {
// effects.add((PreventionEffect)effect);
// }
// }
// return effects;
// }
//
// public List<PreventionEffect> GetApplicablePreventionEffects(IMageObject source, IPlayer target, Game game) {
// List<PreventionEffect> effects = new ArrayList<PreventionEffect>();
// for (IEffect effect: this) {
// if (effect instanceof PreventionEffect && ((PreventionEffect)effect).Applies(source, target, game)) {
// effects.add((PreventionEffect)effect);
// }
// }
// return effects;
// }
private List<ReplacementEffect> getApplicableReplacementEffects(GameEvent event, Game game) {
List<ReplacementEffect> replacementEffects = new ArrayList<ReplacementEffect>();
for (Effect effect: effects) {
@ -180,18 +139,6 @@ public class ContinuousEffects implements Serializable {
}
}
// if (!caught) {
// List<PreventionEffect> 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());

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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<Object> implements Fi
protected FilterPlaneswalkerPermanent planeswalkerFilter = new FilterPlaneswalkerPermanent();
protected FilterPlayer playerFilter = new FilterPlayer();
public FilterPlaneswalkerOrPlayer(List<UUID> defenders) {
public FilterPlaneswalkerOrPlayer(Set<UUID> defenders) {
super("planeswalker or player");
planeswalkerFilter.getControllerId().addAll(defenders);
playerFilter.getPlayerId().addAll(defenders);

View file

@ -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<UUID> getOpponents(UUID controllerId);
public Set<UUID> getOpponents(UUID controllerId);
public Turn getTurn();
public int getTurnNum();
public boolean isMainPhase();

View file

@ -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()) {

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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());

View file

@ -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<CombatGroup> groups = new ArrayList<CombatGroup>();
protected List<UUID> defenders = new ArrayList<UUID>();
protected Set<UUID> defenders = new HashSet<UUID>();
protected UUID attackerId;
public List<CombatGroup> getGroups() {
return groups;
}
public List<UUID> getDefenders() {
public Set<UUID> 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<UUID> 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;
}
}

View file

@ -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,

View file

@ -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<UUID> 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<UUID> 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<Permanent> getActivePermanents() {
public List<Permanent> getAllActivePermanents() {
List<Permanent> active = new ArrayList<Permanent>();
for (Permanent perm: field.values()) {
if (perm.isPhasedIn())
@ -122,7 +164,7 @@ public class Battlefield implements Serializable {
return active;
}
public List<Permanent> getActivePermanents(UUID controllerId) {
public List<Permanent> getAllActivePermanents(UUID controllerId) {
List<Permanent> active = new ArrayList<Permanent>();
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<Permanent> getActivePermanents(CardType type) {
public List<Permanent> getAllActivePermanents(CardType type) {
List<Permanent> active = new ArrayList<Permanent>();
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<Permanent> getActivePermanents(FilterPermanent filter) {
public List<Permanent> getAllActivePermanents(FilterPermanent filter) {
List<Permanent> active = new ArrayList<Permanent>();
for (Permanent perm: field.values()) {
if (perm.isPhasedIn() && filter.match(perm))
@ -149,15 +191,45 @@ public class Battlefield implements Serializable {
return active;
}
public List<Permanent> getActivePermanents(UUID controllerId, CardType type) {
public List<Permanent> getAllActivePermanents(FilterPermanent filter, UUID controllerId) {
List<Permanent> active = new ArrayList<Permanent>();
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<Permanent> getActivePermanents(FilterPermanent filter, UUID sourcePlayerId, Game game) {
if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) {
return getAllActivePermanents(filter);
}
else {
List<Permanent> active = new ArrayList<Permanent>();
Set<UUID> 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<Permanent> getActivePermanents(UUID sourcePlayerId, Game game) {
if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) {
return getAllActivePermanents();
}
else {
List<Permanent> active = new ArrayList<Permanent>();
Set<UUID> range = game.getPlayer(sourcePlayerId).getInRange();
for (Permanent perm: field.values()) {
if (perm.isPhasedIn() && range.contains(perm.getControllerId()))
active.add(perm);
}
return active;
}
}
public List<Permanent> getPhasedIn(UUID controllerId) {
List<Permanent> phasedIn = new ArrayList<Permanent>();
for (Permanent perm: field.values()) {

View file

@ -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();

View file

@ -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;

View file

@ -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;

View file

@ -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<UUID> 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);

View file

@ -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<UUID> inRange = new HashSet<UUID>();
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<UUID> 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<Permanent> it = game.getBattlefield().getAllPermanents().iterator(); it.hasNext();) {
Permanent perm = it.next();
if (perm.getOwnerId().equals(playerId)) {
it.remove();
}
}
for (Iterator<StackObject> it = game.getStack().iterator(); it.hasNext();) {
StackObject object = it.next();
if (object.getControllerId().equals(playerId)) {
it.remove();
}
}
for (Iterator<Permanent> 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<Permanent> getAvailableAttackers(Game game) {
FilterCreatureForAttack attackFilter = new FilterCreatureForAttack();
attackFilter.getControllerId().add(playerId);
List<Permanent> attackers = game.getBattlefield().getActivePermanents(attackFilter);
List<Permanent> attackers = game.getBattlefield().getAllActivePermanents(attackFilter);
return attackers;
}

View file

@ -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();

View file

@ -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;
}
}
}
}

View file

@ -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<Permanent> permanents = game.getBattlefield().getActivePermanents(filter);
public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) {
List<Permanent> permanents = game.getBattlefield().getActivePermanents(filter, sourceControllerId, game);
Iterator<Permanent> it = permanents.iterator();
while(it.hasNext()) {
Permanent permanent = it.next();
if (!permanent.canTarget(game.getObject(sourceId))) {
if (!permanent.canBeTargetedBy(game.getObject(sourceId))) {
it.remove();
}
}

View file

@ -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++;
}

View file

@ -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++;
}
}

View file

@ -101,9 +101,9 @@ public class Targets extends ArrayList<Target> {
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;

View file

@ -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;

View file

@ -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<UUID> defenders, UUID attackerId) {
public TargetDefender(Set<UUID> defenders, UUID attackerId) {
this(1, 1, defenders, attackerId);
}
public TargetDefender(int numTargets, List<UUID> defenders, UUID attackerId) {
public TargetDefender(int numTargets, Set<UUID> defenders, UUID attackerId) {
this(numTargets, numTargets, defenders, attackerId);
}
public TargetDefender(int minNumTargets, int maxNumTargets, List<UUID> defenders, UUID attackerId) {
public TargetDefender(int minNumTargets, int maxNumTargets, Set<UUID> 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;
}

View file

@ -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

View file

@ -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<E> implements List<E>, Iterable<E>, 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 <tt>true</tt> is the item was successfully removed
*/
public boolean remove() {
return this.remove(get());
}