mirror of
https://github.com/magefree/mage.git
synced 2025-12-22 03:22:00 -08:00
Fixed the causes that triggered abilities were applied more often than they should. Fixed the lose ability bug (test with Master of the Pearl Trident giving island walk). Tests now build without errors. Only rarely the Grounded/Drake Umbra lose ability test fails.
This commit is contained in:
parent
80e9d748a7
commit
bb5b9587e0
11 changed files with 119 additions and 29 deletions
|
|
@ -96,4 +96,25 @@ public class LoseAbilityTest extends CardTestPlayerBase {
|
|||
// should NOT have flying
|
||||
Assert.assertFalse(airElemental.getAbilities().contains(FlyingAbility.getInstance()));
|
||||
}
|
||||
/**
|
||||
* Tests that gaining two times a triggered ability and losing one will result in only one triggering
|
||||
*/
|
||||
@Test
|
||||
public void testMultiGainTriggeredVsLoseAbility() {
|
||||
addCard(Constants.Zone.BATTLEFIELD, playerA, "Sublime Archangel",2);
|
||||
addCard(Constants.Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
|
||||
addCard(Constants.Zone.BATTLEFIELD, playerA, "Plains", 3);
|
||||
addCard(Constants.Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
addCard(Constants.Zone.HAND, playerA, "Turn to Frog");
|
||||
addCard(Constants.Zone.BATTLEFIELD, playerB, "Island", 5);
|
||||
|
||||
castSpell(3, Constants.PhaseStep.PRECOMBAT_MAIN, playerA, "Turn to Frog", "Sublime Archangel");
|
||||
attack(3, playerA, "Silvercoat Lion");
|
||||
|
||||
setStopAt(3, Constants.PhaseStep.END_COMBAT);
|
||||
execute();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 16);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
|
|
@ -28,28 +29,36 @@
|
|||
|
||||
package mage.abilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.PermanentCard;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class TriggeredAbilities extends HashMap<String, TriggeredAbility> {
|
||||
|
||||
private Map<String, List<UUID>> sources = new HashMap<String, List<UUID>>();
|
||||
|
||||
public TriggeredAbilities() {}
|
||||
|
||||
public TriggeredAbilities(final TriggeredAbilities abilities) {
|
||||
for (Map.Entry<String, TriggeredAbility> entry: abilities.entrySet()) {
|
||||
this.put(entry.getKey(), entry.getValue().copy());
|
||||
}
|
||||
for (Map.Entry<String, List<UUID>> entry : abilities.sources.entrySet()) {
|
||||
sources.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void checkTriggers(GameEvent event, Game game) {
|
||||
|
|
@ -96,6 +105,22 @@ public class TriggeredAbilities extends HashMap<String, TriggeredAbility> {
|
|||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a by sourceId gained triggered ability
|
||||
*
|
||||
* @param ability - the gained ability
|
||||
* @param sourceId - the source that assigned the ability
|
||||
* @param attachedTo - the object that gained the ability
|
||||
*/
|
||||
public void add(TriggeredAbility ability, UUID sourceId, MageObject attachedTo) {
|
||||
this.add(ability, attachedTo);
|
||||
List<UUID> uuidList = new LinkedList<UUID>();
|
||||
uuidList.add(sourceId);
|
||||
// if the object that gained the ability moves zone, also then the triggered ability must be removed
|
||||
uuidList.add(attachedTo.getId());
|
||||
sources.put(getKey(ability, attachedTo), uuidList);
|
||||
}
|
||||
|
||||
public void add(TriggeredAbility ability, MageObject attachedTo) {
|
||||
this.put(getKey(ability, attachedTo), ability);
|
||||
}
|
||||
|
|
@ -108,6 +133,25 @@ public class TriggeredAbilities extends HashMap<String, TriggeredAbility> {
|
|||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes gained abilities by sourceId
|
||||
*
|
||||
* @param sourceId
|
||||
*/
|
||||
public List<String> removeGainedAbilitiesForSource(UUID sourceId) {
|
||||
List<String> keysToRemove = new ArrayList<String>();
|
||||
|
||||
for (Map.Entry<String, List<UUID>> entry : sources.entrySet()) {
|
||||
if (entry.getValue().contains(sourceId)) {
|
||||
keysToRemove.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
for (String key: keysToRemove) {
|
||||
sources.remove(key);
|
||||
}
|
||||
return keysToRemove;
|
||||
}
|
||||
|
||||
public TriggeredAbilities copy() {
|
||||
return new TriggeredAbilities(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl<GainAbilityAllEff
|
|||
for (Permanent perm: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
|
||||
if (!this.affectedObjectsSet || objects.contains(perm.getId())) {
|
||||
if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
|
||||
perm.addAbility(ability, game);
|
||||
perm.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl<GainAbilityA
|
|||
}
|
||||
}
|
||||
if (creature != null) {
|
||||
creature.addAbility(ability, game);
|
||||
creature.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public class GainAbilityPairedEffect extends ContinuousEffectImpl<GainAbilityPai
|
|||
Permanent paired = game.getPermanent(permanent.getPairedCard());
|
||||
if (paired != null) {
|
||||
permanent.addAbility(ability, game);
|
||||
paired.addAbility(ability, game);
|
||||
paired.addAbility(ability, source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl<GainAbilityTar
|
|||
for (UUID permanentId : targetPointer.getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(permanentId);
|
||||
if (permanent != null) {
|
||||
permanent.addAbility(ability, game);
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
affectedTargets++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -40,6 +41,8 @@ import mage.game.permanent.Permanent;
|
|||
*/
|
||||
public class LoseAbilityAttachedEffect extends ContinuousEffectImpl<LoseAbilityAttachedEffect> {
|
||||
|
||||
private final static Logger logger = Logger.getLogger(LoseAbilityAttachedEffect.class);
|
||||
|
||||
protected Ability ability;
|
||||
protected AttachmentType attachmentType;
|
||||
|
||||
|
|
@ -68,7 +71,10 @@ public class LoseAbilityAttachedEffect extends ContinuousEffectImpl<LoseAbilityA
|
|||
Permanent creature = game.getPermanent(equipment.getAttachedTo());
|
||||
if (creature != null) {
|
||||
while (creature.getAbilities().contains(ability)) {
|
||||
creature.getAbilities().remove(ability);
|
||||
if (!creature.getAbilities().remove(ability)) {
|
||||
// Something went wrong - ability has an other id?
|
||||
logger.warn("ability" + ability.getRule() + "couldn't be removed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1456,6 +1456,8 @@ public abstract class GameImpl<T extends GameImpl<T>> implements Game, Serializa
|
|||
}
|
||||
}
|
||||
getContinuousEffects().removeGainedEffectsForSource(sourceId);
|
||||
// remove gained triggered abilities
|
||||
getState().resetForSourceId(sourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -60,15 +60,15 @@ import java.io.Serializable;
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*
|
||||
* since at any time the game state may be copied and restored you cannot
|
||||
* rely on any object maintaining it's instance
|
||||
* it then becomes necessary to only refer to objects by their ids since
|
||||
* these will always remain constant throughout its lifetime
|
||||
*
|
||||
*/
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*
|
||||
* since at any time the game state may be copied and restored you cannot
|
||||
* rely on any object maintaining it's instance
|
||||
* it then becomes necessary to only refer to objects by their ids since
|
||||
* these will always remain constant throughout its lifetime
|
||||
*
|
||||
*/
|
||||
public class GameState implements Serializable, Copyable<GameState> {
|
||||
|
||||
private Players players;
|
||||
|
|
@ -478,7 +478,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
else if (ability instanceof TriggeredAbility) {
|
||||
// TODO: add sources for triggers - the same way as in addEffect: sources
|
||||
this.triggers.add((TriggeredAbility)ability, attachedTo);
|
||||
this.triggers.add((TriggeredAbility)ability, sourceId, attachedTo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -555,6 +555,18 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes Triggered abilities that were gained from sourceId
|
||||
*
|
||||
* @param sourceId
|
||||
*/
|
||||
public void resetForSourceId(UUID sourceId) {
|
||||
List<String> keysToRemove = triggers.removeGainedAbilitiesForSource(sourceId);
|
||||
for (String key : keysToRemove) {
|
||||
triggers.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
battlefield.clear();
|
||||
effects.clear();
|
||||
|
|
|
|||
|
|
@ -173,9 +173,6 @@ public class PermanentCard extends PermanentImpl<PermanentCard> {
|
|||
if (controller != null && controller.removeFromBattlefield(this, game)) {
|
||||
ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, controllerId, fromZone, toZone);
|
||||
if (!game.replaceEvent(event)) {
|
||||
if (event.getFromZone().equals(Zone.BATTLEFIELD)) {
|
||||
game.resetForSourceId(getId());
|
||||
}
|
||||
Player owner = game.getPlayer(ownerId);
|
||||
game.rememberLKI(objectId, Zone.BATTLEFIELD, this);
|
||||
if (owner != null) {
|
||||
|
|
@ -201,6 +198,9 @@ public class PermanentCard extends PermanentImpl<PermanentCard> {
|
|||
}
|
||||
game.setZone(objectId, event.getToZone());
|
||||
game.fireEvent(event);
|
||||
if (event.getFromZone().equals(Zone.BATTLEFIELD)) {
|
||||
game.resetForSourceId(getId());
|
||||
}
|
||||
return game.getState().getZone(objectId) == toZone;
|
||||
}
|
||||
}
|
||||
|
|
@ -224,6 +224,9 @@ public class PermanentCard extends PermanentImpl<PermanentCard> {
|
|||
}
|
||||
game.setZone(objectId, event.getToZone());
|
||||
game.fireEvent(event);
|
||||
if (event.getFromZone().equals(Zone.BATTLEFIELD)) {
|
||||
game.resetForSourceId(getId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,6 +196,8 @@ public abstract class PermanentImpl<T extends PermanentImpl<T>> extends CardImpl
|
|||
@Override
|
||||
public void removeAllAbilities(UUID sourceId, Game game) {
|
||||
getAbilities().clear();
|
||||
// removes abilities that were gained from abilities of this permanent
|
||||
game.resetForSourceId(this.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue