/*
* 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.permanent;
import mage.abilities.keyword.PhasingAbility;
import mage.constants.CardType;
import mage.constants.RangeOfInfluence;
import mage.filter.FilterPermanent;
import mage.game.Game;
import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
/**
* @author BetaSteward_at_googlemail.com
*/
public class Battlefield implements Serializable {
private final Map<UUID, Permanent> field = new LinkedHashMap<>();
public Battlefield() {
}
public Battlefield(final Battlefield battlefield) {
for (Entry<UUID, Permanent> entry : battlefield.field.entrySet()) {
field.put(entry.getKey(), entry.getValue().copy());
}
}
public Battlefield copy() {
return new Battlefield(this);
}
public void reset(Game game) {
for (Permanent perm : field.values()) {
perm.reset(game);
}
}
public void clear() {
field.clear();
}
/**
* Returns a count of all {@link Permanent} that match the filter and are
* controlled by controllerId.
* <p>
* Some filter predicates do not work here (e.g. AnotherPredicate() because
* filter.match() is called without controllerId. To use this predicates you
* can use count() instead of countAll()
*
* @param filter
* @param controllerId
* @param game
* @return count
*/
public int countAll(FilterPermanent filter, UUID controllerId, Game game) {
return (int) field.values()
.stream()
.filter(permanent -> permanent.getControllerId().equals(controllerId)
&& filter.match(permanent, game)
&& permanent.isPhasedIn())
.count();
}
/**
* Returns a count of all {@link Permanent} that are within the range of
* influence of the specified player id and that match the supplied filter.
*
* @param filter
* @param sourceId - sourceId of the MageObject the calling effect/ability
* belongs to
* @param sourcePlayerId
* @param game
* @return count
*/
public int count(FilterPermanent filter, UUID sourceId, UUID sourcePlayerId, Game game) {
if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) {
return (int) field.values()
.stream()
.filter(permanent -> filter.match(permanent, sourceId, sourcePlayerId, game)
&& permanent.isPhasedIn())
.count();
} else {
Set<UUID> range = game.getPlayer(sourcePlayerId).getInRange();
return (int) field.values()
.stream()
.filter(permanent -> range.contains(permanent.getControllerId())
&& filter.match(permanent, sourceId, sourcePlayerId, game)
&& permanent.isPhasedIn()).count();
}
}
/**
* Returns true if the battlefield contains at least 1 {@link Permanent}
* that matches the filter. This method ignores the range of influence.
*
* @param filter
* @param num
* @param game
* @return boolean
*/
public boolean contains(FilterPermanent filter, int num, Game game) {
return field.values()
.stream()
.filter(permanent -> filter.match(permanent, game)
&& permanent.isPhasedIn()).count() >= num;
}
/**
* Returns true if the battlefield contains num or more {@link Permanent}
* that matches the filter and is controlled by controllerId. This method
* ignores the range of influence.
*
* @param filter
* @param controllerId
* @param num
* @param game
* @return boolean
*/
public boolean contains(FilterPermanent filter, UUID controllerId, int num, Game game) {
return field.values()
.stream()
.filter(permanent -> permanent.getControllerId().equals(controllerId)
&& filter.match(permanent, game)
&& permanent.isPhasedIn())
.count() >= num;
}
/**
* Returns true if the battlefield contains num or more {@link Permanent}
* that is within the range of influence of the specified player id and that
* matches the supplied filter.
*
* @param filter
* @param sourcePlayerId
* @param game
* @param num
* @return boolean
*/
public boolean contains(FilterPermanent filter, UUID sourcePlayerId, Game game, int num) {
if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) {
return field.values().stream()
.filter(permanent -> filter.match(permanent, null, sourcePlayerId, game)
&& permanent.isPhasedIn()).count() >= num;
} else {
Set<UUID> range = game.getPlayer(sourcePlayerId).getInRange();
return field.values().stream()
.filter(permanent -> range.contains(permanent.getControllerId())
&& filter.match(permanent, null, sourcePlayerId, game)
&& permanent.isPhasedIn())
.count() >= num;
}
}
public void addPermanent(Permanent permanent) {
field.put(permanent.getId(), permanent);
}
public Permanent getPermanent(UUID key) {
return field.get(key);
}
public void removePermanent(UUID key) {
field.remove(key);
}
public boolean containsPermanent(UUID key) {
return field.containsKey(key);
}
public void beginningOfTurn(Game game) {
for (Permanent perm : field.values()) {
perm.beginningOfTurn(game);
}
}
public void endOfTurn(UUID controllerId, Game game) {
for (Permanent perm : field.values()) {
perm.endOfTurn(game);
}
}
public Collection<Permanent> getAllPermanents() {
return field.values();
}
public Set<UUID> getAllPermanentIds() {
return field.keySet();
}
public List<Permanent> getAllActivePermanents() {
return field.values()
.stream()
.filter(Permanent::isPhasedIn)
.collect(Collectors.toList());
}
/**
* Returns all {@link Permanent} on the battlefield that are controlled by
* the specified player id. The method ignores the range of influence.
*
* @param controllerId
* @return a list of {@link Permanent}
* @see Permanent
*/
public List<Permanent> getAllActivePermanents(UUID controllerId) {
return field.values()
.stream()
.filter(perm -> perm.isPhasedIn()
&& perm.getControllerId().equals(controllerId))
.collect(Collectors.toList());
}
/**
* Returns all {@link Permanent} on the battlefield that match the specified
* {@link CardType}. This method ignores the range of influence.
*
* @param type
* @return a list of {@link Permanent}
* @see Permanent
*/
public List<Permanent> getAllActivePermanents(CardType type) {
return field.values()
.stream()
.filter(perm -> perm.isPhasedIn() && perm.getCardType().contains(type))
.collect(Collectors.toList());
}
/**
* Returns all {@link Permanent} on the battlefield that match the supplied
* filter. This method ignores the range of influence.
*
* @param filter
* @param game
* @return a list of {@link Permanent}
* @see Permanent
*/
public List<Permanent> getAllActivePermanents(FilterPermanent filter, Game game) {
return field.values()
.stream()
.filter(perm -> perm.isPhasedIn() && filter.match(perm, game))
.collect(Collectors.toList());
}
/**
* Returns all {@link Permanent} that match the filter and are controlled by
* controllerId. This method ignores the range of influence.
*
* @param filter
* @param controllerId
* @param game
* @return a list of {@link Permanent}
* @see Permanent
*/
public List<Permanent> getAllActivePermanents(FilterPermanent filter, UUID controllerId, Game game) {
return field.values()
.stream()
.filter(perm -> perm.isPhasedIn() && perm.getControllerId().equals(controllerId) && filter.match(perm, game))
.collect(Collectors.toList());
}
/**
* Returns all {@link Permanent} that are within the range of influence of
* the specified player id and that match the supplied filter.
*
* @param filter
* @param sourcePlayerId
* @param game
* @return a list of {@link Permanent}
* @see Permanent
*/
public List<Permanent> getActivePermanents(FilterPermanent filter, UUID sourcePlayerId, Game game) {
return getActivePermanents(filter, sourcePlayerId, null, game);
}
/**
* Returns all {@link Permanent} that are within the range of influence of
* the specified player id and that match the supplied filter.
*
* @param filter
* @param sourcePlayerId
* @param sourceId
* @param game
* @return a list of {@link Permanent}
* @see Permanent
*/
public List<Permanent> getActivePermanents(FilterPermanent filter, UUID sourcePlayerId, UUID sourceId, Game game) {
if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) {
return field.values()
.stream()
.filter(perm -> perm.isPhasedIn() && filter.match(perm, sourceId, sourcePlayerId, game))
.collect(Collectors.toList());
} else {
Set<UUID> range = game.getPlayer(sourcePlayerId).getInRange();
return field.values()
.stream()
.filter(perm -> perm.isPhasedIn() && range.contains(perm.getControllerId())
&& filter.match(perm, sourceId, sourcePlayerId, game)).collect(Collectors.toList());
}
}
/**
* Returns all {@link Permanent} that are within the range of influence of
* the specified player id.
*
* @param sourcePlayerId
* @param game
* @return a list of {@link Permanent}
* @see Permanent
*/
public List<Permanent> getActivePermanents(UUID sourcePlayerId, Game game) {
if (game.getRangeOfInfluence() == RangeOfInfluence.ALL) {
return getAllActivePermanents();
} else {
Set<UUID> range = game.getPlayer(sourcePlayerId).getInRange();
return field.values()
.stream()
.filter(perm -> perm.isPhasedIn()
&& range.contains(perm.getControllerId()))
.collect(Collectors.toList());
}
}
public List<Permanent> getPhasedIn(UUID controllerId) {
return field.values()
.stream()
.filter(perm -> perm.getAbilities().containsKey(PhasingAbility.getInstance().getId())
&& perm.isPhasedIn() &&
perm.getControllerId().equals(controllerId))
.collect(Collectors.toList());
}
public List<Permanent> getPhasedOut(UUID controllerId) {
return field.values()
.stream()
.filter(perm -> !perm.isPhasedIn() && perm.getControllerId().equals(controllerId))
.collect(Collectors.toList());
}
public void resetPermanentsControl() {
for (Permanent perm : field.values()) {
if (perm.isPhasedIn()) {
perm.resetControl();
}
}
}
/**
* since control could change several times during applyEvents we only want
* to fire control changed events after all control change effects have been
* applied
*
* @param game
* @return
*/
public boolean fireControlChangeEvents(Game game) {
boolean controlChanged = false;
for (Permanent perm : field.values()) {
if (perm.isPhasedIn()) {
controlChanged |= perm.checkControlChanged(game);
}
}
return controlChanged;
}
}