diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 5989d537f4..5997c368ae 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -251,7 +251,7 @@ public class HumanPlayer extends PlayerImpl { @Override public boolean choose(Outcome outcome, Choice choice, Game game) { - if (outcome.equals(Outcome.PutManaInPool)) { + if (Outcome.PutManaInPool.equals(outcome)) { if (currentlyUnpaidMana != null && ManaUtil.tryToAutoSelectAManaColor(choice, currentlyUnpaidMana)) { return true; diff --git a/Mage/src/mage/abilities/effects/AuraReplacementEffect.java b/Mage/src/mage/abilities/effects/AuraReplacementEffect.java index e5cdec0b93..eb62e657c2 100644 --- a/Mage/src/mage/abilities/effects/AuraReplacementEffect.java +++ b/Mage/src/mage/abilities/effects/AuraReplacementEffect.java @@ -51,13 +51,15 @@ import mage.target.common.TargetCardInGraveyard; /** * Cards with the Aura subtype don't change the zone they are in, if there is no * valid target on the battlefield. Also, when entering the battlefield and it - * was not cast (so from Zone != Hand), this effect gets the target to whitch to attach it - * and adds the Aura the the battlefield and attachs it to the target. + * was not cast (so from Zone != Hand), this effect gets the target to whitch to + * attach it and adds the Aura the the battlefield and attachs it to the target. * The "attachTo:" value in game state has to be set therefore. - * - * If no "attachTo:" value is defined, the controlling player has to chose the aura target. * - * This effect is automatically added to ContinuousEffects at the start of a game + * If no "attachTo:" value is defined, the controlling player has to chose the + * aura target. + * + * This effect is automatically added to ContinuousEffects at the start of a + * game * * @author North */ @@ -93,7 +95,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { return false; } // Aura enters the battlefield attached - Object object = game.getState().getValue("attachTo:"+card.getId()); + Object object = game.getState().getValue("attachTo:" + card.getId()); if (object != null && object instanceof PermanentCard) { return false; } @@ -113,7 +115,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { targetId = stackAbility.getEffects().get(0).getTargetPointer().getFirst(game, stackAbility); } } - + if (targetId == null) { Target target = card.getSpellAbility().getTargets().get(0); enchantCardInGraveyard = target instanceof TargetCardInGraveyard; @@ -122,9 +124,10 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { } Player player = game.getPlayer(card.getOwnerId()); Outcome auraOutcome = Outcome.BoostCreature; - Ability: for (Ability ability:card.getAbilities()) { + Ability: + for (Ability ability : card.getAbilities()) { if (ability instanceof SpellAbility) { - for (Effect effect: ability.getEffects()) { + for (Effect effect : ability.getEffects()) { if (effect instanceof AttachEffect) { auraOutcome = effect.getOutcome(); break Ability; @@ -167,13 +170,16 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { game.getBattlefield().addPermanent(permanent); card.setZone(Zone.BATTLEFIELD, game); game.applyEffects(); - permanent.entersBattlefield(event.getSourceId(), game, fromZone, true); + boolean entered = permanent.entersBattlefield(event.getSourceId(), game, fromZone, true); game.applyEffects(); + if (!entered) { + return false; + } game.fireEvent(new ZoneChangeEvent(permanent, controllerId, fromZone, Zone.BATTLEFIELD)); if (targetCard != null) { permanent.attachTo(targetCard.getId(), game); - } + } if (targetPermanent != null) { targetPermanent.addAttachment(permanent.getId(), game); } @@ -183,7 +189,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { } return true; } - + @Override public boolean checksEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ZONE_CHANGE; @@ -192,7 +198,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { if (((ZoneChangeEvent) event).getToZone().equals(Zone.BATTLEFIELD) - && !(((ZoneChangeEvent) event).getFromZone().equals(Zone.HAND)) ) { + && !(((ZoneChangeEvent) event).getFromZone().equals(Zone.HAND))) { Card card = game.getCard(event.getTargetId()); if (card != null && card.getCardType().contains(CardType.ENCHANTMENT) && card.hasSubtype("Aura")) { return true; diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index db25e8f955..c353c8e8d3 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -421,13 +421,17 @@ public abstract class CardImpl extends MageObjectImpl implements Card { game.setZone(objectId, Zone.BATTLEFIELD); game.setScopeRelevant(true); game.applyEffects(); - permanent.entersBattlefield(sourceId, game, event.getFromZone(), true); + boolean entered = permanent.entersBattlefield(sourceId, game, event.getFromZone(), true); game.setScopeRelevant(false); game.applyEffects(); - if (flag) { - permanent.setTapped(true); + if (entered) { + if (flag) { + permanent.setTapped(true); + } + event.setTarget(permanent); + } else { + return false; } - event.setTarget(permanent); break; default: Card sourceCard = game.getCard(sourceId); @@ -603,11 +607,13 @@ public abstract class CardImpl extends MageObjectImpl implements Card { game.setScopeRelevant(true); permanent.setTapped(tapped); permanent.setFaceDown(facedown, game); - permanent.entersBattlefield(sourceId, game, event.getFromZone(), true); + boolean entered = permanent.entersBattlefield(sourceId, game, event.getFromZone(), true); game.setScopeRelevant(false); game.applyEffects(); - game.addSimultaneousEvent(new ZoneChangeEvent(permanent, event.getPlayerId(), fromZone, Zone.BATTLEFIELD)); - return true; + if (entered) { + game.addSimultaneousEvent(new ZoneChangeEvent(permanent, event.getPlayerId(), fromZone, Zone.BATTLEFIELD)); + return true; + } } if (facedown) { this.setFaceDown(false, game); diff --git a/Mage/src/mage/game/combat/Combat.java b/Mage/src/mage/game/combat/Combat.java index 4cc23e3141..1ed22dcf96 100644 --- a/Mage/src/mage/game/combat/Combat.java +++ b/Mage/src/mage/game/combat/Combat.java @@ -810,19 +810,21 @@ public class Combat implements Serializable, Copyable<Combat> { // check now, if it already blocks a creature that mustBeBlockedByAtLeastOne if (possibleBlocker.getBlocking() > 0) { CombatGroup combatGroupOfPossibleBlocker = findGroupOfBlocker(possibleBlockerId); - for (UUID blockedAttackerId : combatGroupOfPossibleBlocker.getAttackers()) { - if (mustBeBlockedByAtLeastOne.containsKey(blockedAttackerId)) { - // blocks a creature that has to be blocked by at least one - if (combatGroupOfPossibleBlocker.getBlockers().size() == 1) { - // the creature blocks alone already a creature that has to be blocked by at least one, - // so this is ok - return null; + if (combatGroupOfPossibleBlocker != null) { + for (UUID blockedAttackerId : combatGroupOfPossibleBlocker.getAttackers()) { + if (mustBeBlockedByAtLeastOne.containsKey(blockedAttackerId)) { + // blocks a creature that has to be blocked by at least one + if (combatGroupOfPossibleBlocker.getBlockers().size() == 1) { + // the creature blocks alone already a creature that has to be blocked by at least one, + // so this is ok + return null; + } + // TODO: Check if the attacker is already blocked by another creature + // and despite there is need that this attacker blocks this attacker also + // I don't know why + Permanent blockedAttacker = game.getPermanent(blockedAttackerId); + return possibleBlocker.getIdName() + " blocks with other creatures " + blockedAttacker.getIdName() + ", which has to be blocked by only one creature. "; } - // TODO: Check if the attacker is already blocked by another creature - // and despite there is need that this attacker blocks this attacker also - // I don't know why - Permanent blockedAttacker = game.getPermanent(blockedAttackerId); - return possibleBlocker.getIdName() + " blocks with other creatures " + blockedAttacker.getIdName() + ", which has to be blocked by only one creature. "; } } } @@ -947,9 +949,12 @@ public class Combat implements Serializable, Copyable<Combat> { if (!canDefenderBeAttacked(attackerId, defenderId, game)) { return false; } + Permanent attacker = game.getPermanent(attackerId); + if (attacker == null) { + return false; + } CombatGroup newGroup = new CombatGroup(defenderId, defender != null, defender != null ? defender.getControllerId() : defenderId); newGroup.attackers.add(attackerId); - Permanent attacker = game.getPermanent(attackerId); attacker.setAttacking(true); groups.add(newGroup); return true; diff --git a/Mage/src/mage/game/permanent/Permanent.java b/Mage/src/mage/game/permanent/Permanent.java index 33221ea2fb..9a53acdc2d 100644 --- a/Mage/src/mage/game/permanent/Permanent.java +++ b/Mage/src/mage/game/permanent/Permanent.java @@ -1,31 +1,30 @@ /* -* 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. -*/ - + * 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.game.permanent; import java.util.ArrayList; @@ -45,60 +44,89 @@ import mage.game.Game; public interface Permanent extends Card, Controllable { void setControllerId(UUID controllerId); + boolean isTapped(); + boolean untap(Game game); + boolean tap(Game game); + /** * use tap(game) - * <p>setTapped doesn't trigger TAPPED event and should be used - * only if you want permanent to enter battlefield tapped</p> + * <p> + * setTapped doesn't trigger TAPPED event and should be used only if you + * want permanent to enter battlefield tapped</p> * * @param tapped * @deprecated */ @Deprecated void setTapped(boolean tapped); + boolean canTap(); boolean isFlipped(); + boolean unflip(Game game); + boolean flip(Game game); boolean transform(Game game); + boolean isTransformed(); + void setTransformed(boolean value); boolean isPhasedIn(); + boolean phaseIn(Game game); + boolean phaseOut(Game game); boolean isMonstrous(); + void setMonstrous(boolean value); boolean isRenowned(); + void setRenowned(boolean value); void setCardNumber(int cid); + void setExpansionSetCode(String expansionSetCode); + void setRarity(Rarity rarity); + void setFlipCard(boolean flipCard); + void setFlipCardName(String flipCardName); + void setSecondCardFace(Card card); Counters getCounters(); List<UUID> getAttachments(); + UUID getAttachedTo(); + void attachTo(UUID permanentId, Game game); + boolean addAttachment(UUID permanentId, Game game); + boolean removeAttachment(UUID permanentId, Game game); boolean canBeTargetedBy(MageObject source, UUID controllerId, Game game); + boolean hasProtectionFrom(MageObject source, Game game); + boolean cantBeEnchantedBy(MageObject source, Game game); + boolean wasControlledFromStartOfControllerTurn(); + boolean hasSummoningSickness(); + int getDamage(); + int damage(int damage, UUID sourceId, Game game, boolean combat, boolean preventable); int damage(int damage, UUID sourceId, Game game, boolean combat, boolean preventable, ArrayList<UUID> appliedEffects); @@ -114,58 +142,83 @@ public interface Permanent extends Card, Controllable { * @return */ int markDamage(int damage, UUID sourceId, Game game, boolean preventable, boolean combat); + int applyDamage(Game game); void removeAllDamage(Game game); void reset(Game game); + boolean destroy(UUID sourceId, Game game, boolean noRegen); + boolean sacrifice(UUID sourceId, Game game); + boolean regenerate(UUID sourceId, Game game); + boolean fight(Permanent fightTarget, Ability source, Game game); - void entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent); + boolean entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent); + String getValue(); @Deprecated void addAbility(Ability ability); + @Deprecated void addAbility(Ability ability, Game game); + void addAbility(Ability ability, UUID sourceId, Game game); + void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId); void removeAllAbilities(UUID sourceId, Game game); void addLoyaltyUsed(); + boolean canLoyaltyBeUsed(Game game); - public void resetControl(); + boolean changeControllerId(UUID controllerId, Game game); + boolean checkControlChanged(Game game); void beginningOfTurn(Game game); + void endOfTurn(Game game); + int getTurnsOnBattlefield(); void addPower(int power); + void addToughness(int toughness); boolean isAttacking(); + int getBlocking(); + void setAttacking(boolean attacking); + void setBlocking(int blocking); + int getMaxBlocks(); + void setMaxBlocks(int maxBlocks); + int getMinBlockedBy(); + void setMinBlockedBy(int minBlockedBy); + int getMaxBlockedBy(); + boolean isRemovedFromCombat(); + void setRemovedFromCombat(boolean removedFromCombat); /** * Sets the maximum number of blockers the creature can be blocked by. - * Default = 0 which means there is no restriction in the number of blockers. + * Default = 0 which means there is no restriction in the number of + * blockers. * * @param maxBlockedBy maximum number of blockers */ @@ -180,11 +233,14 @@ public interface Permanent extends Card, Controllable { * @return */ boolean canAttack(UUID defenderId, Game game); + boolean canBlock(UUID attackerId, Game game); + boolean canBlockAny(Game game); /** - * Checks by restriction effects if the permanent can use activated abilities + * Checks by restriction effects if the permanent can use activated + * abilities * * @param game * @return true - permanent can use activated abilities @@ -192,11 +248,15 @@ public interface Permanent extends Card, Controllable { boolean canUseActivatedAbilities(Game game); boolean removeFromCombat(Game game); + boolean removeFromCombat(Game game, boolean withInfo); + boolean isDeathtouched(); /** - * Returns a list of object refrences that dealt damage this turn to this permanent + * Returns a list of object refrences that dealt damage this turn to this + * permanent + * * @return */ HashSet<MageObjectReference> getDealtDamageByThisTurn(); @@ -222,13 +282,14 @@ public interface Permanent extends Card, Controllable { * Get card that was imprinted on this one. * * Can be null if no card was imprinted. + * * @return Imprinted card UUID. */ List<UUID> getImprinted(); /** - * Allows to connect any card to permanent. - * Very similar to Imprint except that it is for internal use only. + * Allows to connect any card to permanent. Very similar to Imprint except + * that it is for internal use only. * * @param key * @param connectedCard @@ -236,8 +297,9 @@ public interface Permanent extends Card, Controllable { void addConnectedCard(String key, UUID connectedCard); /** - * Returns connected cards. - * Very similar to Imprint except that it is for internal use only. + * Returns connected cards. Very similar to Imprint except that it is for + * internal use only. + * * @param key * @return */ @@ -245,6 +307,7 @@ public interface Permanent extends Card, Controllable { /** * Clear all connected cards. + * * @param key */ void clearConnectedCards(String key); @@ -269,9 +332,11 @@ public interface Permanent extends Card, Controllable { void clearPairedCard(); void setMorphed(boolean value); + boolean isMorphed(); void setManifested(boolean value); + boolean isManifested(); @Override @@ -280,5 +345,6 @@ public interface Permanent extends Card, Controllable { // Simple int counter to set a timewise create order , the lower the number the earlier the object was created // if objects enter the battlefield at the same time they can get (and should) get the same number. int getCreateOrder(); + void setCreateOrder(int createOrder); } diff --git a/Mage/src/mage/game/permanent/token/Token.java b/Mage/src/mage/game/permanent/token/Token.java index def3ce49c6..59d4727dbe 100644 --- a/Mage/src/mage/game/permanent/token/Token.java +++ b/Mage/src/mage/game/permanent/token/Token.java @@ -159,15 +159,17 @@ public class Token extends MageObjectImpl { this.lastAddedTokenId = newToken.getId(); game.setScopeRelevant(true); game.applyEffects(); - newToken.entersBattlefield(sourceId, game, Zone.OUTSIDE, true); + boolean entered = newToken.entersBattlefield(sourceId, game, Zone.OUTSIDE, true); game.setScopeRelevant(false); game.applyEffects(); - game.fireEvent(new ZoneChangeEvent(newToken, event.getPlayerId(), Zone.OUTSIDE, Zone.BATTLEFIELD)); - if (attacking && game.getCombat() != null) { - game.getCombat().addAttackingCreature(newToken.getId(), game); - } - if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + " puts a " + newToken.getLogName() + " token onto the battlefield"); + if (entered) { + game.fireEvent(new ZoneChangeEvent(newToken, event.getPlayerId(), Zone.OUTSIDE, Zone.BATTLEFIELD)); + if (attacking && game.getCombat() != null) { + game.getCombat().addAttackingCreature(newToken.getId(), game); + } + if (!game.isSimulation()) { + game.informPlayers(controller.getLogName() + " puts a " + newToken.getLogName() + " token onto the battlefield"); + } } }