foul-magics/Mage/src/mage/abilities/effects/ContinuousEffects.java

809 lines
35 KiB
Java

/*
* 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.io.Serializable;
import java.util.*;
import mage.constants.AsThoughEffectType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.SubLayer;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
import mage.abilities.keyword.SpliceOntoArcaneAbility;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.constants.SpellAbilityType;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardIdPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCardInHand;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class ContinuousEffects implements Serializable {
private Date lastSetTimestamp;
//transient Continuous effects
private ContinuousEffectsList<ContinuousEffect> layeredEffects = new ContinuousEffectsList<ContinuousEffect>();
private ContinuousEffectsList<ReplacementEffect> replacementEffects = new ContinuousEffectsList<ReplacementEffect>();
private ContinuousEffectsList<PreventionEffect> preventionEffects = new ContinuousEffectsList<PreventionEffect>();
private ContinuousEffectsList<RequirementEffect> requirementEffects = new ContinuousEffectsList<RequirementEffect>();
private ContinuousEffectsList<RestrictionEffect> restrictionEffects = new ContinuousEffectsList<RestrictionEffect>();
private ContinuousEffectsList<AsThoughEffect> asThoughEffects = new ContinuousEffectsList<AsThoughEffect>();
private ContinuousEffectsList<CostModificationEffect> costModificationEffects = new ContinuousEffectsList<CostModificationEffect>();
private ContinuousEffectsList<SpliceCardEffect> spliceCardEffects = new ContinuousEffectsList<SpliceCardEffect>();
private List<ContinuousEffectsList<?>> allEffectsLists = new ArrayList<ContinuousEffectsList<?>>();
private final ApplyCountersEffect applyCounters;
private final PlaneswalkerRedirectionEffect planeswalkerRedirectionEffect;
private final AuraReplacementEffect auraReplacementEffect;
private List<ContinuousEffect> previous = new ArrayList<ContinuousEffect>();
// effect.id -> sourceId - which effect was added by which sourceId
private Map<UUID, UUID> sources = new HashMap<UUID, UUID>();
public ContinuousEffects() {
applyCounters = new ApplyCountersEffect();
planeswalkerRedirectionEffect = new PlaneswalkerRedirectionEffect();
auraReplacementEffect = new AuraReplacementEffect();
collectAllEffects();
}
public ContinuousEffects(final ContinuousEffects effect) {
this.applyCounters = effect.applyCounters.copy();
this.planeswalkerRedirectionEffect = effect.planeswalkerRedirectionEffect.copy();
this.auraReplacementEffect = effect.auraReplacementEffect.copy();
layeredEffects = effect.layeredEffects.copy();
replacementEffects = effect.replacementEffects.copy();
preventionEffects = effect.preventionEffects.copy();
requirementEffects = effect.requirementEffects.copy();
restrictionEffects = effect.restrictionEffects.copy();
asThoughEffects = effect.asThoughEffects.copy();
costModificationEffects = effect.costModificationEffects.copy();
spliceCardEffects = effect.spliceCardEffects.copy();
for (Map.Entry<UUID, UUID> entry : effect.sources.entrySet()) {
sources.put(entry.getKey(), entry.getValue());
}
collectAllEffects();
lastSetTimestamp = effect.lastSetTimestamp;
}
private void collectAllEffects() {
allEffectsLists.add(layeredEffects);
allEffectsLists.add(replacementEffects);
allEffectsLists.add(preventionEffects);
allEffectsLists.add(requirementEffects);
allEffectsLists.add(restrictionEffects);
allEffectsLists.add(asThoughEffects);
allEffectsLists.add(costModificationEffects);
allEffectsLists.add(spliceCardEffects);
}
public ContinuousEffects copy() {
return new ContinuousEffects(this);
}
public List<RequirementEffect> getRequirementEffects() {
return requirementEffects;
}
public List<RestrictionEffect> getRestrictionEffects() {
return restrictionEffects;
}
public void removeEndOfCombatEffects() {
layeredEffects.removeEndOfCombatEffects();
replacementEffects.removeEndOfCombatEffects();
preventionEffects.removeEndOfCombatEffects();
requirementEffects.removeEndOfCombatEffects();
restrictionEffects.removeEndOfCombatEffects();
asThoughEffects.removeEndOfCombatEffects();
costModificationEffects.removeEndOfCombatEffects();
spliceCardEffects.removeEndOfCombatEffects();
}
public void removeEndOfTurnEffects() {
layeredEffects.removeEndOfTurnEffects();
replacementEffects.removeEndOfTurnEffects();
preventionEffects.removeEndOfTurnEffects();
requirementEffects.removeEndOfTurnEffects();
restrictionEffects.removeEndOfTurnEffects();
asThoughEffects.removeEndOfTurnEffects();
costModificationEffects.removeEndOfTurnEffects();
spliceCardEffects.removeEndOfTurnEffects();
}
public void removeInactiveEffects(Game game) {
layeredEffects.removeInactiveEffects(game);
replacementEffects.removeInactiveEffects(game);
preventionEffects.removeInactiveEffects(game);
requirementEffects.removeInactiveEffects(game);
restrictionEffects.removeInactiveEffects(game);
asThoughEffects.removeInactiveEffects(game);
costModificationEffects.removeInactiveEffects(game);
spliceCardEffects.removeInactiveEffects(game);
}
public List<ContinuousEffect> getLayeredEffects(Game game) {
List<ContinuousEffect> layerEffects = new ArrayList<ContinuousEffect>();
for (ContinuousEffect effect: layeredEffects) {
switch (effect.getDuration()) {
case WhileOnBattlefield:
case WhileOnStack:
case WhileInGraveyard:
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability: abilities) {
// If e.g. triggerd abilities (non static) created the effect, the ability must not be in usable zone (e.g. Unearth giving Haste effect)
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
layerEffects.add(effect);
break;
}
}
break;
default:
layerEffects.add(effect);
}
}
updateTimestamps(layerEffects);
Collections.sort(layerEffects, new TimestampSorter());
return layerEffects;
}
/**
* Initially effect timestamp is set when game starts in game.loadCard method.
* After that timestamp should be updated whenever effect becomes "actual" meaning it becomes turned on
* that is defined by Ability.#isInUseableZone(Game, boolean) method in #getLayeredEffects(Game).
* @param layerEffects
*/
private void updateTimestamps(List<ContinuousEffect> layerEffects) {
for (ContinuousEffect continuousEffect : layerEffects) {
// check if it's new, then set timestamp
if (!previous.contains(continuousEffect)) {
setUniqueTimesstamp(continuousEffect);
}
}
previous.clear();
previous.addAll(layerEffects);
}
public void setUniqueTimesstamp(ContinuousEffect effect) {
do {
effect.setTimestamp();
}
while (effect.getTimestamp().equals(lastSetTimestamp)); // prevent to set the same timestamp so logical order is saved
lastSetTimestamp = effect.getTimestamp();
}
private List<ContinuousEffect> filterLayeredEffects(List<ContinuousEffect> effects, Layer layer) {
List<ContinuousEffect> layerEffects = new ArrayList<ContinuousEffect>();
for (ContinuousEffect effect: effects) {
if (effect.hasLayer(layer)) {
layerEffects.add(effect);
}
}
return layerEffects;
}
public HashMap<RequirementEffect, HashSet<Ability>> getApplicableRequirementEffects(Permanent permanent, Game game) {
HashMap<RequirementEffect, HashSet<Ability>> effects = new HashMap<RequirementEffect, HashSet<Ability>>();
for (RequirementEffect effect: requirementEffects) {
HashSet<Ability> abilities = requirementEffects.getAbility(effect.getId());
HashSet<Ability> applicableAbilities = new HashSet<Ability>();
for (Ability ability : abilities) {
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
if (effect.applies(permanent, ability, game)) {
applicableAbilities.add(ability);
}
}
}
if (!applicableAbilities.isEmpty()) {
effects.put(effect, abilities);
}
}
return effects;
}
public HashMap<RestrictionEffect, HashSet<Ability>> getApplicableRestrictionEffects(Permanent permanent, Game game) {
HashMap<RestrictionEffect, HashSet<Ability>> effects = new HashMap<RestrictionEffect, HashSet<Ability>>();
for (RestrictionEffect effect: restrictionEffects) {
HashSet<Ability> abilities = restrictionEffects.getAbility(effect.getId());
HashSet<Ability> applicableAbilities = new HashSet<Ability>();
for (Ability ability : abilities) {
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, permanent, false)) {
if (effect.applies(permanent, ability, game)) {
applicableAbilities.add(ability);
}
}
}
if (!applicableAbilities.isEmpty()) {
effects.put(effect, abilities);
}
}
return effects;
}
/**
*
* @param event
* @param game
* @return a list of all {@link ReplacementEffect} that apply to the current event
*/
private HashMap<ReplacementEffect, HashSet<Ability>> getApplicableReplacementEffects(GameEvent event, Game game) {
// List<ReplacementEffect> replaceEffects = new ArrayList<ReplacementEffect>();
HashMap<ReplacementEffect, HashSet<Ability>> replaceEffects = new HashMap<ReplacementEffect, HashSet<Ability>>();
if (planeswalkerRedirectionEffect.applies(event, null, game)) {
replaceEffects.put(planeswalkerRedirectionEffect, null);
}
if(auraReplacementEffect.applies(event, null, game)){
replaceEffects.put(auraReplacementEffect, null);
}
//get all applicable transient Replacement effects
for (ReplacementEffect effect: replacementEffects) {
if (event.getAppliedEffects() != null && event.getAppliedEffects().contains(effect.getId())) {
// Effect already applied to this event, ignore it
// TODO: Handle also gained effect that are connected to different abilities.
continue;
}
HashSet<Ability> abilities = replacementEffects.getAbility(effect.getId());
HashSet<Ability> applicableAbilities = new HashSet<Ability>();
for (Ability ability : abilities) {
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
if (!game.getScopeRelevant() || effect.hasSelfScope() || !event.getTargetId().equals(ability.getSourceId())) {
if (effect.applies(event, ability, game)) {
applicableAbilities.add(ability);
}
}
}
}
}
if (!applicableAbilities.isEmpty()) {
replaceEffects.put((ReplacementEffect) effect, applicableAbilities);
}
}
for (PreventionEffect effect: preventionEffects) {
HashSet<Ability> abilities = preventionEffects.getAbility(effect.getId());
HashSet<Ability> applicableAbilities = new HashSet<Ability>();
for (Ability ability : abilities) {
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
if (effect.applies(event, ability, game)) {
applicableAbilities.add(ability);
}
}
}
}
if (!applicableAbilities.isEmpty()) {
replaceEffects.put((ReplacementEffect) effect, applicableAbilities);
}
}
return replaceEffects;
}
/**
* Filters out cost modification effects that are not active.
*
* @param game
* @return
*/
private List<CostModificationEffect> getApplicableCostModificationEffects(Game game) {
List<CostModificationEffect> costEffects = new ArrayList<CostModificationEffect>();
for (CostModificationEffect effect: costModificationEffects) {
HashSet<Ability> abilities = costModificationEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
costEffects.add(effect);
break;
}
}
}
}
return costEffects;
}
/**
* Filters out splice effects that are not active.
*
* @param game
* @return
*/
private List<SpliceCardEffect> getApplicableSpliceCardEffects(Game game) {
List<SpliceCardEffect> spliceEffects = new ArrayList<SpliceCardEffect>();
for (SpliceCardEffect effect: spliceCardEffects) {
HashSet<Ability> abilities = spliceCardEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
spliceEffects.add(effect);
break;
}
}
}
}
return spliceEffects;
}
public boolean asThough(UUID objectId, AsThoughEffectType type, Game game) {
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(game);
for (AsThoughEffect effect: asThoughEffectsList) {
if (effect.getAsThoughEffectType() == type) {
HashSet<Ability> abilities = asThoughEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
if (effect.applies(objectId, ability, game)) {
return true;
}
}
}
}
return false;
}
/**
* Filters out asThough effects that are not active.
*
* @param game
* @return
*/
private List<AsThoughEffect> getApplicableAsThoughEffects(Game game) {
List<AsThoughEffect> asThoughEffectsList = new ArrayList<AsThoughEffect>();
for (AsThoughEffect effect: asThoughEffects) {
HashSet<Ability> abilities = asThoughEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, false)) {
if (effect.getDuration() != Duration.OneUse || !effect.isUsed()) {
asThoughEffectsList.add(effect);
break;
}
}
}
}
return asThoughEffectsList;
}
/**
* Inspects all {@link Permanent permanent's} {@link Ability abilities} on the battlefield
* for {@link CostModificationEffect cost modification effects} and applies them if necessary.
*
* @param abilityToModify
* @param game
* @return
*/
public void costModification ( Ability abilityToModify, Game game ) {
List<CostModificationEffect> costEffects = getApplicableCostModificationEffects(game);
for ( CostModificationEffect effect : costEffects) {
HashSet<Ability> abilities = costModificationEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
if ( effect.applies(abilityToModify, ability, game) ) {
effect.apply(game, ability, abilityToModify);
}
}
}
}
/**
* Checks all available splice effects to be applied.
*
* @param abilityToModify
* @param game
* @return
*/
public void applySpliceEffects ( Ability abilityToModify, Game game ) {
if ( ((SpellAbility) abilityToModify).getSpellAbilityType().equals(SpellAbilityType.SPLICE)) {
// on a spliced ability of a spell can't be spliced again
return;
}
List<SpliceCardEffect> spliceEffects = getApplicableSpliceCardEffects(game);
// get the applyable splice abilities
List<SpliceOntoArcaneAbility> spliceAbilities = new ArrayList<SpliceOntoArcaneAbility>();
for (SpliceCardEffect effect : spliceEffects) {
HashSet<Ability> abilities = spliceCardEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
if (effect.applies(abilityToModify, ability, game) ) {
spliceAbilities.add((SpliceOntoArcaneAbility) ability);
}
}
}
// check if player wants to use splice
if (spliceAbilities.size() > 0) {
Player controller = game.getPlayer(abilityToModify.getControllerId());
if (controller.chooseUse(Outcome.Benefit, "Splice a card?", game)) {
Cards cardsToReveal = new CardsImpl();
do {
FilterCard filter = new FilterCard("a card to splice");
ArrayList<Predicate<MageObject>> idPredicates = new ArrayList<Predicate<MageObject>>();
for (SpliceOntoArcaneAbility ability : spliceAbilities) {
idPredicates.add(new CardIdPredicate((ability.getSourceId())));
}
filter.add(Predicates.or(idPredicates));
TargetCardInHand target = new TargetCardInHand(filter);
target.setRequired(true);
controller.chooseTarget(Outcome.Benefit, target, abilityToModify, game);
UUID cardId = target.getFirstTarget();
if (cardId != null) {
SpliceOntoArcaneAbility selectedAbility = null;
for(SpliceOntoArcaneAbility ability :spliceAbilities) {
if (ability.getSourceId().equals(cardId)) {
selectedAbility = ability;
break;
}
}
if (selectedAbility != null) {
SpliceCardEffect spliceEffect = (SpliceCardEffect) selectedAbility.getEffects().get(0);
spliceEffect.apply(game, selectedAbility, abilityToModify);
cardsToReveal.add(game.getCard(cardId));
spliceAbilities.remove(selectedAbility);
}
}
} while (!spliceAbilities.isEmpty() && controller.chooseUse(Outcome.Benefit, "Splice another card?", game));
controller.revealCards("Spliced cards", cardsToReveal, game);
}
}
}
public boolean replaceEvent(GameEvent event, Game game) {
boolean caught = false;
HashMap<UUID, HashSet<UUID>> consumed = new HashMap<UUID, HashSet<UUID>>();
do {
HashMap<ReplacementEffect, HashSet<Ability>> rEffects = getApplicableReplacementEffects(event, game);
// Remove all consumed effects (ability dependant)
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext();){
ReplacementEffect entry = it1.next();
if (consumed.containsKey(entry.getId())) {
HashSet<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
if (rEffects.get(entry) == null || consumedAbilitiesIds.size() == ((HashSet<Ability>) rEffects.get(entry)).size()) {
it1.remove();
} else {
Iterator it = ((HashSet<Ability>) rEffects.get(entry)).iterator();
while (it.hasNext()) {
Ability ability = (Ability) it.next();
if (consumedAbilitiesIds.contains(ability.getId())) {
it.remove();
}
}
}
}
}
// no effects left, quit
if (rEffects.isEmpty()) {
break;
}
int index;
boolean onlyOne = false;
if (rEffects.size() == 1) {
ReplacementEffect effect = (ReplacementEffect) rEffects.keySet().iterator().next();
HashSet<Ability> abilities = replacementEffects.getAbility(effect.getId());
if (abilities == null || abilities.size() == 1) {
onlyOne = true;
}
}
if (onlyOne) {
index = 0;
} else {
//20100716 - 616.1c
Player player = game.getPlayer(event.getPlayerId());
index = player.chooseEffect(getReplacementEffectsTexts(rEffects, game), game);
}
// get the selected effect
int checked = 0;
ReplacementEffect rEffect = null;
Ability rAbility = null;
for (Map.Entry entry : rEffects.entrySet()) {
if (entry.getValue() == null) {
if (checked == index) {
rEffect = (ReplacementEffect) entry.getKey();
break;
} else {
checked++;
}
} else {
HashSet<Ability> abilities = (HashSet<Ability>) entry.getValue();
int size = abilities.size();
if (index > (checked + size - 1)) {
checked += size;
} else {
rEffect = (ReplacementEffect) entry.getKey();
Iterator it = abilities.iterator();
while (it.hasNext() && rAbility == null) {
if (checked == index) {
rAbility = (Ability) it.next();
} else {
it.next();
checked++;
}
}
}
}
}
if (rEffect != null) {
event.getAppliedEffects().add(rEffect.getId());
caught = rEffect.replaceEvent(event, rAbility, game);
}
if (caught) { // Event was completely replaced -> stop applying effects to it
break;
}
// add used effect to consumed effects
if (rEffect != null) {
if (consumed.containsKey(rEffect.getId())) {
HashSet<UUID> set = consumed.get(rEffect.getId());
if (rAbility != null) {
if (!set.contains(rAbility.getId())) {
set.add(rAbility.getId());
}
}
} else {
HashSet<UUID> set = new HashSet<UUID>();
if (rAbility != null) { // in case of AuraReplacementEffect or PlaneswalkerReplacementEffect there is no Ability
set.add(rAbility.getId());
}
consumed.put(rEffect.getId(), set);
}
}
game.applyEffects();
} while (true);
return caught;
}
//20091005 - 613
public void apply(Game game) {
removeInactiveEffects(game);
List<ContinuousEffect> layerEffects = getLayeredEffects(game);
List<ContinuousEffect> layer = filterLayeredEffects(layerEffects, Layer.CopyEffects_1);
for (ContinuousEffect effect: layer) {
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
effect.apply(Layer.CopyEffects_1, SubLayer.NA, ability, game);
}
}
//Reload layerEffect if copy effects were applied
if (layer.size()>0) {
layerEffects = getLayeredEffects(game);
}
layer = filterLayeredEffects(layerEffects, Layer.ControlChangingEffects_2);
for (ContinuousEffect effect: layer) {
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
effect.apply(Layer.ControlChangingEffects_2, SubLayer.NA, ability, game);
}
}
layer = filterLayeredEffects(layerEffects, Layer.TextChangingEffects_3);
for (ContinuousEffect effect: layer) {
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
effect.apply(Layer.TextChangingEffects_3, SubLayer.NA, ability, game);
}
}
layer = filterLayeredEffects(layerEffects, Layer.TypeChangingEffects_4);
for (ContinuousEffect effect: layer) {
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
effect.apply(Layer.TypeChangingEffects_4, SubLayer.NA, ability, game);
}
}
layer = filterLayeredEffects(layerEffects, Layer.ColorChangingEffects_5);
for (ContinuousEffect effect: layer) {
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
effect.apply(Layer.ColorChangingEffects_5, SubLayer.NA, ability, game);
}
}
layer = filterLayeredEffects(layerEffects, Layer.AbilityAddingRemovingEffects_6);
for (ContinuousEffect effect: layer) {
if (layerEffects.contains(effect)) {
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game);
}
}
layerEffects = getLayeredEffects(game);
}
layer = filterLayeredEffects(layerEffects, Layer.PTChangingEffects_7);
for (ContinuousEffect effect: layer) {
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
effect.apply(Layer.PTChangingEffects_7, SubLayer.SetPT_7b, ability, game);
}
}
for (ContinuousEffect effect: layer) {
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
effect.apply(Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, ability, game);
}
}
applyCounters.apply(Layer.PTChangingEffects_7, SubLayer.Counters_7d, null, game);
for (ContinuousEffect effect: layer) {
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
effect.apply(Layer.PTChangingEffects_7, SubLayer.SwitchPT_e, ability, game);
}
}
layer = filterLayeredEffects(layerEffects, Layer.PlayerEffects);
for (ContinuousEffect effect: layer) {
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
effect.apply(Layer.PlayerEffects, SubLayer.NA, ability, game);
}
}
layer = filterLayeredEffects(layerEffects, Layer.RulesEffects);
for (ContinuousEffect effect: layer) {
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
for (Ability ability : abilities) {
effect.apply(Layer.RulesEffects, SubLayer.NA, ability, game);
}
}
}
public void addEffect(ContinuousEffect effect, UUID sourceId, Ability source) {
addEffect(effect, source);
sources.put(effect.getId(), sourceId);
}
public void addEffect(ContinuousEffect effect, Ability source) {
switch (effect.getEffectType()) {
case REPLACEMENT:
ReplacementEffect newReplacementEffect = (ReplacementEffect)effect;
replacementEffects.addEffect(newReplacementEffect, source);
break;
case PREVENTION:
PreventionEffect newPreventionEffect = (PreventionEffect)effect;
preventionEffects.addEffect(newPreventionEffect, source);
break;
case RESTRICTION:
RestrictionEffect newRestrictionEffect = (RestrictionEffect)effect;
restrictionEffects.addEffect(newRestrictionEffect, source);
break;
case REQUIREMENT:
RequirementEffect newRequirementEffect = (RequirementEffect)effect;
requirementEffects.addEffect(newRequirementEffect, source);
break;
case ASTHOUGH:
AsThoughEffect newAsThoughEffect = (AsThoughEffect)effect;
asThoughEffects.addEffect(newAsThoughEffect, source);
break;
case COSTMODIFICATION:
CostModificationEffect newCostModificationEffect = (CostModificationEffect)effect;
costModificationEffects.addEffect(newCostModificationEffect, source);
break;
case SPLICE:
SpliceCardEffect newSpliceCardEffect = (SpliceCardEffect)effect;
spliceCardEffects.addEffect(newSpliceCardEffect, source);
break;
default:
ContinuousEffect newEffect = (ContinuousEffect)effect;
layeredEffects.addEffect(newEffect, source);
break;
}
}
public void setController(UUID cardId, UUID controllerId) {
for (ContinuousEffectsList effectsList : allEffectsLists) {
setControllerForEffect(effectsList, cardId, controllerId);
}
}
private void setControllerForEffect(ContinuousEffectsList<?> effects, UUID cardId, UUID controllerId) {
for (Effect effect : effects) {
HashSet<Ability> abilities = effects.getAbility(effect.getId());
for (Ability ability : abilities) {
if (ability.getSourceId().equals(cardId)) {
ability.setControllerId(controllerId);
}
}
}
}
public void clear() {
for (ContinuousEffectsList effectsList : allEffectsLists) {
effectsList.clear();
}
sources.clear();
}
/**
* Removes effects granted by sourceId
*
* @param sourceId
*/
public List<Effect> removeGainedEffectsForSource(UUID sourceId) {
List<Effect> effects = new ArrayList<Effect>();
Set<UUID> effectsToRemove = new HashSet<UUID>();
for (Map.Entry<UUID, UUID> source : sources.entrySet()) {
if (sourceId.equals(source.getValue())) {
for (ContinuousEffectsList effectsList : allEffectsLists) {
Iterator it = effectsList.iterator();
while (it.hasNext()) {
ContinuousEffect effect = (ContinuousEffect) it.next();
if (source.getKey().equals(effect.getId())) {
effectsToRemove.add(effect.getId());
effectsList.removeEffectAbilityMap(effect.getId());
it.remove();
}
}
}
}
}
for (UUID effectId: effectsToRemove) {
sources.remove(effectId);
}
return effects;
}
public List<String> getReplacementEffectsTexts(HashMap<ReplacementEffect, HashSet<Ability>> rEffects, Game game) {
List<String> texts = new ArrayList<String>();
for (Map.Entry entry : rEffects.entrySet()) {
for (Ability ability :(HashSet<Ability>) entry.getValue()) {
MageObject object = game.getObject(ability.getSourceId());
if (object != null) {
texts.add(ability.getRule(object.getName()));
} else {
texts.add(((ReplacementEffect)entry.getKey()).getText(null));
}
}
}
return texts;
}
}
class TimestampSorter implements Comparator<ContinuousEffect> {
@Override
public int compare(ContinuousEffect one, ContinuousEffect two) {
return one.getTimestamp().compareTo(two.getTimestamp());
}
}