Merge remote-tracking branch 'upstream/master'

This commit is contained in:
MTGfan 2016-12-08 21:37:02 -05:00
commit 4a2e1c5063
82 changed files with 4468 additions and 688 deletions

View file

@ -32,7 +32,7 @@ import mage.util.Copyable;
public class MageInt implements Serializable, Copyable<MageInt> {
public static MageInt EmptyMageInt = new MageInt(Integer.MIN_VALUE, null) {
public static MageInt EmptyMageInt = new MageInt(Integer.MIN_VALUE, "") {
private static final String exceptionMessage = "MageInt.EmptyMageInt can't be modified.";

View file

@ -39,6 +39,7 @@ import java.util.concurrent.ConcurrentHashMap;
import mage.MageObject;
import mage.cards.Card;
import mage.constants.Zone;
import mage.designations.Designation;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.NumberOfTriggersEvent;
@ -146,6 +147,8 @@ public class TriggeredAbilities extends ConcurrentHashMap<String, TriggeredAbili
public void add(TriggeredAbility ability, UUID sourceId, MageObject attachedTo) {
if (sourceId == null) {
add(ability, attachedTo);
} else if (attachedTo == null) {
this.put(ability.getId() + "_" + sourceId, ability);
} else {
this.add(ability, attachedTo);
List<UUID> uuidList = new LinkedList<>();
@ -190,8 +193,14 @@ public class TriggeredAbilities extends ConcurrentHashMap<String, TriggeredAbili
public void removeAbilitiesOfNonExistingSources(Game game) {
// e.g. Token that had triggered abilities
List<String> keysToRemove = new ArrayList<>();
Abilities:
for (Entry<String, TriggeredAbility> entry : this.entrySet()) {
if (game.getObject(entry.getValue().getSourceId()) == null) {
for (Designation designation : game.getState().getDesignations()) {
if (designation.getId().equals(entry.getValue().getSourceId())) {
continue Abilities;
}
}
keysToRemove.add(entry.getKey());
}
}

View file

@ -29,7 +29,6 @@ package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.DamagedPlayerEvent;

View file

@ -1,7 +1,29 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
* 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.condition.common;

View file

@ -0,0 +1,44 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
/**
*
* @author LevelX2
*/
public class BecomesMonarchTargetEffect extends OneShotEffect {
public BecomesMonarchTargetEffect() {
super(Outcome.Benefit);
staticText = "target player becomes the monarch";
}
public BecomesMonarchTargetEffect(final BecomesMonarchTargetEffect effect) {
super(effect);
}
@Override
public BecomesMonarchTargetEffect copy() {
return new BecomesMonarchTargetEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
if (targetPlayer != null) {
game.setMonarchId(source, targetPlayer.getId());
return true;
}
return false;
}
}

View file

@ -0,0 +1,106 @@
/*
* 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.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.players.Library;
import mage.players.Player;
/**
*
* @author Styxo
*/
public class RevealCardsFromLibraryUntilEffect extends OneShotEffect {
private FilterCard filter;
public RevealCardsFromLibraryUntilEffect(FilterCard filter) {
super(Outcome.ReturnToHand);
this.filter = filter;
this.staticText = "reveal cards from the top of your library until you reveal a " + filter.getMessage() + ". Put that card into your hand and the rest on the bottom of your library in a random order";
}
public RevealCardsFromLibraryUntilEffect(final RevealCardsFromLibraryUntilEffect effect) {
super(effect);
this.filter = effect.filter;
}
@Override
public RevealCardsFromLibraryUntilEffect copy() {
return new RevealCardsFromLibraryUntilEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (controller != null && controller.getLibrary().size() > 0) {
Cards cards = new CardsImpl();
Library library = controller.getLibrary();
Card card = null;
do {
card = library.removeFromTop(game);
if (card != null) {
cards.add(card);
}
} while (library.size() > 0 && card != null && !filter.match(card, game));
// reveal cards
if (!cards.isEmpty()) {
controller.revealCards(sourceObject.getIdName(), cards, game);
if (filter.match(card, game)) {
// put creature card in hand
controller.moveCards(card, Zone.HAND, source, game);
// remove it from revealed card list
cards.remove(card);
}
// Put the rest on the bottom of your library in a random order
Cards randomOrder = new CardsImpl();
while (cards.size() > 0) {
card = cards.getRandom(game);
if (card != null) {
cards.remove(card);
randomOrder.add(card);
controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.HAND, false, false);
}
}
controller.putCardsOnBottomOfLibrary(randomOrder, game, source, false);
}
return true;
}
return false;
}
}

View file

@ -0,0 +1,69 @@
/*
* 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.common;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
* @author JRHerlehy
*/
public class SwordsToPlowsharesEffect extends OneShotEffect {
public SwordsToPlowsharesEffect() {
super(Outcome.GainLife);
staticText = "Its controller gains life equal to its power";
}
public SwordsToPlowsharesEffect(final SwordsToPlowsharesEffect effect) {
super(effect);
}
@Override
public SwordsToPlowsharesEffect copy() {
return new SwordsToPlowsharesEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source));
if (permanent != null) {
Player player = game.getPlayer(permanent.getControllerId());
if (player != null) {
player.gainLife(permanent.getPower().getValue(), game);
}
return true;
}
return false;
}
}

View file

@ -14,27 +14,22 @@ public enum FrameStyle {
* The default card frame, normal M15 card frames
*/
M15_NORMAL(BorderType.M15, false),
/**
* Battle for Zendkiar full art basic lands
*/
BFZ_FULL_ART_BASIC(BorderType.M15, true),
/**
* Kaladesh block Inventions
*/
KLD_INVENTION(BorderType.M15, false),
/**
* Zenkikar full art lands
*/
ZEN_FULL_ART_BASIC(BorderType.MOD, true),
/**
* Unhinged full art lands
*/
UNH_FULL_ART_BASIC(BorderType.SPC, true),
/**
* Unglued full art lands
*/
@ -45,30 +40,26 @@ public enum FrameStyle {
*/
public enum BorderType {
/**
* Various specialty borders
* EG: Unhinged, Unglued
* Various specialty borders EG: Unhinged, Unglued
*/
SPC,
/**
* Old border cards
*/
OLD,
/**
* Modern border cards (8th -> Theros)
*/
MOD,
/**
* M15 border cards (M14 -> current)
*/
M15
}
private BorderType borderType;
private boolean isFullArt;
private final BorderType borderType;
private final boolean isFullArt;
public BorderType getBorderType() {
return borderType;
}
@ -76,7 +67,7 @@ public enum FrameStyle {
public boolean isFullArt() {
return isFullArt;
}
FrameStyle(BorderType borderType, boolean isFullArt) {
this.borderType = borderType;
this.isFullArt = isFullArt;

View file

@ -0,0 +1,212 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.designations;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.ObjectColor;
import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.cards.FrameStyle;
import mage.constants.CardType;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import mage.util.GameLog;
/**
*
* @author LevelX2
*/
public abstract class Designation implements MageObject {
private static List emptyList = new ArrayList();
private static ObjectColor emptyColor = new ObjectColor();
private static ManaCosts emptyCost = new ManaCostsImpl();
private String name;
private UUID id;
private FrameStyle frameStyle;
private Abilities<Ability> abilites = new AbilitiesImpl<>();
private String expansionSetCodeForImage;
public Designation(String name, String expansionSetCode) {
this.name = name;
this.id = UUID.randomUUID();
this.frameStyle = FrameStyle.M15_NORMAL;
this.expansionSetCodeForImage = expansionSetCode;
}
public Designation(final Designation designation) {
this.id = designation.id;
this.name = designation.name;
this.frameStyle = designation.frameStyle;
this.abilites = designation.abilites.copy();
}
public abstract void start(Game game, UUID controllerId);
@Override
public FrameStyle getFrameStyle() {
return frameStyle;
}
public void assignNewId() {
this.id = UUID.randomUUID();
}
@Override
public String getName() {
return name;
}
@Override
public String getIdName() {
return getName() + " [" + getId().toString().substring(0, 3) + "]";
}
@Override
public String getImageName() {
return this.name;
}
@Override
public void setName(String name) {
this.name = name;
}
public void addAbility(Ability ability) {
ability.setSourceId(id);
abilites.add(ability);
}
@Override
public Abilities<Ability> getAbilities() {
return abilites;
}
@Override
public ObjectColor getFrameColor(Game game) {
return emptyColor;
}
@Override
public UUID getId() {
return this.id;
}
public void setExpansionSetCodeForImage(String expansionSetCodeForImage) {
this.expansionSetCodeForImage = expansionSetCodeForImage;
}
public String getExpansionSetCodeForImage() {
return expansionSetCodeForImage;
}
@Override
public String getLogName() {
return GameLog.getColoredObjectIdName(this);
}
@Override
public List<CardType> getCardType() {
return emptyList;
}
@Override
public List<String> getSubtype(Game game) {
return emptyList;
}
@Override
public boolean hasSubtype(String subtype, Game game) {
return false;
}
@Override
public List<String> getSupertype() {
return emptyList;
}
@Override
public boolean hasAbility(UUID abilityId, Game game) {
return abilites.containsKey(abilityId);
}
@Override
public ObjectColor getColor(Game game) {
return emptyColor;
}
@Override
public ManaCosts<ManaCost> getManaCost() {
return emptyCost;
}
@Override
public int getConvertedManaCost() {
return 0;
}
@Override
public MageInt getPower() {
return MageInt.EmptyMageInt;
}
@Override
public MageInt getToughness() {
return MageInt.EmptyMageInt;
}
@Override
public int getStartingLoyalty() {
return 0;
}
@Override
public void adjustCosts(Ability ability, Game game) {
}
@Override
public void adjustTargets(Ability ability, Game game) {
}
@Override
public void setCopy(boolean isCopy) {
}
@Override
public boolean isCopy() {
return false;
}
@Override
public Designation copy() {
return this;
}
@Override
public int getZoneChangeCounter(Game game) {
return 1; // Emblems can't move zones until now so return always 1
}
@Override
public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) {
throw new UnsupportedOperationException("Unsupported operation");
}
@Override
public void setZoneChangeCounter(int value, Game game) {
throw new UnsupportedOperationException("Unsupported operation");
}
}

View file

@ -0,0 +1,152 @@
/*
* 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.designations;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
import mage.abilities.effects.common.BecomesMonarchTargetEffect;
import mage.abilities.effects.common.DrawCardTargetEffect;
import mage.constants.CardType;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author LevelX2
*/
public class Monarch extends Designation {
public Monarch() {
super("The Monarch", "CN2");
addAbility(new MonarchDrawTriggeredAbility());
addAbility(new MonarchDealsCombatDamageToAPlayerTriggeredAbility());
}
/**
*
* @param game
* @param controllerId
*/
@Override
public void start(Game game, UUID controllerId) {
}
}
// At the beginning of the monarchs end step, that player draws a card
class MonarchDrawTriggeredAbility extends BeginningOfEndStepTriggeredAbility {
public MonarchDrawTriggeredAbility() {
super(Zone.ALL, new DrawCardTargetEffect(1), TargetController.ANY, null, false);
}
public MonarchDrawTriggeredAbility(final MonarchDrawTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (game.getMonarchId() != null && event.getPlayerId().equals(game.getMonarchId())) {
setControllerId(game.getMonarchId());
getEffects().get(0).setTargetPointer(new FixedTarget(game.getMonarchId()));
return true;
}
return false;
}
@Override
public MonarchDrawTriggeredAbility copy() {
return new MonarchDrawTriggeredAbility(this);
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return true;
}
@Override
public String getRule() {
return "At the beginning of the monarchs end step, that player draws a card.";
}
}
// Whenever a creature deals combat damage to the monarch, its controller becomes the monarch.
class MonarchDealsCombatDamageToAPlayerTriggeredAbility extends TriggeredAbilityImpl {
public MonarchDealsCombatDamageToAPlayerTriggeredAbility() {
super(Zone.ALL, new BecomesMonarchTargetEffect(), false);
}
public MonarchDealsCombatDamageToAPlayerTriggeredAbility(final MonarchDealsCombatDamageToAPlayerTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (((DamagedPlayerEvent) event).isCombatDamage()) {
MageObject damagingObject = game.getObject(event.getSourceId());
if (damagingObject != null
&& damagingObject instanceof Permanent
&& damagingObject.getCardType().contains(CardType.CREATURE)
&& event.getTargetId().equals(game.getMonarchId())) {
setControllerId(event.getPlayerId());
getEffects().get(0).setTargetPointer(new FixedTarget(((Permanent) damagingObject).getControllerId()));
return true;
}
}
return false;
}
@Override
public MonarchDealsCombatDamageToAPlayerTriggeredAbility copy() {
return new MonarchDealsCombatDamageToAPlayerTriggeredAbility(this);
}
@Override
public boolean isInUseableZone(Game game, MageObject source, GameEvent event) {
return true;
}
@Override
public String getRule() {
return "Whenever a creature deals combat damage to the monarch, its controller becomes the monarch.";
}
}

View file

@ -81,6 +81,8 @@ import mage.constants.SpellAbilityType;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.counters.Counters;
import mage.designations.Designation;
import mage.designations.Monarch;
import mage.filter.Filter;
import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
@ -385,11 +387,11 @@ public abstract class GameImpl implements Game, Serializable {
object = getCard(objectId);
if (object == null) {
// for (CommandObject commandObject : state.getCommand()) {
// if (commandObject.getId().equals(objectId)) {
// return commandObject;
// }
// }
for (Designation designation : state.getDesignations()) {
if (designation.getId().equals(objectId)) {
return designation;
}
}
// can be an ability of a sacrificed Token trying to get it's source object
object = getLastKnownInformation(objectId, Zone.BATTLEFIELD);
}
@ -2916,6 +2918,9 @@ public abstract class GameImpl implements Game, Serializable {
@Override
public void setMonarchId(Ability source, UUID monarchId) {
Player newMonarch = getPlayer(monarchId);
if (getMonarchId() == null) {
getState().addDesignation(new Monarch(), this, monarchId);
}
if (newMonarch != null) {
getState().setMonarchId(monarchId);
informPlayers(newMonarch.getLogName() + " is the monarch");

View file

@ -58,6 +58,7 @@ import mage.abilities.effects.Effect;
import mage.cards.Card;
import mage.cards.SplitCard;
import mage.constants.Zone;
import mage.designations.Designation;
import mage.game.combat.Combat;
import mage.game.combat.CombatGroup;
import mage.game.command.Command;
@ -113,6 +114,7 @@ public class GameState implements Serializable, Copyable<GameState> {
private UUID monarchId; // player that is the monarch
private SpellStack stack;
private Command command;
private List<Designation> designations = new ArrayList<>();
private Exile exile;
private Battlefield battlefield;
private int turnNum = 1;
@ -170,6 +172,7 @@ public class GameState implements Serializable, Copyable<GameState> {
this.stack = state.stack.copy();
this.command = state.command.copy();
this.designations.addAll(state.designations);
this.exile = state.exile.copy();
this.battlefield = state.battlefield.copy();
this.turnNum = state.turnNum;
@ -219,6 +222,7 @@ public class GameState implements Serializable, Copyable<GameState> {
this.monarchId = state.monarchId;
this.stack = state.stack;
this.command = state.command;
this.designations = state.designations;
this.exile = state.exile;
this.battlefield = state.battlefield;
this.turnNum = state.turnNum;
@ -461,6 +465,10 @@ public class GameState implements Serializable, Copyable<GameState> {
return exile;
}
public List<Designation> getDesignations() {
return designations;
}
public Command getCommand() {
return command;
}
@ -859,6 +867,14 @@ public class GameState implements Serializable, Copyable<GameState> {
}
}
public void addDesignation(Designation designation, Game game, UUID controllerId) {
getDesignations().add(designation);
for (Ability ability : designation.getAbilities()) {
ability.setControllerId(controllerId);
addAbility(ability, designation.getId(), null);
}
}
public void addCommandObject(CommandObject commandObject) {
getCommand().add(commandObject);
setZone(commandObject.getId(), Zone.COMMAND);
@ -1026,6 +1042,7 @@ public class GameState implements Serializable, Copyable<GameState> {
stack.clear();
exile.clear();
command.clear();
designations.clear();
revealed.clear();
lookedAt.clear();
turnNum = 0;

View file

@ -339,8 +339,17 @@ public class Combat implements Serializable, Copyable<Combat> {
// No need to attack a special defender
if (defendersForcedToAttack.isEmpty()) {
if (defendersForcedToAttack.isEmpty()) {
if (defendersCostlessAttackable.size() == 1) {
player.declareAttacker(creature.getId(), defenders.iterator().next(), game, false);
if (defendersCostlessAttackable.size() >= 1) {
if (defenders.size() == 1) {
player.declareAttacker(creature.getId(), defenders.iterator().next(), game, false);
} else {
TargetDefender target = new TargetDefender(defenders, creature.getId());
target.setRequired(true);
target.setTargetName("planeswalker or player for " + creature.getLogName() + " to attack");
if (player.chooseTarget(Outcome.Damage, target, null, game)) {
player.declareAttacker(creature.getId(), target.getFirstTarget(), game, false);
}
}
}
} else {
TargetDefender target = new TargetDefender(defendersCostlessAttackable, creature.getId());

View file

@ -1231,14 +1231,14 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public boolean triggerAbility(TriggeredAbility source, Game game) {
if (source == null) {
public boolean triggerAbility(TriggeredAbility triggeredAbility, Game game) {
if (triggeredAbility == null) {
logger.warn("Null source in triggerAbility method");
throw new IllegalArgumentException("source TriggeredAbility must not be null");
}
//20091005 - 603.3c, 603.3d
int bookmark = game.bookmarkState();
TriggeredAbility ability = source.copy();
TriggeredAbility ability = triggeredAbility.copy();
MageObject sourceObject = ability.getSourceObject(game);
if (sourceObject != null) {
sourceObject.adjustTargets(ability, game);
@ -1260,7 +1260,7 @@ public abstract class PlayerImpl implements Player, Serializable {
return true;
}
}
restoreState(bookmark, source.getRule(), game); // why restore is needed here? (to remove the triggered ability from the stack)
restoreState(bookmark, triggeredAbility.getRule(), game); // why restore is needed here? (to remove the triggered ability from the stack)
return false;
}
@ -1829,9 +1829,6 @@ public abstract class PlayerImpl implements Player, Serializable {
if (sourceAbilities != null && sourceAbilities.containsKey(InfectAbility.getInstance().getId())) {
addCounters(CounterType.POISON.createInstance(actualDamage), game);
} else {
if (getId().equals(game.getMonarchId()) && sourceControllerId != null) {
game.setMonarchId(null, sourceControllerId);
}
GameEvent damageToLifeLossEvent = new GameEvent(EventType.DAMAGE_CAUSES_LIFE_LOSS, playerId, sourceId, playerId, actualDamage, combatDamage);
if (!game.replaceEvent(damageToLifeLossEvent)) {
this.loseLife(damageToLifeLossEvent.getAmount(), game, combatDamage);