This commit is contained in:
klayhamn 2015-07-28 21:17:00 +03:00
commit 0ddc11f4dc
85 changed files with 3845 additions and 759 deletions

View file

@ -0,0 +1,98 @@
/*
* 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.dynamicvalue.common;
import java.io.ObjectStreamException;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.MageSingleton;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.cards.Card;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.game.Game;
import mage.players.Player;
/**
* Don't use this for continuous effects because it applies a reveal effect!
*
* @author LevelX2
*/
public class ParleyCount implements DynamicValue, MageSingleton {
private static final ParleyCount fINSTANCE = new ParleyCount();
private Object readResolve() throws ObjectStreamException {
return fINSTANCE;
}
public static ParleyCount getInstance() {
return fINSTANCE;
}
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
// Each player reveals the top card of his or her library. For each nonland card revealed this way
int parleyValue = 0;
MageObject sourceObject = game.getObject(sourceAbility.getSourceId());
if (sourceObject != null) {
for (UUID playerId : game.getState().getPlayerList(sourceAbility.getControllerId())) {
Player player = game.getPlayer(playerId);
if (player != null) {
Card card = player.getLibrary().getFromTop(game);
if (card != null) {
if (!card.getCardType().contains(CardType.LAND)) {
parleyValue++;
}
player.revealCards(sourceObject.getIdName() + " (" + player.getName() + ")", new CardsImpl(card), game);
}
}
}
}
return parleyValue;
}
@Override
public DynamicValue copy() {
return fINSTANCE;
}
@Override
public String toString() {
return "1";
}
@Override
public String getMessage() {
return "Parley";
}
}

View file

@ -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;

View file

@ -0,0 +1,103 @@
/*
* 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.Mode;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author LoneFox
*/
public class DamageTargetControllerEffect extends OneShotEffect {
protected DynamicValue amount;
protected boolean preventable;
public DamageTargetControllerEffect(int amount) {
this(new StaticValue(amount), true);
}
public DamageTargetControllerEffect(int amount, boolean preventable) {
this(new StaticValue(amount), preventable);
}
public DamageTargetControllerEffect(DynamicValue amount) {
this(amount, true);
}
public DamageTargetControllerEffect(DynamicValue amount, boolean preventable) {
super(Outcome.Damage);
this.amount = amount;
this.preventable = preventable;
}
public DamageTargetControllerEffect(final DamageTargetControllerEffect effect) {
super(effect);
amount = effect.amount.copy();
preventable = effect.preventable;
}
@Override
public DamageTargetControllerEffect copy() {
return new DamageTargetControllerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if(permanent != null) {
Player targetController = game.getPlayer(permanent.getControllerId());
if(targetController != null) {
targetController.damage(amount.calculate(game, source, this), source.getSourceId(), game, false, preventable);
}
return true;
}
return false;
}
@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
String text = "{this} deals " + amount.getMessage() + " damage to target "
+ mode.getTargets().get(0).getTargetName() + "'s controller";
if(!preventable) {
text += ". The damage can't be prevented";
}
return text;
}
}

View file

@ -0,0 +1,96 @@
/*
* 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.ReplacementEffectImpl;
import mage.cards.Card;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent.EventType;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.stack.StackObject;
import mage.players.Player;
/**
*
* @author LoneFox
*/
public class DiscardOntoBattlefieldEffect extends ReplacementEffectImpl {
public DiscardOntoBattlefieldEffect() {
super(Duration.EndOfGame, Outcome.PutCardInPlay);
staticText = "If a spell or ability an opponent controls causes you to discard {this}, put it onto the battlefield instead of putting it into your graveyard";
}
public DiscardOntoBattlefieldEffect(final DiscardOntoBattlefieldEffect effect) {
super(effect);
}
@Override
public DiscardOntoBattlefieldEffect copy() {
return new DiscardOntoBattlefieldEffect(this);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == EventType.ZONE_CHANGE;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (event.getTargetId().equals(source.getSourceId())) {
ZoneChangeEvent zcEvent = (ZoneChangeEvent) event;
if (zcEvent.getFromZone() == Zone.HAND && zcEvent.getToZone() == Zone.GRAVEYARD) {
StackObject spell = game.getStack().getStackObject(event.getSourceId());
if (spell != null && game.getOpponents(source.getControllerId()).contains(spell.getControllerId())) {
return true;
}
}
}
return false;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Card card = game.getCard(source.getSourceId());
if (card != null) {
Player owner = game.getPlayer(card.getOwnerId());
if (owner != null) {
if (owner.putOntoBattlefieldWithInfo(card, game, Zone.HAND, source.getSourceId())) {
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.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class UntapAllEffect extends OneShotEffect {
private final FilterPermanent filter;
public UntapAllEffect(FilterPermanent filter) {
super(Outcome.Untap);
staticText = "untap all " + filter.getMessage();
this.filter = filter;
}
public UntapAllEffect(final UntapAllEffect effect) {
super(effect);
this.filter = effect.filter;
}
@Override
public boolean apply(Game game, Ability source) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
permanent.untap(game);
}
return true;
}
@Override
public UntapAllEffect copy() {
return new UntapAllEffect(this);
}
}

View file

@ -0,0 +1,114 @@
/*
* 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.continuous;
import java.util.ArrayList;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.TargetController;
import mage.game.Game;
import mage.players.Player;
/**
*
* @author LoneFox
*/
public class PlayWithHandRevealedEffect extends ContinuousEffectImpl {
private TargetController who;
public PlayWithHandRevealedEffect(TargetController who) {
super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Detriment);
this.who = who;
}
public PlayWithHandRevealedEffect(final PlayWithHandRevealedEffect effect) {
super(effect);
who = effect.who;
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Iterable<UUID> affectedPlayers;
switch(who) {
case ANY:
affectedPlayers = controller.getInRange();
break;
case OPPONENT:
affectedPlayers = game.getOpponents(source.getControllerId());
break;
case YOU:
ArrayList tmp = new ArrayList<UUID>();
tmp.add(source.getControllerId());
affectedPlayers = tmp;
break;
default:
return false;
}
for(UUID playerID: affectedPlayers) {
Player player = game.getPlayer(playerID);
if (player != null) {
player.revealCards(player.getName() + "'s hand cards", player.getHand(), game, false);
}
}
return true;
}
return false;
}
@Override
public PlayWithHandRevealedEffect copy() {
return new PlayWithHandRevealedEffect(this);
}
@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {
return staticText;
}
switch(who) {
case ANY:
return "Players play with their hands revealed";
case OPPONENT:
return "Your opponents play with their hands revealed";
case YOU:
return "Play with your hand revealed";
default:
return "Unknown TargetController for PlayWithHandRevealedEffect";
}
}
}

View file

@ -27,12 +27,12 @@
*/
package mage.abilities.effects.common.discard;
import mage.constants.Outcome;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
@ -98,7 +98,7 @@ public class DiscardControllerEffect extends OneShotEffect {
}
private void setText() {
StringBuilder sb = new StringBuilder("Discard ");
StringBuilder sb = new StringBuilder("discard ");
if (amount.toString().equals("1")) {
sb.append("a");
} else {

View file

@ -10,20 +10,29 @@ import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.turn.TurnMod;
import mage.util.CardUtil;
/**
*
* @author Mael
*/
public class SkipNextTurnSourceEffect extends OneShotEffect {
int numberOfTurns;
public SkipNextTurnSourceEffect() {
this(1);
}
public SkipNextTurnSourceEffect(int numberOfTurns) {
super(Outcome.Neutral);
staticText = "You skip your next turn";
this.numberOfTurns = numberOfTurns;
staticText = "you skip your next " + (numberOfTurns == 1 ? "turn" : CardUtil.numberToText(numberOfTurns) + " turns");
}
public SkipNextTurnSourceEffect(final SkipNextTurnSourceEffect effect) {
super(effect);
this.numberOfTurns = effect.numberOfTurns;
}
@Override
@ -33,7 +42,9 @@ public class SkipNextTurnSourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), true));
for (int i = 0; i < numberOfTurns; i++) {
game.getState().getTurnMods().add(new TurnMod(source.getControllerId(), true));
}
return true;
}
}

View file

@ -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);

View file

@ -117,16 +117,11 @@ public abstract class ExpansionSet implements Serializable {
// if the packs are too big, it removes the first cards.
// since it adds lands then commons before uncommons
// and rares this should be the least disruptive.
List<Card> theBooster = this.createBooster();
List<CardInfo> commons = getCardsByRarity(Rarity.COMMON);
int iterations = 0;
while (15 > theBooster.size() && !commons.isEmpty()) {
while (15 > theBooster.size()) {
addToBooster(theBooster, commons);
iterations++;
if (iterations > 14) {
break;
}
}
while (theBooster.size() > 15) {
@ -301,9 +296,8 @@ public abstract class ExpansionSet implements Serializable {
savedCardsInfos = CardRepository.instance.findCards(criteria);
savedCards.put(rarity, savedCardsInfos);
}
List<CardInfo> cards = new ArrayList<>();
cards.addAll(savedCardsInfos);
return cards;
// Return a copy of the saved cards information, as not to modify the original.
return new ArrayList<>(savedCardsInfos);
}
public List<CardInfo> getSpecialCommon() {

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.constants;
/**
@ -33,15 +32,17 @@ package mage.constants;
* @author LevelX2
*/
public enum AbilityWord {
BLOODRUSH("Bloodrush"),
CONSTELLATION("Constellation"),
FEROCIOUS("Ferocious"),
FEROCIOUS("Ferocious"),
FORMIDABLE("Formidable"),
GRANDEUR("Grandeur"),
HELLBENT("Hellbent"),
HEROIC("Heroic"),
FORMIDABLE("Formidable"),
LANDFALL("Landfall"),
METALCRAFT("Metalcraft"),
GRANDEUR("Grandeur"),
PARLEY("Parley"),
RAID("Raid");
private final String text;

View file

@ -174,7 +174,7 @@ public abstract class GameImpl implements Game, Serializable {
protected GameState state;
private transient Stack<Integer> savedStates = new Stack<>();
protected transient GameStates gameStates = new GameStates();
// game states to allow player roll back
// game states to allow player rollback
protected transient Map<Integer, GameState> gameStatesRollBack = new HashMap<>();
protected boolean executingRollback;
@ -700,7 +700,6 @@ public abstract class GameImpl implements Game, Serializable {
if (!playTurn(playerByOrder)) {
break;
}
state.setTurnNum(state.getTurnNum() + 1);
}
playExtraTurns();
playerByOrder = playerList.getNext(this);
@ -741,7 +740,6 @@ public abstract class GameImpl implements Game, Serializable {
informPlayers(extraPlayer.getLogName() + " takes an extra turn");
}
playTurn(extraPlayer);
state.setTurnNum(state.getTurnNum() + 1);
}
}
extraTurn = getNextExtraTurn();
@ -768,6 +766,7 @@ public abstract class GameImpl implements Game, Serializable {
}
private boolean playTurn(Player player) {
boolean skipTurn = false;
do {
if (executingRollback) {
executingRollback = false;
@ -781,41 +780,23 @@ public abstract class GameImpl implements Game, Serializable {
state.setActivePlayerId(player.getId());
saveRollBackGameState();
}
this.logStartOfTurn(player);
if (checkStopOnTurnOption()) {
return false;
}
state.getTurn().play(this, player);
skipTurn = state.getTurn().play(this, player);
} while (executingRollback);
if (isPaused() || gameOver(null)) {
return false;
}
endOfTurn();
if (!skipTurn) {
endOfTurn();
state.setTurnNum(state.getTurnNum() + 1);
}
return true;
}
private void logStartOfTurn(Player player) {
StringBuilder sb = new StringBuilder("Turn ").append(state.getTurnNum()).append(" ");
sb.append(player.getLogName());
sb.append(" (");
int delimiter = this.getPlayers().size() - 1;
for (Player gamePlayer : this.getPlayers().values()) {
sb.append(gamePlayer.getLife());
int poison = gamePlayer.getCounters().getCount(CounterType.POISON);
if (poison > 0) {
sb.append("[P:").append(poison).append("]");
}
if (delimiter > 0) {
sb.append(" - ");
delimiter--;
}
}
sb.append(")");
fireStatusEvent(sb.toString(), true);
}
private boolean checkStopOnTurnOption() {
if (gameOptions.stopOnTurn != null && gameOptions.stopAtStep == PhaseStep.UNTAP) {
if (gameOptions.stopOnTurn.equals(state.getTurnNum())) {

View file

@ -4,8 +4,8 @@ import java.io.Serializable;
import mage.constants.PhaseStep;
/**
* Game options for Mage game.
* Mainly used in tests to configure {@link GameImpl} with specific params.
* Game options for Mage game. Mainly used in tests to configure
* {@link GameImpl} with specific params.
*
* @author ayratn
*/
@ -23,8 +23,8 @@ public class GameOptions implements Serializable {
public boolean testMode = false;
/**
* Defines the turn number game should stop on.
* By default, is null meaning that game shouldn't stop on any specific turn.
* Defines the turn number game should stop on. By default, is null meaning
* that game shouldn't stop on any specific turn.
*/
public Integer stopOnTurn = null;
@ -37,9 +37,9 @@ public class GameOptions implements Serializable {
* If true, library won't be shuffled at the beginning of the game
*/
public boolean skipInitShuffling = false;
/**
* If true, players can roll back turn if all players agree
* If true, players can rollback turn if all players agree
*/
public boolean rollbackTurnsAllowed = true;
}

View file

@ -41,6 +41,7 @@ import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.DelayedTriggeredAbilities;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.MageSingleton;
import mage.abilities.Mode;
import mage.abilities.SpecialActions;
import mage.abilities.StaticAbility;
@ -856,13 +857,19 @@ public class GameState implements Serializable, Copyable<GameState> {
* @param ability
*/
public void addOtherAbility(Card attachedTo, Ability ability) {
ability.setSourceId(attachedTo.getId());
ability.setControllerId(attachedTo.getOwnerId());
Ability newAbility;
if (ability instanceof MageSingleton) {
newAbility = ability;
} else {
newAbility = ability.copy();
}
newAbility.setSourceId(attachedTo.getId());
newAbility.setControllerId(attachedTo.getOwnerId());
if (!cardState.containsKey(attachedTo.getId())) {
cardState.put(attachedTo.getId(), new CardState());
}
cardState.get(attachedTo.getId()).addAbility(ability);
addAbility(ability, attachedTo.getId(), attachedTo);
cardState.get(attachedTo.getId()).addAbility(newAbility);
addAbility(newAbility, attachedTo.getId(), attachedTo);
}
/**

View file

@ -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;

View file

@ -119,7 +119,20 @@ public class GameEvent implements Serializable {
*/
SPELL_CAST,
ACTIVATE_ABILITY, ACTIVATED_ABILITY,
ADD_MANA, MANA_ADDED,
/* ADD_MANA
targetId id of the ability that added the mana
sourceId sourceId of the ability that added the mana
playerId player the mana is added to the mana pool for
mana the mana added
*/
ADD_MANA,
/* MANA_ADDED
targetId id of the ability that added the mana
sourceId sourceId of the ability that added the mana
playerId player the mana is added to the mana pool for
mana the mana added
*/
MANA_ADDED,
/* MANA_PAYED
targetId id if the ability the mana was paid for (not the sourceId)
sourceId sourceId of the mana source

View file

@ -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);
}

View file

@ -899,10 +899,10 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
}
@Override
public void entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent) {
public boolean entersBattlefield(UUID sourceId, Game game, Zone fromZone, boolean fireEvent) {
controlledFromStartOfControllerTurn = false;
if (this.isFaceDown(game)) {
// remove some attributes here, bceause first apply effects comes later otherwise abilities (e.g. color related) will unintended trigger
// remove some attributes here, because first apply effects comes later otherwise abilities (e.g. color related) will unintended trigger
MorphAbility.setPermanentToFaceDownCreature(this);
}
EntersTheBattlefieldEvent event = new EntersTheBattlefieldEvent(this, sourceId, getControllerId(), fromZone);
@ -914,7 +914,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
game.addSimultaneousEvent(event);
}
}
return true;
}
return false;
}
@Override

View file

@ -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");
}
}
}

View file

@ -34,6 +34,7 @@ import java.util.List;
import java.util.UUID;
import mage.constants.PhaseStep;
import mage.constants.TurnPhase;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
@ -116,16 +117,24 @@ public class Turn implements Serializable {
return null;
}
public void play(Game game, Player activePlayer) {
/**
*
* @param game
* @param activePlayer
* @return true if turn is skipped
*/
public boolean play(Game game, Player activePlayer) {
activePlayer.becomesActivePlayer();
this.setDeclareAttackersStepStarted(false);
if (game.isPaused() || game.gameOver(null)) {
return;
return false;
}
if (game.getState().getTurnMods().skipTurn(activePlayer.getId())) {
return;
game.informPlayers(activePlayer.getLogName() + " skips his or her turn.");
return true;
}
logStartOfTurn(game, activePlayer);
checkTurnIsControlledByOtherPlayer(game, activePlayer.getId());
@ -134,7 +143,7 @@ public class Turn implements Serializable {
game.getPlayer(activePlayer.getId()).beginTurn(game);
for (Phase phase : phases) {
if (game.isPaused() || game.gameOver(null)) {
return;
return false;
}
if (!isEndTurnRequested() || phase.getType().equals(TurnPhase.END)) {
currentPhase = phase;
@ -142,7 +151,7 @@ public class Turn implements Serializable {
if (!game.getState().getTurnMods().skipPhase(activePlayer.getId(), currentPhase.getType())) {
if (phase.play(game, activePlayer.getId())) {
if (game.executingRollback()) {
return;
return false;
}
//20091005 - 500.4/703.4n
game.emptyManaPools();
@ -155,7 +164,7 @@ public class Turn implements Serializable {
}
}
}
return false;
}
public void resumePlay(Game game, boolean wasPaused) {
@ -327,4 +336,23 @@ public class Turn implements Serializable {
return sb.toString();
}
private void logStartOfTurn(Game game, Player player) {
StringBuilder sb = new StringBuilder("Turn ").append(game.getState().getTurnNum()).append(" ");
sb.append(player.getLogName());
sb.append(" (");
int delimiter = game.getPlayers().size() - 1;
for (Player gamePlayer : game.getPlayers().values()) {
sb.append(gamePlayer.getLife());
int poison = gamePlayer.getCounters().getCount(CounterType.POISON);
if (poison > 0) {
sb.append("[P:").append(poison).append("]");
}
if (delimiter > 0) {
sb.append(" - ");
delimiter--;
}
}
sb.append(")");
game.fireStatusEvent(sb.toString(), true);
}
}

View file

@ -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.players;
import java.io.Serializable;
@ -63,7 +62,7 @@ public class ManaPool implements Serializable {
private boolean autoPayment; // auto payment from mana pool: true - mode is active
private boolean autoPaymentRestricted; // auto payment from mana pool: true - if auto Payment is on, it will only pay if one kind of mana is in the pool
private ManaType unlockedManaType; // type of mana that was selected to pay manually
private final Set<ManaType> doNotEmptyManaTypes = new HashSet<>();
public ManaPool(UUID playerId) {
@ -75,7 +74,7 @@ public class ManaPool implements Serializable {
public ManaPool(final ManaPool pool) {
this.playerId = pool.playerId;
for (ManaPoolItem item: pool.manaItems) {
for (ManaPoolItem item : pool.manaItems) {
manaItems.add(item.copy());
}
this.autoPayment = pool.autoPayment;
@ -105,24 +104,24 @@ public class ManaPool implements Serializable {
}
/**
*
*
* @param manaType the mana type that should be paid
* @param ability
* @param filter
* @param game
* @return
* @return
*/
public boolean pay(ManaType manaType, Ability ability, Filter filter, Game game) {
if (!autoPayment && !manaType.equals(unlockedManaType)) {
// if manual payment and the needed mana type was not unlocked, nothing will be paid
return false;
}
if (autoPayment && autoPaymentRestricted && !wasManaAddedBeyondStock() && !manaType.equals(unlockedManaType)) {
if (autoPayment && autoPaymentRestricted && !wasManaAddedBeyondStock() && !manaType.equals(unlockedManaType)) {
// if automatic restricted payment and there is laready mana in the pool
// and the needed mana type was not unlocked, nothing will be paid
return false;
}
if (getConditional(manaType, ability, filter, game) > 0) {
removeConditional(manaType, ability, game);
lockManaType(); // pay only one mana if mana payment is set to manually
@ -134,7 +133,7 @@ public class ManaPool implements Serializable {
if (!filter.match(sourceObject, game)) {
continue;
}
}
}
if (!manaType.equals(unlockedManaType) && autoPayment && autoPaymentRestricted && mana.count() == mana.getStock()) {
// no mana added beyond the stock so don't auto pay this
continue;
@ -202,7 +201,7 @@ public class ManaPool implements Serializable {
public void clearEmptyManaPoolRules() {
doNotEmptyManaTypes.clear();
}
public void addDoNotEmptyManaType(ManaType manaType) {
doNotEmptyManaTypes.add(manaType);
}
@ -217,7 +216,7 @@ public class ManaPool implements Serializable {
if (!doNotEmptyManaTypes.contains(manaType)) {
if (item.get(manaType) > 0) {
if (!item.getDuration().equals(Duration.EndOfTurn) || game.getPhase().getType().equals(TurnPhase.END)) {
if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) {
if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) {
int amount = item.get(manaType);
item.clear(manaType);
item.add(ManaType.COLORLESS, amount);
@ -230,7 +229,7 @@ public class ManaPool implements Serializable {
if (conditionalItem != null) {
if (conditionalItem.get(manaType) > 0) {
if (!item.getDuration().equals(Duration.EndOfTurn) || game.getPhase().getType().equals(TurnPhase.END)) {
if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) {
if (game.replaceEvent(new GameEvent(GameEvent.EventType.EMPTY_MANA_POOL, playerId, null, playerId))) {
int amount = conditionalItem.get(manaType);
conditionalItem.clear(manaType);
conditionalItem.add(ManaType.COLORLESS, amount);
@ -246,8 +245,8 @@ public class ManaPool implements Serializable {
if (item.count() == 0) {
it.remove();
}
}
return total;
}
return total;
}
private int payX(Ability ability, Game game) {
@ -261,8 +260,7 @@ public class ManaPool implements Serializable {
total += item.count();
it.remove();
}
}
else {
} else {
total += item.count();
it.remove();
}
@ -272,10 +270,11 @@ public class ManaPool implements Serializable {
/**
* remove all mana from pool that applies and that matches filter
*
* @param ability
* @param game
* @param filter
* @return
* @return
*/
public int payX(Ability ability, Game game, FilterMana filter) {
if (filter == null) {
@ -297,8 +296,7 @@ public class ManaPool implements Serializable {
}
}
}
}
else {
} else {
if (filter.isBlack()) {
total += item.getBlack();
item.removeBlack();
@ -333,7 +331,7 @@ public class ManaPool implements Serializable {
public Mana getMana() {
Mana m = new Mana();
for (ManaPoolItem item: manaItems) {
for (ManaPoolItem item : manaItems) {
m.add(item.getMana());
}
return m;
@ -375,12 +373,12 @@ public class ManaPool implements Serializable {
public void addMana(Mana manaToAdd, Game game, Ability source) {
addMana(manaToAdd, game, source, false);
}
public void addMana(Mana manaToAdd, Game game, Ability source, boolean emptyOnTurnsEnd) {
Mana mana = manaToAdd.copy();
if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), source.getControllerId(), mana))) {
if (!game.replaceEvent(new ManaEvent(EventType.ADD_MANA, source.getId(), source.getSourceId(), playerId, mana))) {
if (mana instanceof ConditionalMana) {
ManaPoolItem item = new ManaPoolItem((ConditionalMana)mana, source.getSourceId(), source.getOriginalId());
ManaPoolItem item = new ManaPoolItem((ConditionalMana) mana, source.getSourceId(), source.getOriginalId());
if (emptyOnTurnsEnd) {
item.setDuration(Duration.EndOfTurn);
}
@ -389,18 +387,18 @@ public class ManaPool implements Serializable {
ManaPoolItem item = new ManaPoolItem(mana.getRed(), mana.getGreen(), mana.getBlue(), mana.getWhite(), mana.getBlack(), mana.getColorless(), source.getSourceId(), source.getOriginalId(), mana.getFlag());
if (emptyOnTurnsEnd) {
item.setDuration(Duration.EndOfTurn);
}
}
this.manaItems.add(item);
}
GameEvent event = GameEvent.getEvent(GameEvent.EventType.MANA_ADDED, source.getId(), source.getSourceId(), source.getControllerId());
event.setData(mana.toString());
game.fireEvent(event);
ManaEvent manaEvent = new ManaEvent(EventType.MANA_ADDED, source.getId(), source.getSourceId(), playerId, mana);
manaEvent.setData(mana.toString());
game.fireEvent(manaEvent);
}
}
public List<ConditionalMana> getConditionalMana() {
List<ConditionalMana> conditionalMana = new ArrayList<>();
for (ManaPoolItem item: manaItems) {
for (ManaPoolItem item : manaItems) {
if (item.isConditional()) {
conditionalMana.add(item.getConditionalMana());
}
@ -410,7 +408,7 @@ public class ManaPool implements Serializable {
public int count() {
int x = 0;
for (ManaPoolItem item: manaItems) {
for (ManaPoolItem item : manaItems) {
x += item.count();
}
return x;
@ -465,7 +463,7 @@ public class ManaPool implements Serializable {
mana.setStock(mana.count());
}
}
private boolean wasManaAddedBeyondStock() {
for (ManaPoolItem mana : manaItems) {
if (mana.getStock() < mana.count()) {

View file

@ -39,6 +39,7 @@ public class GameLog {
static final String LOG_COLOR_PLAYER = "#20B2AA"; // LightSeaGreen
static final String LOG_COLOR_PLAYER_REQUEST = "#D2691E"; // Chocolate
static final String LOG_COLOR_PLAYER_CONFIRM = "#D2691E"; // Chocolate
// colors for more dark background
static final String LOG_COLOR_GREEN = "#90EE90"; // LightGreen
static final String LOG_COLOR_RED = "#FF6347"; // Tomato
static final String LOG_COLOR_BLUE = "#87CEFA"; // LightSkyBlue
@ -46,6 +47,14 @@ public class GameLog {
static final String LOG_COLOR_WHITE = "#F0E68C"; // Khaki
static final String LOG_COLOR_MULTI = "#DAA520"; // GoldenRod
static final String LOG_COLOR_COLORLESS = "#B0C4DE"; // LightSteelBlue
// colors for tooltip (light background)
static final String LOG_TT_COLOR_RED = "Red"; // Tomato
static final String LOG_TT_COLOR_GREEN = "Green"; // LightGreen
static final String LOG_TT_COLOR_BLUE = "Blue";
static final String LOG_TT_COLOR_BLACK = "Black";
static final String LOG_TT_COLOR_WHITE = "#CCDB00";
static final String LOG_TT_COLOR_MULTI = "#FFAC40";
static final String LOG_TT_COLOR_COLORLESS = "#94A4BA";
static final String LOG_COLOR_NEUTRAL = "#F0F8FF"; // AliceBlue
public static String replaceNameByColoredName(MageObject mageObject, String text) {
@ -60,6 +69,10 @@ public class GameLog {
return "<font color=\'" + getColorName(mageObject.getColor(null)) + "\'>" + mageObject.getName() + " [" + mageObject.getId().toString().substring(0, 3) + "]</font>";
}
public static String getColoredObjectIdNameForTooltip(MageObject mageObject) {
return "<font color=\'" + getTooltipColorName(mageObject.getColor(null)) + "\'>" + mageObject.getName() + " [" + mageObject.getId().toString().substring(0, 3) + "]</font>";
}
public static String getNeutralColoredText(String text) {
return "<font color=\'" + LOG_COLOR_NEUTRAL + "\'>" + text + "</font>";
}
@ -97,4 +110,22 @@ public class GameLog {
return LOG_COLOR_BLACK;
}
}
private static String getTooltipColorName(ObjectColor objectColor) {
if (objectColor.isMulticolored()) {
return LOG_TT_COLOR_MULTI;
} else if (objectColor.isColorless()) {
return LOG_TT_COLOR_COLORLESS;
} else if (objectColor.isRed()) {
return LOG_TT_COLOR_RED;
} else if (objectColor.isGreen()) {
return LOG_TT_COLOR_GREEN;
} else if (objectColor.isBlue()) {
return LOG_TT_COLOR_BLUE;
} else if (objectColor.isWhite()) {
return LOG_TT_COLOR_WHITE;
} else {
return LOG_TT_COLOR_BLACK;
}
}
}