diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java index fdd49bb6b41..05bc713a8b4 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java @@ -63,6 +63,9 @@ public class CardPanelRenderImpl extends CardPanel { if (!a.getRules().equals(b.getRules())) { return false; } + if (a.getRarity() == null || b.getRarity() == null) { + return false; + } if (!a.getRarity().equals(b.getRarity())) { return false; } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java index 3784205acd3..34a2ede8113 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java @@ -42,7 +42,7 @@ public class CardImageUtils { return filePath; } } - log.warn("Token image file not found: " + card.getTokenSetCode() + " - " + card.getName()); + log.warn("Token image file not found: " + card.getSet() + " - " + card.getTokenSetCode() + " - " + card.getName()); return null; } @@ -117,7 +117,7 @@ public class CardImageUtils { return buildPath(imagesDir, set); } } - + public static String getImageBasePath() { String useDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_USE_DEFAULT, "true"); String imagesPath = useDefault.equals("true") ? Constants.IO.imageBaseDir : PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PATH, null); @@ -126,7 +126,7 @@ public class CardImageUtils { } return imagesPath; } - + public static String getTokenBasePath() { String imagesPath = getImageBasePath(); diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 190c68bf016..5413d844ac5 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -45,6 +45,7 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.counters.Counter; import mage.counters.CounterType; +import mage.designations.Designation; import mage.game.Game; import mage.game.command.Emblem; import mage.game.permanent.Permanent; @@ -138,8 +139,7 @@ public class CardView extends SimpleCardView { this(card, game, false); this.id = cardId; } - - + public CardView(CardView cardView) { super(cardView.id, cardView.expansionSetCode, cardView.cardNumber, cardView.usesVariousArt, cardView.tokenSetCode, cardView.gameObject, cardView.tokenDescriptor); @@ -488,6 +488,20 @@ public class CardView extends SimpleCardView { this.rarity = Rarity.COMMON; } + public CardView(Designation designation, StackAbility stackAbility) { + this(true); + this.gameObject = true; + this.id = designation.getId(); + this.mageObjectType = MageObjectType.NULL; + this.name = designation.getName(); + this.displayName = name; + this.rules = new ArrayList<>(); + this.rules.add(stackAbility.getRule(designation.getName())); + this.frameStyle = FrameStyle.M15_NORMAL; + this.expansionSetCode = designation.getExpansionSetCodeForImage(); + this.rarity = Rarity.COMMON; + } + public CardView(boolean empty) { super(null, "", "0", false, "", ""); if (!empty) { diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index 775fd2e099c..bc12e6cb692 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -40,6 +40,7 @@ import mage.constants.CardType; import mage.constants.PhaseStep; import mage.constants.TurnPhase; import mage.constants.Zone; +import mage.designations.Designation; import mage.game.ExileZone; import mage.game.Game; import mage.game.GameState; @@ -143,6 +144,14 @@ public class GameView implements Serializable { stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, object.getName(), cardView)); checkPaid(stackObject.getId(), ((StackAbility) stackObject)); + } else if (object instanceof Designation) { + Designation designation = (Designation) game.getObject(object.getId()); + if (designation != null) { + stack.put(stackObject.getId(), new CardView(designation, (StackAbility) stackObject)); + } else { + LOGGER.fatal("Designation object not found: " + object.getName() + " " + object.toString() + " " + object.getClass().toString()); + } + } else if (object instanceof StackAbility) { StackAbility stackAbility = ((StackAbility) object); stackAbility.newId(); diff --git a/Mage/src/main/java/mage/MageInt.java b/Mage/src/main/java/mage/MageInt.java index bb0fb75f820..73954ede007 100644 --- a/Mage/src/main/java/mage/MageInt.java +++ b/Mage/src/main/java/mage/MageInt.java @@ -32,7 +32,7 @@ import mage.util.Copyable; public class MageInt implements Serializable, Copyable { - 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."; diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java index 967b22938e4..b9f6a179c0f 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java @@ -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 uuidList = new LinkedList<>(); @@ -190,8 +193,14 @@ public class TriggeredAbilities extends ConcurrentHashMap keysToRemove = new ArrayList<>(); + Abilities: for (Entry 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()); } } diff --git a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java index 43f94d113cc..c13f59afbbd 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java @@ -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; diff --git a/Mage/src/main/java/mage/abilities/condition/common/MonarchIsSourceControllerCondition.java b/Mage/src/main/java/mage/abilities/condition/common/MonarchIsSourceControllerCondition.java index e94fa838f98..a7c6910f3b3 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/MonarchIsSourceControllerCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/MonarchIsSourceControllerCondition.java @@ -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; diff --git a/Mage/src/main/java/mage/abilities/effects/common/BecomesMonarchTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/BecomesMonarchTargetEffect.java new file mode 100644 index 00000000000..d4fe9ebeb68 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/BecomesMonarchTargetEffect.java @@ -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; + } + +} diff --git a/Mage/src/main/java/mage/cards/FrameStyle.java b/Mage/src/main/java/mage/cards/FrameStyle.java index 3a006b61cc3..add2795cc52 100644 --- a/Mage/src/main/java/mage/cards/FrameStyle.java +++ b/Mage/src/main/java/mage/cards/FrameStyle.java @@ -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; diff --git a/Mage/src/main/java/mage/designations/Designation.java b/Mage/src/main/java/mage/designations/Designation.java new file mode 100644 index 00000000000..ecf203ff47d --- /dev/null +++ b/Mage/src/main/java/mage/designations/Designation.java @@ -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 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 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 getCardType() { + return emptyList; + } + + @Override + public List getSubtype(Game game) { + return emptyList; + } + + @Override + public boolean hasSubtype(String subtype, Game game) { + return false; + } + + @Override + public List 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 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"); + } + +} diff --git a/Mage/src/main/java/mage/designations/Monarch.java b/Mage/src/main/java/mage/designations/Monarch.java new file mode 100644 index 00000000000..1277945e439 --- /dev/null +++ b/Mage/src/main/java/mage/designations/Monarch.java @@ -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 monarch’s 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 monarch’s 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."; + } +} diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index df561aea631..8425f4b4d4a 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -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"); diff --git a/Mage/src/main/java/mage/game/GameState.java b/Mage/src/main/java/mage/game/GameState.java index 47e113e3044..6b42e627eac 100644 --- a/Mage/src/main/java/mage/game/GameState.java +++ b/Mage/src/main/java/mage/game/GameState.java @@ -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 { private UUID monarchId; // player that is the monarch private SpellStack stack; private Command command; + private List designations = new ArrayList<>(); private Exile exile; private Battlefield battlefield; private int turnNum = 1; @@ -170,6 +172,7 @@ public class GameState implements Serializable, Copyable { 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 { 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 { return exile; } + public List getDesignations() { + return designations; + } + public Command getCommand() { return command; } @@ -859,6 +867,14 @@ public class GameState implements Serializable, Copyable { } } + 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 { stack.clear(); exile.clear(); command.clear(); + designations.clear(); revealed.clear(); lookedAt.clear(); turnNum = 0; diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index dd0ccecaa07..86f053fa9d0 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -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);