/*
* 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;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.MageSingleton;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.DomainValue;
import mage.abilities.dynamicvalue.common.SignInversionDynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.constants.AbilityType;
import mage.constants.DependencyType;
import mage.constants.Duration;
import mage.constants.EffectType;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.game.Game;
import mage.players.Player;
/**
* @author BetaSteward_at_googlemail.com
*/
public abstract class ContinuousEffectImpl extends EffectImpl implements ContinuousEffect {
protected Duration duration;
protected Layer layer;
protected SubLayer sublayer;
protected long order;
protected boolean used = false;
protected boolean discarded = false; // for manual effect discard
protected boolean affectedObjectsSet = false;
protected List<MageObjectReference> affectedObjectList = new ArrayList<>();
protected boolean temporary = false;
protected EnumSet<DependencyType> dependencyTypes; // this effect has the dependencyTypes defined here
protected DependencyType dependendToType; // this effect is dependent to this type
/*
A Characteristic Defining Ability (CDA) is an ability that defines a characteristic of a card or token.
There are 3 specific rules that distinguish a CDA from other abilities.
1) A CDA can only define a characteristic of either the card or token it comes from.
2) A CDA can not be triggered, activated, or conditional.
3) A CDA must define a characteristic. Usually color, power and/or toughness, or sub-type.
*/
protected boolean characterDefining = false;
// until your next turn
protected int startingTurn;
protected UUID startingControllerId;
public ContinuousEffectImpl(Duration duration, Outcome outcome) {
super(outcome);
this.duration = duration;
this.order = 0;
this.effectType = EffectType.CONTINUOUS;
this.dependencyTypes = EnumSet.noneOf(DependencyType.class);
this.dependendToType = null;
}
public ContinuousEffectImpl(Duration duration, Layer layer, SubLayer sublayer, Outcome outcome) {
this(duration, outcome);
this.layer = layer;
this.sublayer = sublayer;
}
public ContinuousEffectImpl(final ContinuousEffectImpl effect) {
super(effect);
this.duration = effect.duration;
this.layer = effect.layer;
this.sublayer = effect.sublayer;
this.order = effect.order;
this.used = effect.used;
this.discarded = effect.discarded;
this.affectedObjectsSet = effect.affectedObjectsSet;
this.affectedObjectList.addAll(effect.affectedObjectList);
this.temporary = effect.temporary;
this.startingTurn = effect.startingTurn;
this.startingControllerId = effect.startingControllerId;
this.dependencyTypes = effect.dependencyTypes;
this.dependendToType = effect.dependendToType;
this.characterDefining = effect.characterDefining;
}
@Override
public Duration getDuration() {
return duration;
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
if (this.layer == layer && this.sublayer == sublayer) {
return apply(game, source);
}
return false;
}
@Override
public long getOrder() {
return order;
}
@Override
public void setOrder(long order) {
this.order = order;
}
@Override
public void newId() {
if (!(this instanceof MageSingleton)) {
this.id = UUID.randomUUID();
}
}
@Override
public boolean hasLayer(Layer layer) {
return this.layer == layer;
}
@Override
public boolean isUsed() {
return used;
}
@Override
public boolean isDiscarded() {
return discarded;
}
/**
* Sets the discarded state of the effect. So it will be removed on next
* check.
*/
@Override
public void discard() {
this.discarded = true;
}
@Override
public void init(Ability source, Game game) {
targetPointer.init(game, source);
//20100716 - 611.2c
if (AbilityType.ACTIVATED == source.getAbilityType()
|| AbilityType.SPELL == source.getAbilityType()
|| AbilityType.TRIGGERED == source.getAbilityType()) {
if (layer != null) {
switch (layer) {
case CopyEffects_1:
case ControlChangingEffects_2:
case TextChangingEffects_3:
case TypeChangingEffects_4:
case ColorChangingEffects_5:
case AbilityAddingRemovingEffects_6:
case PTChangingEffects_7:
this.affectedObjectsSet = true;
}
} else if (hasLayer(Layer.CopyEffects_1) || hasLayer(Layer.ControlChangingEffects_2) || hasLayer(Layer.TextChangingEffects_3)
|| hasLayer(Layer.TypeChangingEffects_4) || hasLayer(Layer.ColorChangingEffects_5) || hasLayer(Layer.AbilityAddingRemovingEffects_6)
|| hasLayer(Layer.PTChangingEffects_7)) {
this.affectedObjectsSet = true;
}
}
startingTurn = game.getTurnNum();
startingControllerId = source.getControllerId();
}
@Override
public boolean isInactive(Ability source, Game game) {
if (duration == Duration.UntilYourNextTurn) {
Player player = game.getPlayer(startingControllerId);
if (player != null) {
if (player.isInGame()) {
return game.getActivePlayerId().equals(startingControllerId) && game.getTurnNum() != startingTurn;
}
return player.hasReachedNextTurnAfterLeaving();
}
return true;
}
return false;
}
@Override
public Layer getLayer() {
return layer;
}
@Override
public SubLayer getSublayer() {
return sublayer;
}
@Override
public void overrideRuleText(String text) {
this.staticText = text;
}
protected static boolean isCanKill(DynamicValue toughness) {
if (toughness instanceof StaticValue) {
return toughness.calculate(null, null, null) < 0;
}
if (toughness instanceof SignInversionDynamicValue) {
// count this class as used for "-{something_positive}"
return true;
}
if (toughness instanceof DomainValue) {
return ((DomainValue) toughness).getAmount() < 0;
}
return false;
}
@Override
public List<MageObjectReference> getAffectedObjects() {
return affectedObjectList;
}
/**
* Returns the status if the effect is temporary added to the
* ContinuousEffects
*
* @return
*/
@Override
public boolean isTemporary() {
return temporary;
}
@Override
public void setTemporary(boolean temporary) {
this.temporary = temporary;
}
public boolean isCharacterDefining() {
return characterDefining;
}
public void setCharacterDefining(boolean characterDefining) {
this.characterDefining = characterDefining;
}
@Override
public Set<UUID> isDependentTo(List<ContinuousEffect> allEffectsInLayer) {
if (dependendToType != null) {
return allEffectsInLayer.stream()
.filter(effect -> effect.getDependencyTypes().contains(dependendToType))
.map(Effect::getId)
.collect(Collectors.toSet());
}
return new HashSet<>();
}
@Override
public EnumSet<DependencyType> getDependencyTypes() {
return dependencyTypes;
}
@Override
public void addDependencyType(DependencyType dependencyType) {
dependencyTypes.add(dependencyType);
}
@Override
public void setDependedToType(DependencyType dependencyType) {
dependendToType = dependencyType;
}
}