mirror of
https://github.com/correl/mage.git
synced 2025-04-10 17:00:08 -09:00
Improved ability outcome processing;
This commit is contained in:
parent
ab10ee10c9
commit
89394ffe0a
20 changed files with 227 additions and 100 deletions
Mage.Server.Plugins
Mage.Player.AI.MA/src/mage/player/ai
Mage.Player.AI/src/main/java/mage/player/ai
Mage.Sets/src/mage/cards
Mage.Tests/src/test/java/org/mage/test/cards/abilities
Mage/src/main/java/mage
|
@ -264,6 +264,8 @@ public class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
Iterator<Ability> iterator = options.iterator();
|
Iterator<Ability> iterator = options.iterator();
|
||||||
boolean bad = true;
|
boolean bad = true;
|
||||||
boolean good = true;
|
boolean good = true;
|
||||||
|
|
||||||
|
// TODO: add custom outcome from ability?
|
||||||
for (Effect effect : ability.getEffects()) {
|
for (Effect effect : ability.getEffects()) {
|
||||||
if (effect.getOutcome().isGood()) {
|
if (effect.getOutcome().isGood()) {
|
||||||
bad = false;
|
bad = false;
|
||||||
|
|
|
@ -6,24 +6,25 @@
|
||||||
|
|
||||||
package mage.player.ai.ma.optimizers.impl;
|
package mage.player.ai.ma.optimizers.impl;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes abilities that require only discard a card for activation.
|
* Removes abilities that require only discard a card for activation.
|
||||||
*
|
*
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class OutcomeOptimizer extends BaseTreeOptimizer {
|
public class OutcomeOptimizer extends BaseTreeOptimizer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void filter(Game game, List<Ability> actions) {
|
public void filter(Game game, List<Ability> actions) {
|
||||||
for (Ability ability : actions) {
|
for (Ability ability : actions) {
|
||||||
for (Effect effect: ability.getEffects()) {
|
for (Effect effect : ability.getEffects()) {
|
||||||
if (effect.getOutcome() == Outcome.AIDontUseIt) {
|
if (ability.getCustomOutcome() == Outcome.AIDontUseIt || effect.getOutcome() == Outcome.AIDontUseIt) {
|
||||||
removeAbility(ability);
|
removeAbility(ability);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1160,12 +1160,12 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
if (!playableAbilities.isEmpty()) {
|
if (!playableAbilities.isEmpty()) {
|
||||||
for (ActivatedAbility ability : playableAbilities) {
|
for (ActivatedAbility ability : playableAbilities) {
|
||||||
if (ability.canActivate(playerId, game).canActivate()) {
|
if (ability.canActivate(playerId, game).canActivate()) {
|
||||||
if (ability.getEffects().hasOutcome(Outcome.PutLandInPlay)) {
|
if (ability.getEffects().hasOutcome(ability, Outcome.PutLandInPlay)) {
|
||||||
if (this.activateAbility(ability, game)) {
|
if (this.activateAbility(ability, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ability.getEffects().hasOutcome(Outcome.PutCreatureInPlay)) {
|
if (ability.getEffects().hasOutcome(ability, Outcome.PutCreatureInPlay)) {
|
||||||
if (getOpponentBlockers(opponentId, game).size() <= 1) {
|
if (getOpponentBlockers(opponentId, game).size() <= 1) {
|
||||||
if (this.activateAbility(ability, game)) {
|
if (this.activateAbility(ability, game)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package mage.cards.k;
|
package mage.cards.k;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.TriggeredAbilityImpl;
|
import mage.abilities.TriggeredAbilityImpl;
|
||||||
|
@ -20,8 +18,9 @@ import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.common.TargetCardInHand;
|
import mage.target.common.TargetCardInHand;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Backfir3
|
* @author Backfir3
|
||||||
*/
|
*/
|
||||||
public final class KaaliaOfTheVast extends CardImpl {
|
public final class KaaliaOfTheVast extends CardImpl {
|
||||||
|
@ -73,9 +72,7 @@ class KaaliaOfTheVastAttacksAbility extends TriggeredAbilityImpl {
|
||||||
public boolean checkTrigger(GameEvent event, Game game) {
|
public boolean checkTrigger(GameEvent event, Game game) {
|
||||||
if (event.getSourceId().equals(this.getSourceId())) {
|
if (event.getSourceId().equals(this.getSourceId())) {
|
||||||
Player opponent = game.getPlayer(event.getTargetId());
|
Player opponent = game.getPlayer(event.getTargetId());
|
||||||
if (opponent != null) {
|
return opponent != null;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +120,7 @@ class KaaliaOfTheVastEffect extends OneShotEffect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TargetCardInHand target = new TargetCardInHand(filter);
|
TargetCardInHand target = new TargetCardInHand(filter);
|
||||||
if (target.canChoose(controller.getId(), game) && target.choose(getOutcome(), controller.getId(), source.getSourceId(), game)) {
|
if (target.canChoose(controller.getId(), game) && target.choose(outcome, controller.getId(), source.getSourceId(), game)) {
|
||||||
if (!target.getTargets().isEmpty()) {
|
if (!target.getTargets().isEmpty()) {
|
||||||
UUID cardId = target.getFirstTarget();
|
UUID cardId = target.getFirstTarget();
|
||||||
Card card = game.getCard(cardId);
|
Card card = game.getCard(cardId);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package mage.cards.p;
|
package mage.cards.p;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.AttacksTriggeredAbility;
|
import mage.abilities.common.AttacksTriggeredAbility;
|
||||||
|
@ -19,8 +18,9 @@ import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.common.TargetCardInHand;
|
import mage.target.common.TargetCardInHand;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Rafbill
|
* @author Rafbill
|
||||||
*/
|
*/
|
||||||
public final class PreeminentCaptain extends CardImpl {
|
public final class PreeminentCaptain extends CardImpl {
|
||||||
|
@ -71,7 +71,7 @@ class PreeminentCaptainEffect extends OneShotEffect {
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
TargetCardInHand target = new TargetCardInHand(filter);
|
TargetCardInHand target = new TargetCardInHand(filter);
|
||||||
if (controller != null && target.canChoose(controller.getId(), game)
|
if (controller != null && target.canChoose(controller.getId(), game)
|
||||||
&& target.choose(getOutcome(), controller.getId(), source.getSourceId(), game)) {
|
&& target.choose(outcome, controller.getId(), source.getSourceId(), game)) {
|
||||||
if (!target.getTargets().isEmpty()) {
|
if (!target.getTargets().isEmpty()) {
|
||||||
UUID cardId = target.getFirstTarget();
|
UUID cardId = target.getFirstTarget();
|
||||||
Card card = controller.getHand().get(cardId, game);
|
Card card = controller.getHand().get(cardId, game);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package mage.cards.r;
|
package mage.cards.r;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.DelayedTriggeredAbility;
|
import mage.abilities.DelayedTriggeredAbility;
|
||||||
import mage.abilities.Mode;
|
import mage.abilities.Mode;
|
||||||
|
@ -29,34 +27,34 @@ import mage.target.common.TargetCardInGraveyard;
|
||||||
import mage.target.common.TargetCardInYourGraveyard;
|
import mage.target.common.TargetCardInYourGraveyard;
|
||||||
import mage.target.common.TargetControlledCreaturePermanent;
|
import mage.target.common.TargetControlledCreaturePermanent;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Once you announce you're casting Rescue from the Underworld, no player may
|
* Once you announce you're casting Rescue from the Underworld, no player may
|
||||||
* attempt to stop you from casting the spell by removing the creature you want
|
* attempt to stop you from casting the spell by removing the creature you want
|
||||||
* to sacrifice.
|
* to sacrifice.
|
||||||
*
|
* <p>
|
||||||
* If you sacrifice a creature token to cast Rescue from the Underworld, it
|
* If you sacrifice a creature token to cast Rescue from the Underworld, it
|
||||||
* won't return to the battlefield, although the target creature card will.
|
* won't return to the battlefield, although the target creature card will.
|
||||||
*
|
* <p>
|
||||||
* If either the sacrificed creature or the target creature card leaves the
|
* If either the sacrificed creature or the target creature card leaves the
|
||||||
* graveyard before the delayed triggered ability resolves during your next
|
* graveyard before the delayed triggered ability resolves during your next
|
||||||
* upkeep, it won't return.
|
* upkeep, it won't return.
|
||||||
*
|
* <p>
|
||||||
* However, if the sacrificed creature is put into another public zone instead
|
* However, if the sacrificed creature is put into another public zone instead
|
||||||
* of the graveyard, perhaps because it's your commander or because of another
|
* of the graveyard, perhaps because it's your commander or because of another
|
||||||
* replacement effect, it will return to the battlefield from the zone it went
|
* replacement effect, it will return to the battlefield from the zone it went
|
||||||
* to.
|
* to.
|
||||||
*
|
* <p>
|
||||||
* Rescue from the Underworld is exiled as it resolves, not later as its delayed
|
* Rescue from the Underworld is exiled as it resolves, not later as its delayed
|
||||||
* trigger resolves.
|
* trigger resolves.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public final class RescueFromTheUnderworld extends CardImpl {
|
public final class RescueFromTheUnderworld extends CardImpl {
|
||||||
|
|
||||||
public RescueFromTheUnderworld(UUID ownerId, CardSetInfo setInfo) {
|
public RescueFromTheUnderworld(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{B}");
|
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{B}");
|
||||||
|
|
||||||
// As an additional cost to cast Rescue from the Underworld, sacrifice a creature.
|
// As an additional cost to cast Rescue from the Underworld, sacrifice a creature.
|
||||||
this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent("a creature"), false)));
|
this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, new FilterControlledCreaturePermanent("a creature"), false)));
|
||||||
|
@ -106,7 +104,7 @@ class RescueFromTheUnderworldCreateDelayedTriggeredAbilityEffect extends OneShot
|
||||||
protected DelayedTriggeredAbility ability;
|
protected DelayedTriggeredAbility ability;
|
||||||
|
|
||||||
public RescueFromTheUnderworldCreateDelayedTriggeredAbilityEffect(DelayedTriggeredAbility ability) {
|
public RescueFromTheUnderworldCreateDelayedTriggeredAbilityEffect(DelayedTriggeredAbility ability) {
|
||||||
super(ability.getEffects().get(0).getOutcome());
|
super(ability.getEffects().getOutcome(ability));
|
||||||
this.ability = ability;
|
this.ability = ability;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
package org.mage.test.cards.abilities;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.effects.common.DamageTargetEffect;
|
||||||
|
import mage.abilities.effects.common.ExileTargetEffect;
|
||||||
|
import mage.abilities.effects.common.GainLifeEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.BoostSourceEffect;
|
||||||
|
import mage.constants.Duration;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class OutcomesTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normal outcome from effects
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_FromEffects_Single() {
|
||||||
|
Ability abilityGood = new SimpleStaticAbility(new GainLifeEffect(10));
|
||||||
|
Assert.assertEquals(1, abilityGood.getEffects().getOutcomeScore(abilityGood));
|
||||||
|
|
||||||
|
Ability abilityBad = new SimpleStaticAbility(new DamageTargetEffect(10));
|
||||||
|
Assert.assertEquals(-1, abilityBad.getEffects().getOutcomeScore(abilityBad));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_FromEffects_Multi() {
|
||||||
|
Ability abilityGood = new SimpleStaticAbility(new GainLifeEffect(10));
|
||||||
|
abilityGood.addEffect(new BoostSourceEffect(10, 10, Duration.EndOfTurn));
|
||||||
|
Assert.assertEquals(1 + 1, abilityGood.getEffects().getOutcomeScore(abilityGood));
|
||||||
|
|
||||||
|
Ability abilityBad = new SimpleStaticAbility(new DamageTargetEffect(10));
|
||||||
|
abilityBad.addEffect(new ExileTargetEffect());
|
||||||
|
Assert.assertEquals(-1 + -1, abilityBad.getEffects().getOutcomeScore(abilityBad));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_FromEffects_MultiCombine() {
|
||||||
|
Ability ability = new SimpleStaticAbility(new GainLifeEffect(10));
|
||||||
|
ability.addEffect(new BoostSourceEffect(10, 10, Duration.EndOfTurn));
|
||||||
|
ability.addEffect(new ExileTargetEffect());
|
||||||
|
Assert.assertEquals(1 + 1 + -1, ability.getEffects().getOutcomeScore(ability));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_FromEffects_Default() {
|
||||||
|
Ability ability = new LeavesBattlefieldTriggeredAbility(null, false);
|
||||||
|
Assert.assertEquals(0, ability.getEffects().getOutcomeScore(ability));
|
||||||
|
Assert.assertEquals(Outcome.Detriment, ability.getEffects().getOutcome(ability));
|
||||||
|
Assert.assertEquals(Outcome.BoostCreature, ability.getEffects().getOutcome(ability, Outcome.BoostCreature));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special outcome from ability (AI activates only good abilities)
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_FromAbility_Single() {
|
||||||
|
Ability abilityGood = new SimpleStaticAbility(new GainLifeEffect(10));
|
||||||
|
abilityGood.addCustomOutcome(Outcome.Detriment);
|
||||||
|
Assert.assertEquals(-1, abilityGood.getEffects().getOutcomeScore(abilityGood));
|
||||||
|
Assert.assertEquals(Outcome.Detriment, abilityGood.getEffects().getOutcome(abilityGood));
|
||||||
|
|
||||||
|
Ability abilityBad = new SimpleStaticAbility(new DamageTargetEffect(10));
|
||||||
|
abilityBad.addCustomOutcome(Outcome.Neutral);
|
||||||
|
Assert.assertEquals(1, abilityBad.getEffects().getOutcomeScore(abilityBad));
|
||||||
|
Assert.assertEquals(Outcome.Neutral, abilityBad.getEffects().getOutcome(abilityBad));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_FromAbility_Multi() {
|
||||||
|
Ability abilityGood = new SimpleStaticAbility(new GainLifeEffect(10));
|
||||||
|
abilityGood.addEffect(new BoostSourceEffect(10, 10, Duration.EndOfTurn));
|
||||||
|
abilityGood.addCustomOutcome(Outcome.Detriment);
|
||||||
|
Assert.assertEquals(-1 + -1, abilityGood.getEffects().getOutcomeScore(abilityGood));
|
||||||
|
|
||||||
|
Ability abilityBad = new SimpleStaticAbility(new DamageTargetEffect(10));
|
||||||
|
abilityBad.addEffect(new ExileTargetEffect());
|
||||||
|
abilityBad.addCustomOutcome(Outcome.Neutral);
|
||||||
|
Assert.assertEquals(1 + 1, abilityBad.getEffects().getOutcomeScore(abilityBad));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_FromAbility_MultiCombine() {
|
||||||
|
Ability ability = new SimpleStaticAbility(new GainLifeEffect(10));
|
||||||
|
ability.addEffect(new BoostSourceEffect(10, 10, Duration.EndOfTurn));
|
||||||
|
ability.addEffect(new ExileTargetEffect());
|
||||||
|
ability.addCustomOutcome(Outcome.Neutral); // must "convert" all effects to good
|
||||||
|
Assert.assertEquals(1 + 1 + 1, ability.getEffects().getOutcomeScore(ability));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,5 @@
|
||||||
|
|
||||||
package mage.abilities;
|
package mage.abilities;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import mage.abilities.common.ZoneChangeTriggeredAbility;
|
import mage.abilities.common.ZoneChangeTriggeredAbility;
|
||||||
import mage.abilities.costs.Cost;
|
import mage.abilities.costs.Cost;
|
||||||
import mage.abilities.keyword.ProtectionAbility;
|
import mage.abilities.keyword.ProtectionAbility;
|
||||||
|
@ -13,6 +10,9 @@ import mage.game.Game;
|
||||||
import mage.util.ThreadLocalStringBuilder;
|
import mage.util.ThreadLocalStringBuilder;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param <T>
|
* @param <T>
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
|
@ -220,7 +220,7 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(T ability) {
|
public boolean contains(T ability) {
|
||||||
for (Iterator<T> iterator = this.iterator(); iterator.hasNext();) { // simple loop can cause java.util.ConcurrentModificationException
|
for (Iterator<T> iterator = this.iterator(); iterator.hasNext(); ) { // simple loop can cause java.util.ConcurrentModificationException
|
||||||
T test = iterator.next();
|
T test = iterator.next();
|
||||||
// Checking also by getRule() without other restrictions is a problem when a triggered ability will be copied to a permanent that had the same ability
|
// Checking also by getRule() without other restrictions is a problem when a triggered ability will be copied to a permanent that had the same ability
|
||||||
// already before the copy. Because then it keeps the triggered ability twice and it triggers twice.
|
// already before the copy. Because then it keeps the triggered ability twice and it triggers twice.
|
||||||
|
@ -273,7 +273,7 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getOutcomeTotal() {
|
public int getOutcomeTotal() {
|
||||||
return stream().mapToInt(ability -> ability.getEffects().getOutcomeTotal()).sum();
|
return stream().mapToInt(ability -> ability.getEffects().getOutcomeScore(ability)).sum();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
package mage.abilities;
|
package mage.abilities;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.costs.Cost;
|
import mage.abilities.costs.Cost;
|
||||||
import mage.abilities.costs.CostAdjuster;
|
import mage.abilities.costs.CostAdjuster;
|
||||||
|
@ -12,10 +9,7 @@ import mage.abilities.costs.mana.ManaCosts;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
import mage.abilities.effects.Effects;
|
import mage.abilities.effects.Effects;
|
||||||
import mage.abilities.hint.Hint;
|
import mage.abilities.hint.Hint;
|
||||||
import mage.constants.AbilityType;
|
import mage.constants.*;
|
||||||
import mage.constants.AbilityWord;
|
|
||||||
import mage.constants.EffectType;
|
|
||||||
import mage.constants.Zone;
|
|
||||||
import mage.game.Controllable;
|
import mage.game.Controllable;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
|
@ -26,6 +20,10 @@ import mage.target.Targets;
|
||||||
import mage.target.targetadjustment.TargetAdjuster;
|
import mage.target.targetadjustment.TargetAdjuster;
|
||||||
import mage.watchers.Watcher;
|
import mage.watchers.Watcher;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Practically everything in the game is started from an Ability. This interface
|
* Practically everything in the game is started from an Ability. This interface
|
||||||
* describes what an Ability is composed of at the highest level.
|
* describes what an Ability is composed of at the highest level.
|
||||||
|
@ -46,10 +44,8 @@ public interface Ability extends Controllable, Serializable {
|
||||||
*
|
*
|
||||||
* @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility,
|
* @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility,
|
||||||
* mage.game.Game)
|
* mage.game.Game)
|
||||||
* @see
|
* @see mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility)
|
||||||
* mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility)
|
* @see mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility)
|
||||||
* @see
|
|
||||||
* mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility)
|
|
||||||
*/
|
*/
|
||||||
void newId();
|
void newId();
|
||||||
|
|
||||||
|
@ -58,10 +54,8 @@ public interface Ability extends Controllable, Serializable {
|
||||||
*
|
*
|
||||||
* @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility,
|
* @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility,
|
||||||
* mage.game.Game)
|
* mage.game.Game)
|
||||||
* @see
|
* @see mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility)
|
||||||
* mage.game.GameImpl#addTriggeredAbility(mage.abilities.TriggeredAbility)
|
* @see mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility)
|
||||||
* @see
|
|
||||||
* mage.game.GameImpl#addDelayedTriggeredAbility(mage.abilities.DelayedTriggeredAbility)
|
|
||||||
*/
|
*/
|
||||||
void newOriginalId();
|
void newOriginalId();
|
||||||
|
|
||||||
|
@ -267,16 +261,15 @@ public interface Ability extends Controllable, Serializable {
|
||||||
/**
|
/**
|
||||||
* Activates this ability prompting the controller to pay any mandatory
|
* Activates this ability prompting the controller to pay any mandatory
|
||||||
*
|
*
|
||||||
* @param game A reference the {@link Game} for which this ability should be
|
* @param game A reference the {@link Game} for which this ability should be
|
||||||
* activated within.
|
* activated within.
|
||||||
* @param noMana Whether or not {@link ManaCosts} have to be paid.
|
* @param noMana Whether or not {@link ManaCosts} have to be paid.
|
||||||
* @return True if this ability was successfully activated.
|
* @return True if this ability was successfully activated.
|
||||||
* @see mage.players.PlayerImpl#cast(mage.abilities.SpellAbility,
|
* @see mage.players.PlayerImpl#cast(mage.abilities.SpellAbility,
|
||||||
* mage.game.Game, boolean)
|
* mage.game.Game, boolean)
|
||||||
* @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility,
|
* @see mage.players.PlayerImpl#playAbility(mage.abilities.ActivatedAbility,
|
||||||
* mage.game.Game)
|
* mage.game.Game)
|
||||||
* @see
|
* @see mage.players.PlayerImpl#triggerAbility(mage.abilities.TriggeredAbility,
|
||||||
* mage.players.PlayerImpl#triggerAbility(mage.abilities.TriggeredAbility,
|
|
||||||
* mage.game.Game)
|
* mage.game.Game)
|
||||||
*/
|
*/
|
||||||
boolean activate(Game game, boolean noMana);
|
boolean activate(Game game, boolean noMana);
|
||||||
|
@ -290,8 +283,7 @@ public interface Ability extends Controllable, Serializable {
|
||||||
*
|
*
|
||||||
* @param game The {@link Game} for which this ability resolves within.
|
* @param game The {@link Game} for which this ability resolves within.
|
||||||
* @return Whether or not this ability successfully resolved.
|
* @return Whether or not this ability successfully resolved.
|
||||||
* @see
|
* @see mage.players.PlayerImpl#playManaAbility(mage.abilities.mana.ManaAbility,
|
||||||
* mage.players.PlayerImpl#playManaAbility(mage.abilities.mana.ManaAbility,
|
|
||||||
* mage.game.Game)
|
* mage.game.Game)
|
||||||
* @see mage.players.PlayerImpl#specialAction(mage.abilities.SpecialAction,
|
* @see mage.players.PlayerImpl#specialAction(mage.abilities.SpecialAction,
|
||||||
* mage.game.Game)
|
* mage.game.Game)
|
||||||
|
@ -526,4 +518,8 @@ public interface Ability extends Controllable, Serializable {
|
||||||
List<Hint> getHints();
|
List<Hint> getHints();
|
||||||
|
|
||||||
Ability addHint(Hint hint);
|
Ability addHint(Hint hint);
|
||||||
|
|
||||||
|
Ability addCustomOutcome(Outcome customOutcome);
|
||||||
|
|
||||||
|
Outcome getCustomOutcome();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
package mage.abilities;
|
package mage.abilities;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.costs.*;
|
import mage.abilities.costs.*;
|
||||||
import mage.abilities.costs.common.PayLifeCost;
|
import mage.abilities.costs.common.PayLifeCost;
|
||||||
|
@ -33,6 +29,11 @@ import mage.util.ThreadLocalStringBuilder;
|
||||||
import mage.watchers.Watcher;
|
import mage.watchers.Watcher;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
|
@ -68,6 +69,7 @@ public abstract class AbilityImpl implements Ability {
|
||||||
protected TargetAdjuster targetAdjuster = null;
|
protected TargetAdjuster targetAdjuster = null;
|
||||||
protected CostAdjuster costAdjuster = null;
|
protected CostAdjuster costAdjuster = null;
|
||||||
protected List<Hint> hints = new ArrayList<>();
|
protected List<Hint> hints = new ArrayList<>();
|
||||||
|
protected Outcome customOutcome = null; // uses for AI decisions instead effects
|
||||||
|
|
||||||
public AbilityImpl(AbilityType abilityType, Zone zone) {
|
public AbilityImpl(AbilityType abilityType, Zone zone) {
|
||||||
this.id = UUID.randomUUID();
|
this.id = UUID.randomUUID();
|
||||||
|
@ -117,6 +119,7 @@ public abstract class AbilityImpl implements Ability {
|
||||||
for (Hint hint : ability.getHints()) {
|
for (Hint hint : ability.getHints()) {
|
||||||
this.hints.add(hint.copy());
|
this.hints.add(hint.copy());
|
||||||
}
|
}
|
||||||
|
this.customOutcome = ability.customOutcome;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -321,7 +324,7 @@ public abstract class AbilityImpl implements Ability {
|
||||||
sourceObject.adjustTargets(this, game);
|
sourceObject.adjustTargets(this, game);
|
||||||
}
|
}
|
||||||
if (!getTargets().isEmpty()) {
|
if (!getTargets().isEmpty()) {
|
||||||
Outcome outcome = getEffects().isEmpty() ? Outcome.Detriment : getEffects().get(0).getOutcome();
|
Outcome outcome = getEffects().getOutcome(this);
|
||||||
// only activated abilities can be canceled by user (not triggered)
|
// only activated abilities can be canceled by user (not triggered)
|
||||||
if (!getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game, this instanceof ActivatedAbility)) {
|
if (!getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game, this instanceof ActivatedAbility)) {
|
||||||
// was canceled during targer selection
|
// was canceled during targer selection
|
||||||
|
@ -948,10 +951,7 @@ public abstract class AbilityImpl implements Ability {
|
||||||
}
|
}
|
||||||
return ((Permanent) object).isPhasedIn();
|
return ((Permanent) object).isPhasedIn();
|
||||||
} else if (object instanceof Card) {
|
} else if (object instanceof Card) {
|
||||||
if (!((Card) object).getAbilities(game).contains(this)) {
|
return ((Card) object).getAbilities(game).contains(this);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else if (!object.getAbilities().contains(this)) { // not sure which object it can still be
|
} else if (!object.getAbilities().contains(this)) { // not sure which object it can still be
|
||||||
// check if it's an ability that is temporary gained to a card
|
// check if it's an ability that is temporary gained to a card
|
||||||
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(this.getSourceId());
|
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(this.getSourceId());
|
||||||
|
@ -1250,4 +1250,15 @@ public abstract class AbilityImpl implements Ability {
|
||||||
this.hints.add(hint);
|
this.hints.add(hint);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ability addCustomOutcome(Outcome customOutcome) {
|
||||||
|
this.customOutcome = customOutcome;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Outcome getCustomOutcome() {
|
||||||
|
return this.customOutcome;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package mage.abilities;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
import mage.constants.AbilityType;
|
import mage.constants.AbilityType;
|
||||||
import mage.constants.Outcome;
|
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
|
@ -61,7 +60,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
|
||||||
MageObject object = game.getObject(getSourceId());
|
MageObject object = game.getObject(getSourceId());
|
||||||
Player player = game.getPlayer(this.getControllerId());
|
Player player = game.getPlayer(this.getControllerId());
|
||||||
if (player != null && object != null) {
|
if (player != null && object != null) {
|
||||||
if (!player.chooseUse(getEffects().isEmpty() ? Outcome.Detriment : getEffects().get(0).getOutcome(), this.getRule(object.getLogName()), this, game)) {
|
if (!player.chooseUse(getEffects().getOutcome(this), this.getRule(object.getLogName()), this, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
package mage.abilities.effects;
|
package mage.abilities.effects;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.Mode;
|
import mage.abilities.Mode;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.target.targetpointer.TargetPointer;
|
import mage.target.targetpointer.TargetPointer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
|
@ -99,7 +97,11 @@ public class Effects extends ArrayList<Effect> {
|
||||||
return sbText.toString();
|
return sbText.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasOutcome(Outcome outcome) {
|
public boolean hasOutcome(Ability source, Outcome outcome) {
|
||||||
|
Outcome realOutcome = (source == null ? null : source.getCustomOutcome());
|
||||||
|
if (realOutcome != null) {
|
||||||
|
return realOutcome == outcome;
|
||||||
|
}
|
||||||
for (Effect effect : this) {
|
for (Effect effect : this) {
|
||||||
if (effect.getOutcome() == outcome) {
|
if (effect.getOutcome() == outcome) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -108,18 +110,40 @@ public class Effects extends ArrayList<Effect> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Outcome> getOutcomes() {
|
/**
|
||||||
Set<Outcome> outcomes = new HashSet<>();
|
* @param source source ability for effects
|
||||||
for (Effect effect : this) {
|
* @return real outcome of ability
|
||||||
outcomes.add(effect.getOutcome());
|
*/
|
||||||
}
|
public Outcome getOutcome(Ability source) {
|
||||||
return new ArrayList<>(outcomes);
|
return getOutcome(source, Outcome.Detriment);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOutcomeTotal() {
|
public Outcome getOutcome(Ability source, Outcome defaultOutcome) {
|
||||||
|
Outcome realOutcome = (source == null ? null : source.getCustomOutcome());
|
||||||
|
if (realOutcome != null) {
|
||||||
|
return realOutcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isEmpty()) {
|
||||||
|
return this.get(0).getOutcome();
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultOutcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param source source ability for effects
|
||||||
|
* @return total score of outcome effects (plus/minus)
|
||||||
|
*/
|
||||||
|
public int getOutcomeScore(Ability source) {
|
||||||
int total = 0;
|
int total = 0;
|
||||||
for (Effect effect : this) {
|
for (Effect effect : this) {
|
||||||
if (effect.getOutcome().isGood()) {
|
// custom ability outcome must "rewrite" effect's outcome (it uses for AI desisions and card score... hmm, getOutcomeTotal used on 28.01.2020)
|
||||||
|
Outcome realOutcome = (source == null ? null : source.getCustomOutcome());
|
||||||
|
if (realOutcome == null) {
|
||||||
|
realOutcome = effect.getOutcome();
|
||||||
|
}
|
||||||
|
if (realOutcome.isGood()) {
|
||||||
total++;
|
total++;
|
||||||
} else {
|
} else {
|
||||||
total--;
|
total--;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.abilities.effects.common;
|
package mage.abilities.effects.common;
|
||||||
|
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
|
@ -103,11 +102,11 @@ public class CopyPermanentEffect extends OneShotEffect {
|
||||||
// attach - search effect in spell ability (example: cast Utopia Sprawl, cast Estrid's Invocation on it)
|
// attach - search effect in spell ability (example: cast Utopia Sprawl, cast Estrid's Invocation on it)
|
||||||
for (Ability ability : bluePrintPermanent.getAbilities()) {
|
for (Ability ability : bluePrintPermanent.getAbilities()) {
|
||||||
if (ability instanceof SpellAbility) {
|
if (ability instanceof SpellAbility) {
|
||||||
|
auraOutcome = ability.getEffects().getOutcome(ability);
|
||||||
for (Effect effect : ability.getEffects()) {
|
for (Effect effect : ability.getEffects()) {
|
||||||
if (effect instanceof AttachEffect) {
|
if (effect instanceof AttachEffect) {
|
||||||
if (bluePrintPermanent.getSpellAbility().getTargets().size() > 0) {
|
if (bluePrintPermanent.getSpellAbility().getTargets().size() > 0) {
|
||||||
auraTarget = bluePrintPermanent.getSpellAbility().getTargets().get(0);
|
auraTarget = bluePrintPermanent.getSpellAbility().getTargets().get(0);
|
||||||
auraOutcome = effect.getOutcome();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,12 +117,9 @@ public class CopyPermanentEffect extends OneShotEffect {
|
||||||
if (auraTarget == null) {
|
if (auraTarget == null) {
|
||||||
for (Ability ability : bluePrintPermanent.getAbilities()) {
|
for (Ability ability : bluePrintPermanent.getAbilities()) {
|
||||||
if (ability instanceof EnchantAbility) {
|
if (ability instanceof EnchantAbility) {
|
||||||
|
auraOutcome = ability.getEffects().getOutcome(ability);
|
||||||
if (ability.getTargets().size() > 0) { // Animate Dead don't have targets
|
if (ability.getTargets().size() > 0) { // Animate Dead don't have targets
|
||||||
auraTarget = ability.getTargets().get(0);
|
auraTarget = ability.getTargets().get(0);
|
||||||
for (Effect effect : ability.getEffects()) {
|
|
||||||
// first outcome
|
|
||||||
auraOutcome = effect.getOutcome();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package mage.abilities.effects.common;
|
package mage.abilities.effects.common;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
@ -6,11 +5,9 @@ import mage.abilities.DelayedTriggeredAbility;
|
||||||
import mage.abilities.Mode;
|
import mage.abilities.Mode;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.constants.Outcome;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect {
|
public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect {
|
||||||
|
@ -28,7 +25,7 @@ public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
|
|
||||||
public CreateDelayedTriggeredAbilityEffect(DelayedTriggeredAbility ability, boolean copyTargets, boolean initAbility) {
|
public CreateDelayedTriggeredAbilityEffect(DelayedTriggeredAbility ability, boolean copyTargets, boolean initAbility) {
|
||||||
super(ability.getEffects().isEmpty() ? Outcome.Detriment : ability.getEffects().get(0).getOutcome());
|
super(ability.getEffects().getOutcome(ability));
|
||||||
this.ability = ability;
|
this.ability = ability;
|
||||||
this.copyTargets = copyTargets;
|
this.copyTargets = copyTargets;
|
||||||
this.initAbility = initAbility;
|
this.initAbility = initAbility;
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
|
|
||||||
package mage.abilities.effects.common;
|
package mage.abilities.effects.common;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.Mode;
|
import mage.abilities.Mode;
|
||||||
import mage.abilities.SpecialAction;
|
import mage.abilities.SpecialAction;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.constants.Outcome;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
public class CreateSpecialActionEffect extends OneShotEffect {
|
public class CreateSpecialActionEffect extends OneShotEffect {
|
||||||
|
@ -17,7 +14,7 @@ public class CreateSpecialActionEffect extends OneShotEffect {
|
||||||
protected SpecialAction action;
|
protected SpecialAction action;
|
||||||
|
|
||||||
public CreateSpecialActionEffect(SpecialAction action) {
|
public CreateSpecialActionEffect(SpecialAction action) {
|
||||||
super(action.getEffects().isEmpty() ? Outcome.Detriment : action.getEffects().get(0).getOutcome());
|
super(action.getEffects().getOutcome(action));
|
||||||
this.action = action;
|
this.action = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ public class DoIfCostPaid extends OneShotEffect {
|
||||||
}
|
}
|
||||||
message = CardUtil.replaceSourceName(message, mageObject.getLogName());
|
message = CardUtil.replaceSourceName(message, mageObject.getLogName());
|
||||||
boolean result = true;
|
boolean result = true;
|
||||||
Outcome payOutcome = executingEffects.size() > 0 ? executingEffects.get(0).getOutcome() : this.outcome;
|
Outcome payOutcome = executingEffects.getOutcome(source, this.outcome);
|
||||||
if (cost.canPay(source, source.getSourceId(), player.getId(), game)
|
if (cost.canPay(source, source.getSourceId(), player.getId(), game)
|
||||||
&& (!optional || player.chooseUse(payOutcome, message, source, game))) {
|
&& (!optional || player.chooseUse(payOutcome, message, source, game))) {
|
||||||
cost.clearPaid();
|
cost.clearPaid();
|
||||||
|
|
|
@ -39,8 +39,7 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
public GainAbilityTargetEffect(Ability ability, Duration duration, String rule, boolean onCard, Layer layer, SubLayer subLayer) {
|
public GainAbilityTargetEffect(Ability ability, Duration duration, String rule, boolean onCard, Layer layer, SubLayer subLayer) {
|
||||||
super(duration, layer, subLayer,
|
super(duration, layer, subLayer, ability.getEffects().getOutcome(ability, Outcome.AddAbility));
|
||||||
!ability.getEffects().isEmpty() ? ability.getEffects().get(0).getOutcome() : Outcome.AddAbility);
|
|
||||||
this.ability = ability;
|
this.ability = ability;
|
||||||
staticText = rule;
|
staticText = rule;
|
||||||
this.onCard = onCard;
|
this.onCard = onCard;
|
||||||
|
|
|
@ -132,6 +132,7 @@ public final class RateCard {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int isEffectRemoval(Card card, Ability ability, Effect effect) {
|
private static int isEffectRemoval(Card card, Ability ability, Effect effect) {
|
||||||
|
// it's effect relates score, do not use custom outcome from ability
|
||||||
if (effect.getOutcome() == Outcome.Removal) {
|
if (effect.getOutcome() == Outcome.Removal) {
|
||||||
// found removal
|
// found removal
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
package mage.game.stack;
|
package mage.game.stack;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.ObjectColor;
|
import mage.ObjectColor;
|
||||||
|
@ -34,6 +30,11 @@ import mage.util.GameLog;
|
||||||
import mage.util.SubTypeList;
|
import mage.util.SubTypeList;
|
||||||
import mage.watchers.Watcher;
|
import mage.watchers.Watcher;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* @author BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
|
@ -578,7 +579,7 @@ public class StackAbility extends StackObjImpl implements Ability {
|
||||||
game.getStack().push(newStackAbility);
|
game.getStack().push(newStackAbility);
|
||||||
if (chooseNewTargets && !newAbility.getTargets().isEmpty()) {
|
if (chooseNewTargets && !newAbility.getTargets().isEmpty()) {
|
||||||
Player controller = game.getPlayer(newControllerId);
|
Player controller = game.getPlayer(newControllerId);
|
||||||
Outcome outcome = newAbility.getEffects().isEmpty() ? Outcome.Detriment : newAbility.getEffects().get(0).getOutcome();
|
Outcome outcome = newAbility.getEffects().getOutcome(newAbility);
|
||||||
if (controller.chooseUse(outcome, "Choose new targets?", source, game)) {
|
if (controller.chooseUse(outcome, "Choose new targets?", source, game)) {
|
||||||
newAbility.getTargets().clearChosen();
|
newAbility.getTargets().clearChosen();
|
||||||
newAbility.getTargets().chooseTargets(outcome, newControllerId, newAbility, false, game, false);
|
newAbility.getTargets().chooseTargets(outcome, newControllerId, newAbility, false, game, false);
|
||||||
|
@ -648,7 +649,16 @@ public class StackAbility extends StackObjImpl implements Ability {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Ability addHint(Hint hint) {
|
public Ability addHint(Hint hint) {
|
||||||
// only abilities supports addhint
|
throw new IllegalArgumentException("Stack ability is not supports hint adding");
|
||||||
return null;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Ability addCustomOutcome(Outcome customOutcome) {
|
||||||
|
throw new IllegalArgumentException("Stack ability is not supports custom outcome adding");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Outcome getCustomOutcome() {
|
||||||
|
return this.ability.getCustomOutcome();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,7 +163,7 @@ public abstract class StackObjImpl implements StackObject {
|
||||||
targetAmount = " (amount: " + target.getTargetAmount(targetId) + ")";
|
targetAmount = " (amount: " + target.getTargetAmount(targetId) + ")";
|
||||||
}
|
}
|
||||||
// change the target?
|
// change the target?
|
||||||
Outcome outcome = mode.getEffects().isEmpty() ? Outcome.Detriment : mode.getEffects().get(0).getOutcome();
|
Outcome outcome = mode.getEffects().getOutcome(ability);
|
||||||
|
|
||||||
if (targetNames != null
|
if (targetNames != null
|
||||||
&& (forceChange || targetController.chooseUse(outcome, "Change this target: " + targetNames + targetAmount + '?', ability, game))) {
|
&& (forceChange || targetController.chooseUse(outcome, "Change this target: " + targetNames + targetAmount + '?', ability, game))) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue