Merge origin/master

This commit is contained in:
LevelX2 2015-04-20 10:57:39 +02:00
commit 89ce87e16e
57 changed files with 2814 additions and 1336 deletions

View file

@ -215,14 +215,20 @@ public abstract class AbilityImpl implements Ability {
else {
game.addEffect((ContinuousEffect) effect, this);
}
/**
* All restrained trigger events are fired now.
* To restrain the events is mainly neccessary because of the movement of multiple object at once.
* If the event is fired directly as one object moved, other objects are not already in the correct zone
* to check for their effects. (e.g. Valakut, the Molten Pinnacle)
*/
game.getState().handleSimultaneousEvent(game);
game.resetShortLivingLKI();
/**
* game.applyEffects() has to be done at least for every effect that moves cards/permanent between zones,
* so Static effects work as intened if dependant from the moved objects zone it is in
* Otherwise for example were static abilities with replacement effects deactivated to late
* Example: {@link org.mage.test.cards.replacement.DryadMilitantTest#testDiesByDestroy testDiesByDestroy}
*/
// game.applyEffects();
// some effects must be applied before next effect is resolved, because effect is dependend.
if (effect.applyEffectsAfter()) {
game.applyEffects();
}

View file

@ -4,10 +4,10 @@
*/
package mage.abilities.common;
import mage.constants.CardType;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
@ -18,12 +18,19 @@ import mage.game.permanent.Permanent;
*/
public class BecomesTappedCreatureControlledTriggeredAbility extends TriggeredAbilityImpl{
FilterControlledCreaturePermanent filter;
public BecomesTappedCreatureControlledTriggeredAbility(Effect effect, boolean optional) {
this(effect, optional, new FilterControlledCreaturePermanent("a creature you control"));
}
public BecomesTappedCreatureControlledTriggeredAbility(Effect effect, boolean optional, FilterControlledCreaturePermanent filter) {
super(Zone.BATTLEFIELD, effect, optional);
this.filter = filter;
}
public BecomesTappedCreatureControlledTriggeredAbility(final BecomesTappedCreatureControlledTriggeredAbility ability) {
super(ability);
this.filter = ability.filter;
}
@Override
@ -39,15 +46,11 @@ public class BecomesTappedCreatureControlledTriggeredAbility extends TriggeredAb
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null && permanent.getControllerId().equals(this.controllerId)
&& permanent.getCardType().contains(CardType.CREATURE)) {
return true;
}
return false;
return permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game);
}
@Override
public String getRule() {
return "When a creature you control becomes tapped, " + super.getRule();
return "When " + filter.getMessage() + " becomes tapped, " + super.getRule();
}
}

View file

@ -32,8 +32,10 @@ import mage.constants.Outcome;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.OneShotEffect;
import mage.filter.Filter;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
/**
* @author BetaSteward_at_googlemail.com
@ -43,21 +45,27 @@ public class ChooseNewTargetsTargetEffect extends OneShotEffect {
private boolean forceChange;
private boolean onlyOneTarget;
private FilterPermanent filterNewTarget;
public ChooseNewTargetsTargetEffect() {
this(false, false);
}
public ChooseNewTargetsTargetEffect(boolean forceChange, boolean onlyOneTarget) {
this(forceChange, onlyOneTarget, null);
}
/**
*
* @param forceChange forces the user to choose another target (only targets with maxtargets = 1 supported)
* @param onlyOneTarget only one target can be selected for the change
* @param filterNewTarget restriction to the new target
*/
public ChooseNewTargetsTargetEffect(boolean forceChange, boolean onlyOneTarget) {
public ChooseNewTargetsTargetEffect(boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) {
super(Outcome.Benefit);
this.forceChange = forceChange;
this.onlyOneTarget = onlyOneTarget;
this.filterNewTarget = filterNewTarget;
}
public ChooseNewTargetsTargetEffect(final ChooseNewTargetsTargetEffect effect) {
@ -68,9 +76,9 @@ public class ChooseNewTargetsTargetEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(source.getFirstTarget());
if (spell != null) {
return spell.chooseNewTargets(game, source.getControllerId(), forceChange, onlyOneTarget);
StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget());
if (stackObject != null) {
return stackObject.chooseNewTargets(game, source.getControllerId(), forceChange, onlyOneTarget, filterNewTarget);
}
return false;
}
@ -82,6 +90,9 @@ public class ChooseNewTargetsTargetEffect extends OneShotEffect {
@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
StringBuilder sb = new StringBuilder();
if (forceChange) {
sb.append("change the target of target ");

View file

@ -95,9 +95,10 @@ public class AddCountersTargetEffect extends OneShotEffect {
permanent.addCounters(newCounter, game);
int numberAdded = permanent.getCounters().getCount(counter.getName()) - before;
affectedTargets ++;
if (!game.isSimulation())
if (!game.isSimulation()) {
game.informPlayers(sourceObject.getLogName() +": "+ controller.getName()+ " puts " +
numberAdded + " " + counter.getName().toLowerCase() + " counter on " + permanent.getLogName());
}
}
} else {
Player player = game.getPlayer(uuid);

View file

@ -31,7 +31,7 @@ public class ShadowAbility extends EvasionAbility implements MageSingleton {
@Override
public String getRule() {
return "Shadow";
return "Shadow <i>(This creature can block or be blocked by only creatures with shadow.)</i>";
}
@Override
@ -53,10 +53,7 @@ class ShadowEffect extends RestrictionEffect implements MageSingleton {
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
if (permanent.getAbilities().containsKey(ShadowAbility.getInstance().getId())) {
return true;
}
return false;
return permanent.getAbilities().containsKey(ShadowAbility.getInstance().getId());
}
@Override
@ -66,11 +63,8 @@ class ShadowEffect extends RestrictionEffect implements MageSingleton {
@Override
public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) {
if (blocker.getAbilities().containsKey(ShadowAbility.getInstance().getId())
|| game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_SHADOW, source, blocker.getControllerId(), game)) {
return true;
}
return false;
return blocker.getAbilities().containsKey(ShadowAbility.getInstance().getId())
|| game.getContinuousEffects().asThough(blocker.getId(), AsThoughEffectType.BLOCK_SHADOW, source, blocker.getControllerId(), game);
}
@Override

File diff suppressed because it is too large Load diff

View file

@ -60,7 +60,7 @@ public enum CardRepository {
// raise this if db structure was changed
private static final long CARD_DB_VERSION = 37;
// raise this if new cards were added to the server
private static final long CARD_CONTENT_VERSION = 12;
private static final long CARD_CONTENT_VERSION = 13;
private final Random random = new Random();
private Dao<CardInfo, Object> cardDao;

View file

@ -177,7 +177,16 @@ public class GameEvent {
ADD_COUNTER, COUNTER_ADDED,
ADD_COUNTERS, COUNTERS_ADDED,
COUNTER_REMOVED,
LOSE_CONTROL, LOST_CONTROL,
LOSE_CONTROL,
/* LOST_CONTROL
targetId id of the creature that lost control
sourceId id of the creature that lost control
playerId player that controlles the creature before
amount not used for this event
flag not used for this event
*/
LOST_CONTROL,
GAIN_CONTROL, GAINED_CONTROL,
CREATE_TOKEN,

View file

@ -108,7 +108,7 @@ public class PermanentToken extends PermanentImpl {
if (!game.replaceEvent(new ZoneChangeEvent(this, sourceId, this.getControllerId(), Zone.BATTLEFIELD, Zone.EXILED))) {
game.rememberLKI(objectId, Zone.BATTLEFIELD, this);
if (game.getPlayer(controllerId).removeFromBattlefield(this, game)) {
game.fireEvent(new ZoneChangeEvent(this, sourceId, this.getControllerId(), Zone.BATTLEFIELD, Zone.EXILED));
game.addSimultaneousEvent(new ZoneChangeEvent(this, sourceId, this.getControllerId(), Zone.BATTLEFIELD, Zone.EXILED));
return true;
}
}

View file

@ -55,6 +55,7 @@ import mage.constants.SpellAbilityType;
import mage.constants.Zone;
import mage.counters.Counter;
import mage.counters.Counters;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
@ -81,6 +82,7 @@ public class Spell implements StackObject, Card {
private UUID controllerId;
private boolean copiedSpell;
private boolean faceDown;
private boolean countered;
public Spell(Card card, SpellAbility ability, UUID controllerId, Zone fromZone) {
this.card = card;
@ -99,6 +101,7 @@ public class Spell implements StackObject, Card {
}
this.controllerId = controllerId;
this.fromZone = fromZone;
this.countered = false;
}
public Spell(final Spell spell) {
@ -198,8 +201,8 @@ public class Spell implements StackObject, Card {
result |= spellAbility.resolve(game);
}
}
game.getState().handleSimultaneousEvent(game);
game.resetShortLivingLKI();
// game.getState().handleSimultaneousEvent(game);
// game.resetShortLivingLKI();
index++;
}
}
@ -319,7 +322,7 @@ public class Spell implements StackObject, Card {
* @return
*/
public boolean chooseNewTargets(Game game, UUID playerId) {
return chooseNewTargets(game, playerId, false, false);
return chooseNewTargets(game, playerId, false, false, null);
}
/**
@ -377,13 +380,13 @@ public class Spell implements StackObject, Card {
*
* @param game
* @param playerId - player that can/has to change the taregt of the spell
* @param forceChange - does only work for targets with maximum of one
* targetId
* @param onlyOneTarget - 114.6b one target must be changed to another
* target
* @param forceChange - does only work for targets with maximum of one targetId
* @param onlyOneTarget - 114.6b one target must be changed to another target
* @param filterNewTarget restriction for the new target, if null nothing is cheched
* @return
*/
public boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget) {
@Override
public boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) {
Player player = game.getPlayer(playerId);
if (player != null) {
StringBuilder newTargetDescription = new StringBuilder();
@ -393,7 +396,7 @@ public class Spell implements StackObject, Card {
for (UUID modeId : spellAbility.getModes().getSelectedModes()) {
Mode mode = spellAbility.getModes().get(modeId);
for (Target target : mode.getTargets()) {
Target newTarget = chooseNewTarget(player, spellAbility, mode, target, forceChange, game);
Target newTarget = chooseNewTarget(player, spellAbility, mode, target, forceChange, filterNewTarget, game);
// clear the old target and copy all targets from new target
target.clearChosen();
for (UUID targetId : newTarget.getTargets()) {
@ -424,7 +427,7 @@ public class Spell implements StackObject, Card {
* @param game
* @return
*/
private Target chooseNewTarget(Player player, SpellAbility spellAbility, Mode mode, Target target, boolean forceChange, Game game) {
private Target chooseNewTarget(Player player, SpellAbility spellAbility, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) {
Target newTarget = target.copy();
newTarget.clearChosen();
for (UUID targetId : target.getTargets()) {
@ -443,6 +446,14 @@ public class Spell implements StackObject, Card {
newTarget.clearChosen();
// TODO: Distinction between "spell controller" and "player that can change the target" - here player is used for both
newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), spellAbility, game);
// check target restriction
if (newTarget.getFirstTarget() != null && filterNewTarget != null) {
Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget());
if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) {
game.informPlayer(player, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")");
newTarget.clearChosen();
}
}
} while (player.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1));
// choose a new target
} else {
@ -473,6 +484,12 @@ public class Spell implements StackObject, Card {
} else {
newTarget.addTarget(targetId, target.getTargetAmount(targetId), spellAbility, game, false);
}
} else if (newTarget.getFirstTarget() != null && filterNewTarget != null) {
Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget());
if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) {
game.informPlayer(player, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")");
again = true;
}
} else {
// valid target was selected, add it to the new target definition
newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), spellAbility, game, false);
@ -506,6 +523,7 @@ public class Spell implements StackObject, Card {
@Override
public void counter(UUID sourceId, Game game) {
this.countered = true;
if (!isCopiedSpell()) {
card.moveToZone(Zone.GRAVEYARD, sourceId, game, false);
}
@ -987,4 +1005,8 @@ public class Spell implements StackObject, Card {
throw new UnsupportedOperationException("Not supported.");
}
public boolean isCountered() {
return countered;
}
}

View file

@ -97,9 +97,10 @@ public class SpellStack extends ArrayDeque<StackObject> {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) {
if ( stackObject instanceof Spell ) {
game.rememberLKI(objectId, Zone.STACK, (Spell)stackObject);
} else {
this.remove(stackObject);
}
this.remove(stackObject);
stackObject.counter(sourceId, game); // tries to move to graveyard
stackObject.counter(sourceId, game);
if (!game.isSimulation())
game.informPlayers(counteredObjectName + " is countered by " + sourceObject.getLogName());
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId()));

View file

@ -54,10 +54,15 @@ import mage.target.Targets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.cards.Card;
import mage.constants.AbilityWord;
import mage.constants.Outcome;
import mage.filter.FilterPermanent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetAmount;
import mage.watchers.Watcher;
/**
@ -535,4 +540,197 @@ public class StackAbility implements StackObject, Ability {
throw new UnsupportedOperationException("Not supported.");
}
/**
* 114.6. Some effects allow a player to change the target(s) of a spell or
* ability, and other effects allow a player to choose new targets for a
* spell or ability.
*
* 114.6a If an effect allows a player to "change the
* target(s)" of a spell or ability, each target can be changed only to
* another legal target. If a target can't be changed to another legal
* target, the original target is unchanged, even if the original target is
* itself illegal by then. If all the targets aren't changed to other legal
* targets, none of them are changed.
*
* 114.6b If an effect allows a player to "change a target" of a
* spell or ability, the process described in rule 114.6a
* is followed, except that only one of those targets may be changed
* (rather than all of them or none of them).
*
* 114.6c If an effect allows a
* player to "change any targets" of a spell or ability, the process
* described in rule 114.6a is followed, except that any number of those
* targets may be changed (rather than all of them or none of them).
*
* 114.6d If an effect allows a player to "choose new targets" for a spell or
* ability, the player may leave any number of the targets unchanged, even
* if those targets would be illegal. If the player chooses to change some
* or all of the targets, the new targets must be legal and must not cause
* any unchanged targets to become illegal.
*
* 114.6e When changing targets or
* choosing new targets for a spell or ability, only the final set of
* targets is evaluated to determine whether the change is legal.
*
* Example: Arc Trail is a sorcery that reads "Arc Trail deals 2 damage to
* target creature or player and 1 damage to another target creature or
* player." The current targets of Arc Trail are Runeclaw Bear and Llanowar
* Elves, in that order. You cast Redirect, an instant that reads "You may
* choose new targets for target spell," targeting Arc Trail. You can change
* the first target to Llanowar Elves and change the second target to
* Runeclaw Bear.
*
* 114.7. Modal spells and abilities may have different targeting
* requirements for each mode. An effect that allows a player to change the
* target(s) of a modal spell or ability, or to choose new targets for a
* modal spell or ability, doesn't allow that player to change its mode.
* (See rule 700.2.)
*
* 706.10c Some effects copy a spell or ability and state that its
* controller may choose new targets for the copy. The player may leave any
* number of the targets unchanged, even if those targets would be illegal.
* If the player chooses to change some or all of the targets, the new
* targets must be legal. Once the player has decided what the copy's
* targets will be, the copy is put onto the stack with those targets.
*
* @param game
* @param playerId - player that can/has to change the target of the ability
* @param forceChange - does only work for targets with maximum of one targetId
* @param onlyOneTarget - 114.6b one target must be changed to another target
* @param filterNewTarget restriction for the new target, if null nothing is cheched
* @return
*/
@Override
public boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) {
Player player = game.getPlayer(playerId);
if (player != null) {
StringBuilder newTargetDescription = new StringBuilder();
// Some abilities can have more than one mode
for (UUID modeId : ability.getModes().getSelectedModes()) {
Mode mode = ability.getModes().get(modeId);
for (Target target : mode.getTargets()) {
Target newTarget = chooseNewTarget(player, getStackAbility(), mode, target, forceChange, filterNewTarget, game);
// clear the old target and copy all targets from new target
target.clearChosen();
for (UUID targetId : newTarget.getTargets()) {
target.addTarget(targetId, newTarget.getTargetAmount(targetId), ability, game, false);
}
}
newTargetDescription.append(((AbilityImpl)ability).getTargetDescription(mode.getTargets(), game));
}
if (newTargetDescription.length() > 0 && !game.isSimulation()) {
game.informPlayers(this.getName() + " is now " + newTargetDescription.toString());
}
return true;
}
return false;
}
/**
* Handles the change of one target instance of a mode
*
* @param player - player that can choose the new target
* @param ability
* @param mode
* @param target
* @param forceChange
* @param game
* @return
*/
private Target chooseNewTarget(Player player, Ability ability, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) {
Target newTarget = target.copy();
newTarget.clearChosen();
for (UUID targetId : target.getTargets()) {
String targetNames = getNamesOfTargets(targetId, game);
// change the target?
if (targetNames != null
&& (forceChange || player.chooseUse(mode.getEffects().get(0).getOutcome(), "Change this target: " + targetNames + "?", game))) {
// choose exactly one other target
if (forceChange && target.possibleTargets(this.getSourceId(), getControllerId(), game).size() > 1) { // controller of ability must be used (e.g. TargetOpponent)
int iteration = 0;
do {
if (iteration > 0 && !game.isSimulation()) {
game.informPlayer(player, "You may only select exactly one target that must be different from the origin target!");
}
iteration++;
newTarget.clearChosen();
// TODO: Distinction between "ability controller" and "player that can change the target" - here player is used for both
newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), ability, game);
// check target restriction
if (newTarget.getFirstTarget() != null && filterNewTarget != null) {
Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget());
if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) {
game.informPlayer(player, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")");
newTarget.clearChosen();
}
}
} while (player.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1));
// choose a new target
} else {
// build a target definition with exactly one possible target to select that replaces old target
Target tempTarget = target.copy();
if (target instanceof TargetAmount) {
((TargetAmount)tempTarget).setAmountDefinition(new StaticValue(target.getTargetAmount(targetId)));
}
tempTarget.setMinNumberOfTargets(1);
tempTarget.setMaxNumberOfTargets(1);
boolean again;
do {
again = false;
tempTarget.clearChosen();
if (!tempTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), ability, game)) {
if (player.chooseUse(Outcome.Benefit, "No target object selected. Reset to original target?", game)) {
// use previous target no target was selected
newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false);
} else {
again = true;
}
} else {
// if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition
if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) {
if (player.isHuman()) {
game.informPlayer(player, "This target was already selected from origin ability. You can only keep this target!");
again = true;
} else {
newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false);
}
} else if (newTarget.getFirstTarget() != null && filterNewTarget != null) {
Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget());
if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) {
game.informPlayer(player, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")");
again = true;
}
} else {
// valid target was selected, add it to the new target definition
newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false);
}
}
} while (again && player.isInGame());
}
}
// keep the target
else {
newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false);
}
}
return newTarget;
}
private String getNamesOfTargets(UUID targetId, Game game) {
MageObject object = game.getObject(targetId);
String targetNames = null;
if (object == null) {
Player targetPlayer = game.getPlayer(targetId);
if (targetPlayer != null) {
targetNames = targetPlayer.getName();
}
} else {
targetNames = object.getName();
}
return targetNames;
}
}

View file

@ -31,6 +31,7 @@ package mage.game.stack;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.filter.FilterPermanent;
import mage.game.Controllable;
import mage.game.Game;
@ -41,6 +42,7 @@ public interface StackObject extends MageObject, Controllable {
void counter(UUID sourceId, Game game);
Ability getStackAbility();
int getConvertedManaCost();
boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget);
@Override
StackObject copy();
}

View file

@ -104,4 +104,8 @@ public interface Target extends Serializable {
UUID getFirstTarget();
Target copy();
}
// some targets are choosen from players that are not the controller of the ability (e.g. Pandemonium)
void setTargetController(UUID playerId);
UUID getTargetController();
}

View file

@ -60,6 +60,7 @@ public abstract class TargetImpl implements Target {
// is the target handled as targeted spell/ability (notTarget = true is used for not targeted effects like e.g. sacrifice)
protected boolean notTarget = false;
protected boolean atRandom = false;
protected UUID targetController = null; // if null the ability controller is the targetController
@Override
public abstract TargetImpl copy();
@ -84,6 +85,7 @@ public abstract class TargetImpl implements Target {
this.zoneChangeCounters.putAll(target.zoneChangeCounters);
this.atRandom = target.atRandom;
this.notTarget = target.notTarget;
this.targetController = target.targetController;
}
@Override
@ -421,5 +423,15 @@ public abstract class TargetImpl implements Target {
this.atRandom = atRandom;
}
@Override
public void setTargetController(UUID playerId) {
this.targetController = playerId;
}
@Override
public UUID getTargetController() {
return targetController;
}
}

View file

@ -97,7 +97,11 @@ public class Targets extends ArrayList<Target> {
}
while (!isChosen()) {
Target target = this.getUnchosen().get(0);
if (!target.chooseTarget(outcome, playerId, source, game)) {
UUID targetController = playerId;
if (target.getTargetController() != null) { // some targets can have controller different than ability controller
targetController = target.getTargetController();
}
if (!target.chooseTarget(outcome, targetController, source, game)) {
return false;
}
}

View file

@ -132,7 +132,7 @@ public class CardUtil {
* @param increaseCount
*/
public static void increaseCost(Ability ability, int increaseCount) {
adjustCost(ability, -increaseCount);
adjustAbilityCost(ability, -increaseCount);
adjustAlternativeCosts(ability, -increaseCount);
}
@ -143,7 +143,7 @@ public class CardUtil {
* @param reduceCount
*/
public static void reduceCost(Ability ability, int reduceCount) {
adjustCost(ability, reduceCount);
adjustAbilityCost(ability, reduceCount);
adjustAlternativeCosts(ability, reduceCount);
}
@ -154,7 +154,7 @@ public class CardUtil {
* @param reduceCount
*/
public static void adjustCost(SpellAbility spellAbility, int reduceCount) {
CardUtil.adjustCost((Ability) spellAbility, reduceCount);
CardUtil.adjustAbilityCost((Ability) spellAbility, reduceCount);
adjustAlternativeCosts(spellAbility, reduceCount);
}
@ -208,7 +208,7 @@ public class CardUtil {
* @param ability
* @param reduceCount
*/
private static void adjustCost(Ability ability, int reduceCount) {
public static void adjustAbilityCost(Ability ability, int reduceCount) {
ManaCosts<ManaCost> adjustedCost = adjustCost(ability.getManaCostsToPay(), reduceCount);
ability.getManaCostsToPay().clear();
ability.getManaCostsToPay().addAll(adjustedCost);