Merge branch 'master' into JaceArchitectOfThoughtFix

This commit is contained in:
LevelX2 2018-11-26 17:29:31 +01:00 committed by GitHub
commit c4cd4b8b6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
320 changed files with 14921 additions and 4691 deletions

View file

@ -479,16 +479,7 @@ public interface Ability extends Controllable, Serializable {
boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game);
/**
* Sets the object that actually existed while a ability triggerd or an
* ability was activated.
*
* @param mageObject
* @param game
*/
void setSourceObject(MageObject mageObject, Game game);
/**
* Returns the object that actually existed while a ability triggerd or an
* Returns the object that actually existed while a ability triggered or an
* ability was activated. If not set yet, the current object will be
* retrieved from the game.
*
@ -497,6 +488,8 @@ public interface Ability extends Controllable, Serializable {
*/
MageObject getSourceObject(Game game);
void setSourceObjectZoneChangeCounter(int zoneChangeCounter);
int getSourceObjectZoneChangeCounter();
/**
@ -520,6 +513,8 @@ public interface Ability extends Controllable, Serializable {
*/
Permanent getSourcePermanentIfItStillExists(Game game);
Permanent getSourcePermanentOrLKI(Game game);
String getTargetDescription(Targets targets, Game game);
void setCanFizzle(boolean canFizzle);

View file

@ -5,7 +5,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.Mana;
import mage.abilities.costs.*;
import mage.abilities.costs.common.PayLifeCost;
@ -15,8 +14,8 @@ import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.mana.DynamicManaEffect;
import mage.abilities.effects.common.ManaEffect;
import mage.abilities.effects.mana.DynamicManaEffect;
import mage.abilities.mana.ActivatedManaAbilityImpl;
import mage.cards.Card;
import mage.cards.SplitCard;
@ -67,7 +66,6 @@ public abstract class AbilityImpl implements Ability {
protected boolean costModificationActive = true;
protected boolean activated = false;
protected boolean worksFaceDown = false;
protected MageObject sourceObject;
protected int sourceObjectZoneChangeCounter;
protected List<Watcher> watchers = new ArrayList<>();
protected List<Ability> subAbilities = null;
@ -116,7 +114,6 @@ public abstract class AbilityImpl implements Ability {
this.costModificationActive = ability.costModificationActive;
this.worksFaceDown = ability.worksFaceDown;
this.abilityWord = ability.abilityWord;
this.sourceObject = ability.sourceObject;
this.sourceObjectZoneChangeCounter = ability.sourceObjectZoneChangeCounter;
this.canFizzle = ability.canFizzle;
this.targetAdjuster = ability.targetAdjuster;
@ -131,8 +128,6 @@ public abstract class AbilityImpl implements Ability {
public void newId() {
if (!(this instanceof MageSingleton)) {
this.id = UUID.randomUUID();
// this.sourceObject = null;
// this.sourceObjectZoneChangeCounter = -1;
}
getEffects().newId();
}
@ -226,8 +221,10 @@ public abstract class AbilityImpl implements Ability {
return false;
}
getSourceObject(game);
MageObject sourceObject = getSourceObject(game);
if (getSourceObjectZoneChangeCounter() == 0) {
setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(getSourceId()));
}
if (controller.isTestMode()) {
if (!controller.addTargets(this, game)) {
return false;
@ -1160,58 +1157,44 @@ public abstract class AbilityImpl implements Ability {
@Override
public MageObject getSourceObject(Game game) {
if (sourceObject == null) {
setSourceObject(null, game);
if (sourceObject == null) {
logger.warn("Source object could not be retrieved: " + this.getRule());
}
}
return sourceObject;
return game.getObject(getSourceId());
}
@Override
public MageObject getSourceObjectIfItStillExists(Game game) {
MageObject currentObject = game.getObject(getSourceId());
if (currentObject != null) {
if (sourceObject == null) {
setSourceObject(currentObject, game);
}
MageObjectReference mor = new MageObjectReference(currentObject, game);
if (mor.getZoneChangeCounter() == getSourceObjectZoneChangeCounter()) {
// source object has meanwhile not changed zone
return currentObject;
}
if (getSourceObjectZoneChangeCounter() == 0
|| getSourceObjectZoneChangeCounter() == game.getState().getZoneChangeCounter(getSourceId())) {
return game.getObject(getSourceId());
}
return null;
}
@Override
public Permanent getSourcePermanentIfItStillExists(Game game) {
if (sourceObject == null || !sourceObject.getId().equals(getSourceId())) {
setSourceObject(game.getObject(getSourceId()), game);
}
if (sourceObject instanceof Permanent) {
if (game.getState().getZoneChangeCounter(getSourceId()) == getSourceObjectZoneChangeCounter()) {
return (Permanent) sourceObject;
}
MageObject mageObject = getSourceObjectIfItStillExists(game);
if (mageObject instanceof Permanent) {
return (Permanent) mageObject;
}
return null;
}
@Override
public int getSourceObjectZoneChangeCounter() {
return sourceObjectZoneChangeCounter;
public Permanent getSourcePermanentOrLKI(Game game) {
if (getSourceObjectZoneChangeCounter() == 0
|| getSourceObjectZoneChangeCounter() == game.getState().getZoneChangeCounter(getSourceId())) {
return game.getPermanent(getSourceId());
}
return (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD, getSourceObjectZoneChangeCounter());
}
@Override
public void setSourceObject(MageObject sourceObject, Game game) {
if (sourceObject == null) {
this.sourceObject = game.getObject(sourceId);
this.sourceObjectZoneChangeCounter = game.getState().getZoneChangeCounter(sourceId);
} else {
this.sourceObject = sourceObject;
this.sourceObjectZoneChangeCounter = this.sourceObject.getZoneChangeCounter(game);
}
public void setSourceObjectZoneChangeCounter(int sourceObjectZoneChangeCounter) {
this.sourceObjectZoneChangeCounter = sourceObjectZoneChangeCounter;
}
@Override
public int getSourceObjectZoneChangeCounter() {
return sourceObjectZoneChangeCounter;
}
@Override

View file

@ -219,9 +219,9 @@ public abstract class ActivatedAbilityImpl extends AbilityImpl implements Activa
} else {
MageObject mageObject = game.getObject(this.sourceId);
if (mageObject instanceof Emblem) {
return ((Emblem) mageObject).getControllerId().equals(playerId);
return ((Emblem) mageObject).isControlledBy(playerId);
} else if (mageObject instanceof Plane) {
return ((Plane) mageObject).getControllerId().equals(playerId);
return ((Plane) mageObject).isControlledBy(playerId);
} else if (game.getState().getZone(this.sourceId) != Zone.BATTLEFIELD) {
return ((Card) mageObject).isOwnedBy(playerId);
}

View file

@ -1,4 +1,3 @@
package mage.abilities;
import java.util.UUID;
@ -31,7 +30,10 @@ public class PlayLandAbility extends ActivatedAbilityImpl {
return ActivationStatus.getFalse();
}
//20091005 - 114.2a
return new ActivationStatus(game.isActivePlayer(playerId) && game.getPlayer(playerId).canPlayLand() && game.canPlaySorcery(playerId), permittingObject);
return new ActivationStatus(game.isActivePlayer(playerId)
&& game.getPlayer(playerId).canPlayLand()
&& game.canPlaySorcery(playerId),
permittingObject);
}
@Override

View file

@ -1,4 +1,3 @@
package mage.abilities;
import java.util.Locale;
@ -45,9 +44,6 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge
@Override
public void trigger(Game game, UUID controllerId) {
//20091005 - 603.4
if (!(this instanceof DelayedTriggeredAbility)) {
setSourceObject(null, game);
}
if (checkInterveningIfClause(game)) {
game.addTriggeredAbility(this);
}

View file

@ -0,0 +1,50 @@
/*
* 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.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
/**
*
* @author jeffwadsworth
*/
public class ControllerPlaysLandTriggeredAbility extends TriggeredAbilityImpl {
public ControllerPlaysLandTriggeredAbility(Zone zone, Effect effect, Boolean optional) {
super(zone, effect, optional);
}
public ControllerPlaysLandTriggeredAbility(ControllerPlaysLandTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.LAND_PLAYED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent land = game.getPermanent(event.getTargetId());
return land.getControllerId().equals(controllerId);
}
@Override
public ControllerPlaysLandTriggeredAbility copy() {
return new ControllerPlaysLandTriggeredAbility(this);
}
@Override
public String getRule() {
return "Whenever you play a land, ";
}
}

View file

@ -1,123 +0,0 @@
package mage.abilities.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.target.Target;
import mage.target.TargetPlayer;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author North
*/
public class CreatureEntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl {
private boolean opponentController;
protected FilterCreaturePermanent filter = new FilterCreaturePermanent();
/**
* optional = false<br>
* opponentController = false
*
* @param effect
*/
public CreatureEntersBattlefieldTriggeredAbility(Effect effect) {
this(effect, false, false);
}
/**
* opponentController = false
*
* @param effect
* @param optional
*/
public CreatureEntersBattlefieldTriggeredAbility(Effect effect, boolean optional) {
this(effect, optional, false);
}
/**
*
* @param effect
* @param optional
* @param opponentController
*/
public CreatureEntersBattlefieldTriggeredAbility(Effect effect, boolean optional, boolean opponentController) {
this(Zone.BATTLEFIELD, effect, optional, opponentController);
}
/**
* @param zone
* @param effect
* @param optional
* @param opponentController
*/
public CreatureEntersBattlefieldTriggeredAbility(Zone zone, Effect effect, boolean optional, boolean opponentController) {
this(zone, effect, null, optional, opponentController);
}
/**
* @param zone
* @param effect
* @param filter filter the triggering creatures
* @param optional
* @param opponentController
*/
public CreatureEntersBattlefieldTriggeredAbility(Zone zone, Effect effect, FilterCreaturePermanent filter, boolean optional, boolean opponentController) {
super(zone, effect, optional);
this.opponentController = opponentController;
if (filter != null) {
this.filter = filter;
}
}
public CreatureEntersBattlefieldTriggeredAbility(CreatureEntersBattlefieldTriggeredAbility ability) {
super(ability);
this.opponentController = ability.opponentController;
this.filter = ability.filter;
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.ENTERS_THE_BATTLEFIELD;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget();
if (filter.match(permanent, sourceId, controllerId, game)
&& (permanent.isControlledBy(this.controllerId) ^ opponentController)) {
if (!this.getTargets().isEmpty()) {
Target target = this.getTargets().get(0);
if (target instanceof TargetPlayer) {
target.add(permanent.getControllerId(), game);
}
if (target instanceof TargetCreaturePermanent) {
target.add(event.getTargetId(), game);
}
}
return true;
}
return false;
}
@Override
public String getRule() {
return "Whenever a " + filter.getMessage() + " enters the battlefield under "
+ (opponentController ? "an opponent's control, " : "your control, ")
+ super.getRule();
}
@Override
public CreatureEntersBattlefieldTriggeredAbility copy() {
return new CreatureEntersBattlefieldTriggeredAbility(this);
}
}

View file

@ -13,7 +13,6 @@ import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author LevelX2
*/
public class DealsDamageToAPlayerAllTriggeredAbility extends TriggeredAbilityImpl {
@ -28,7 +27,11 @@ public class DealsDamageToAPlayerAllTriggeredAbility extends TriggeredAbilityImp
}
public DealsDamageToAPlayerAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, boolean onlyCombat, boolean affectsDefendingPlayer) {
super(Zone.BATTLEFIELD, effect, optional);
this(Zone.BATTLEFIELD, effect, filter, optional, setTargetPointer, onlyCombat, affectsDefendingPlayer);
}
public DealsDamageToAPlayerAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, boolean onlyCombat, boolean affectsDefendingPlayer) {
super(zone, effect, optional);
this.setTargetPointer = setTargetPointer;
this.filter = filter;
this.onlyCombat = onlyCombat;
@ -57,27 +60,25 @@ public class DealsDamageToAPlayerAllTriggeredAbility extends TriggeredAbilityImp
public boolean checkTrigger(GameEvent event, Game game) {
if (!onlyCombat || ((DamagedPlayerEvent) event).isCombatDamage()) {
Permanent permanent = game.getPermanent(event.getSourceId());
if (permanent != null) {
if (filter.match(permanent, getSourceId(), getControllerId(), game)) {
for (Effect effect : this.getEffects()) {
effect.setValue("damage", event.getAmount());
effect.setValue("sourceId", event.getSourceId());
if (affectsDefendingPlayer) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
continue;
}
switch (setTargetPointer) {
case PLAYER:
effect.setTargetPointer(new FixedTarget(permanent.getControllerId()));
break;
case PERMANENT:
effect.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game)));
break;
}
if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) {
for (Effect effect : this.getEffects()) {
effect.setValue("damage", event.getAmount());
effect.setValue("sourceId", event.getSourceId());
if (affectsDefendingPlayer) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
continue;
}
return true;
switch (setTargetPointer) {
case PLAYER:
effect.setTargetPointer(new FixedTarget(permanent.getControllerId()));
break;
case PERMANENT:
effect.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game)));
break;
}
}
return true;
}
}
return false;

View file

@ -0,0 +1,49 @@
/*
* 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.common;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
/**
*
* @author jeffwadsworth
*/
public class OpponentPlaysLandTriggeredAbility extends TriggeredAbilityImpl {
public OpponentPlaysLandTriggeredAbility(Zone zone, Effect effect, Boolean optional) {
super(zone, effect, optional);
}
public OpponentPlaysLandTriggeredAbility(OpponentPlaysLandTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.LAND_PLAYED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent land = game.getPermanent(event.getTargetId());
return game.getOpponents(controllerId).contains(land.getControllerId());
}
@Override
public OpponentPlaysLandTriggeredAbility copy() {
return new OpponentPlaysLandTriggeredAbility(this);
}
@Override
public String getRule() {
return "Whenever an opponent plays a land, ";
}
}

View file

@ -1,4 +1,3 @@
package mage.abilities.common;
import mage.abilities.Ability;
@ -138,7 +137,8 @@ class ChapterTriggeredAbility extends TriggeredAbilityImpl {
if (event.getTargetId().equals(getSourceId()) && event.getData().equals(CounterType.LORE.getName())) {
int amountAdded = event.getAmount();
int loreCounters = amountAdded;
Permanent sourceSaga = game.getPermanentOrLKIBattlefield(getSourceId());
//Permanent sourceSaga = game.getPermanentOrLKIBattlefield(getSourceId()); BUG #5393
Permanent sourceSaga = game.getPermanent(getSourceId());
if (sourceSaga == null) {
sourceSaga = game.getPermanentEntering(getSourceId());
}

View file

@ -1,4 +1,3 @@
package mage.abilities.common.delayed;
import java.util.LinkedHashSet;

View file

@ -0,0 +1,44 @@
package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.game.Game;
import mage.watchers.common.PlayersAttackedThisTurnWatcher;
import java.util.UUID;
/**
* @author JayDi85
*/
public class AttackedThisTurnOpponentsCount implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return this.calculate(game, sourceAbility.getControllerId());
}
public int calculate(Game game, UUID controllerId) {
PlayersAttackedThisTurnWatcher watcher = (PlayersAttackedThisTurnWatcher) game.getState().getWatchers().get(PlayersAttackedThisTurnWatcher.class.getSimpleName());
if (watcher != null) {
return watcher.getAttackedOpponentsCount(controllerId);
}
return 0;
}
@Override
public AttackedThisTurnOpponentsCount copy() {
return new AttackedThisTurnOpponentsCount();
}
@Override
public String toString() {
return "1";
}
@Override
public String getMessage() {
return "the number of opponents you attacked this turn";
}
}

View file

@ -0,0 +1,32 @@
package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.game.Game;
/**
* @author JayDi85
*/
public class OpponentsCount implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return game.getOpponents(sourceAbility.getControllerId()).size();
}
@Override
public OpponentsCount copy() {
return new OpponentsCount();
}
@Override
public String getMessage() {
return "number of opponents you have";
}
@Override
public String toString() {
return "1";
}
}

View file

@ -1,4 +1,3 @@
package mage.abilities.effects;
import mage.MageObject;
@ -47,6 +46,7 @@ public abstract class RedirectionEffect extends ReplacementEffectImpl {
this.redirectTarget = effect.redirectTarget;
this.amountToRedirect = effect.amountToRedirect;
this.usageType = effect.usageType;
this.applyEffectsCounter = effect.applyEffectsCounter;
}
@Override
@ -80,6 +80,7 @@ public abstract class RedirectionEffect extends ReplacementEffectImpl {
if (applyEffectsCounter > 0) {
if (applyEffectsCounter < game.getState().getApplyEffectsCounter()) {
this.discard();
return false;
}
} else {
applyEffectsCounter = game.getState().getApplyEffectsCounter();

View file

@ -1,12 +1,12 @@
package mage.abilities.effects.common;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.EnchantAbility;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.FilterPermanent;
@ -20,8 +20,9 @@ import mage.target.TargetPermanent;
import mage.util.functions.ApplyToPermanent;
import mage.util.functions.EmptyApplyToPermanent;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class CopyPermanentEffect extends OneShotEffect {
@ -59,7 +60,9 @@ public class CopyPermanentEffect extends OneShotEffect {
super(effect);
this.filter = effect.filter.copy();
this.applier = effect.applier;
this.bluePrintPermanent = effect.bluePrintPermanent;
if (effect.bluePrintPermanent != null) {
this.bluePrintPermanent = effect.bluePrintPermanent.copy();
}
this.useTargetOfAbility = effect.useTargetOfAbility;
}
@ -84,42 +87,70 @@ public class CopyPermanentEffect extends OneShotEffect {
}
if (copyFromPermanent != null) {
bluePrintPermanent = game.copyPermanent(copyFromPermanent, sourcePermanent.getId(), source, applier);
//if object is a copy of an aura, it needs to attach
if (bluePrintPermanent.hasSubtype(SubType.AURA, game)){
if (bluePrintPermanent == null) {
return false;
}
// if object is a copy of an aura, it needs to attach again for new target
if (bluePrintPermanent.hasSubtype(SubType.AURA, game)) {
//copied from mage.cards.c.CopyEnchantment.java
Target target = bluePrintPermanent.getSpellAbility().getTargets().get(0);
// permanent can be attached (Estrid's Mask) or enchant (Utopia Sprawl)
// TODO: fix Animate Dead -- it's can't be copied (can't retarget)
Outcome auraOutcome = Outcome.BoostCreature;
Target auraTarget = null;
// attach - search effect in spell ability (example: cast Utopia Sprawl, cast Estrid's Invocation on it)
for (Ability ability : bluePrintPermanent.getAbilities()) {
if (ability instanceof SpellAbility) {
for (Effect effect : ability.getEffects()) {
if (effect instanceof AttachEffect) {
auraOutcome = effect.getOutcome();
if (bluePrintPermanent.getSpellAbility().getTargets().size() > 0) {
auraTarget = bluePrintPermanent.getSpellAbility().getTargets().get(0);
auraOutcome = effect.getOutcome();
}
}
}
}
}
/*if this is a copy of a copy, the copy's target has been
*copied and needs to be cleared
*/
{
UUID targetId = target.getFirstTarget();
if(targetId != null)
target.remove(targetId);
// enchant - search in all abilities (example: cast Estrid's Invocation on enchanted creature by Estrid, the Masked second ability, cast Estrid's Invocation on it)
if (auraTarget == null) {
for (Ability ability : bluePrintPermanent.getAbilities()) {
if (ability instanceof EnchantAbility) {
if (ability.getTargets().size() > 0) { // Animate Dead don't have targets
auraTarget = ability.getTargets().get(0);
for (Effect effect : ability.getEffects()) {
// first outcome
auraOutcome = effect.getOutcome();
}
}
}
}
}
target.setNotTarget(true);
if (controller.choose(auraOutcome, target, source.getSourceId(), game)) {
UUID targetId = target.getFirstTarget();
Permanent targetPermanent = game.getPermanent(targetId);
Player targetPlayer = game.getPlayer(targetId);
if (targetPermanent != null) {
targetPermanent.addAttachment(sourcePermanent.getId(), game);
} else if (targetPlayer != null) {
targetPlayer.addAttachment(sourcePermanent.getId(), game);
} else {
return false;
/* if this is a copy of a copy, the copy's target has been
* copied and needs to be cleared
*/
if (auraTarget != null) {
// clear selected target
if (auraTarget.getFirstTarget() != null) {
auraTarget.remove(auraTarget.getFirstTarget());
}
// select new target
auraTarget.setNotTarget(true);
if (controller.choose(auraOutcome, auraTarget, source.getSourceId(), game)) {
UUID targetId = auraTarget.getFirstTarget();
Permanent targetPermanent = game.getPermanent(targetId);
Player targetPlayer = game.getPlayer(targetId);
if (targetPermanent != null) {
targetPermanent.addAttachment(sourcePermanent.getId(), game);
} else if (targetPlayer != null) {
targetPlayer.addAttachment(sourcePermanent.getId(), game);
} else {
return false;
}
}
}
}

View file

@ -1,4 +1,3 @@
package mage.abilities.effects.common;
import java.util.UUID;
@ -6,6 +5,8 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects;
@ -22,15 +23,22 @@ import mage.util.CardUtil;
public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect {
protected Effects executingEffects = new Effects();
private final Cost cost;
protected Cost cost;
private String chooseUseText;
protected DynamicValue genericMana;
public DoUnlessAnyPlayerPaysEffect(Effect effect, DynamicValue genericMana) {
super(Outcome.Detriment);
this.genericMana = genericMana;
this.executingEffects.add(effect);
}
public DoUnlessAnyPlayerPaysEffect(Effect effect, Cost cost) {
this(effect, cost, null);
}
public DoUnlessAnyPlayerPaysEffect(Effect effect, Cost cost, String chooseUseText) {
super(Outcome.Benefit);
super(Outcome.Neutral);
this.executingEffects.add(effect);
this.cost = cost;
this.chooseUseText = chooseUseText;
@ -38,8 +46,13 @@ public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect {
public DoUnlessAnyPlayerPaysEffect(final DoUnlessAnyPlayerPaysEffect effect) {
super(effect);
if (effect.cost != null) {
this.cost = effect.cost.copy();
}
if (effect.genericMana != null) {
this.genericMana = effect.genericMana.copy();
}
this.executingEffects = effect.executingEffects.copy();
this.cost = effect.cost.copy();
this.chooseUseText = effect.chooseUseText;
}
@ -51,11 +64,18 @@ public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (controller != null && sourceObject != null) {
Cost costToPay;
if (controller != null
&& sourceObject != null) {
if (cost != null) {
costToPay = cost.copy();
} else {
costToPay = new GenericManaCost(genericMana.calculate(game, source, this));
}
String message;
if (chooseUseText == null) {
String effectText = executingEffects.getText(source.getModes().getMode());
message = "Pay " + cost.getText() + " to prevent (" + effectText.substring(0, effectText.length() - 1) + ")?";
message = "Pay " + costToPay.getText() + " to prevent (" + effectText.substring(0, effectText.length() - 1) + ")?";
} else {
message = chooseUseText;
}
@ -65,9 +85,10 @@ public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect {
// check if any player is willing to pay
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);
if (player != null && cost.canPay(source, source.getSourceId(), player.getId(), game) && player.chooseUse(Outcome.Detriment, message, source, game)) {
cost.clearPaid();
if (cost.pay(source, game, source.getSourceId(), player.getId(), false, null)) {
if (player != null
&& costToPay.canPay(source, source.getSourceId(), player.getId(), game) && player.chooseUse(Outcome.Detriment, message, source, game)) {
costToPay.clearPaid();
if (costToPay.pay(source, game, source.getSourceId(), player.getId(), false, null)) {
if (!game.isSimulation()) {
game.informPlayers(player.getLogName() + " pays the cost to prevent the effect");
}
@ -100,8 +121,18 @@ public class DoUnlessAnyPlayerPaysEffect extends OneShotEffect {
if (!staticText.isEmpty()) {
return staticText;
}
StringBuilder sb = new StringBuilder();
if (cost != null) {
sb.append(cost.getText());
} else {
sb.append("{X}");
}
if (genericMana != null && !genericMana.getMessage().isEmpty()) {
sb.append(", where X is ");
sb.append(genericMana.getMessage());
}
String effectsText = executingEffects.getText(mode);
return effectsText.substring(0, effectsText.length() - 1) + " unless any player pays " + cost.getText();
return effectsText.substring(0, effectsText.length() - 1) + " unless any player pays " + sb.toString();
}
@Override

View file

@ -19,7 +19,7 @@ public class DontUntapInControllersUntapStepEnchantedEffect extends ContinuousRu
}
public DontUntapInControllersUntapStepEnchantedEffect(String description) {
super(Duration.WhileOnBattlefield, Outcome.Detriment, false, true);
super(Duration.WhileOnBattlefield, Outcome.Removal, false, true);
staticText = "Enchanted " + description + " doesn't untap during its controller's untap step";
}

View file

@ -3,6 +3,7 @@ package mage.abilities.effects.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.MultikickerCount;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
@ -51,7 +52,7 @@ public class DrawCardSourceControllerEffect extends OneShotEffect {
private void setText() {
StringBuilder sb = new StringBuilder();
boolean oneCard = (amount instanceof StaticValue && amount.calculate(null, null, this) == 1)
|| amount instanceof PermanentsOnBattlefieldCount || amount.toString().equals("1");
|| amount instanceof PermanentsOnBattlefieldCount || amount.toString().equals("1") || amount.toString().equals("a");
sb.append("draw ").append(oneCard ? "a" : CardUtil.numberToText(amount.toString())).append(" card");
if (!oneCard) {
sb.append('s');

View file

@ -14,7 +14,7 @@ import mage.players.Player;
public class ExileAndGainLifeEqualPowerTargetEffect extends OneShotEffect {
public ExileAndGainLifeEqualPowerTargetEffect() {
super(Outcome.GainLife);
super(Outcome.Removal);
staticText = "Exile target creature. Its controller gains life equal to its power";
}

View file

@ -19,7 +19,7 @@ import mage.util.CardUtil;
public class ExileUntilSourceLeavesEffect extends OneShotEffect {
public ExileUntilSourceLeavesEffect(String targetName) {
super(Outcome.Benefit);
super(Outcome.Removal);
this.staticText = "exile target " + targetName + " an opponent controls until {this} leaves the battlefield";
}

View file

@ -1,6 +1,6 @@
package mage.abilities.effects.common;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.OneShotEffect;
@ -32,35 +32,58 @@ public class FightTargetsEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Card card = game.getCard(source.getSourceId());
if (card != null) {
// only if both targets are legal the effect will be applied
if (source.getTargets().get(0).isLegal(source, game) && source.getTargets().get(1).isLegal(source, game)) {
Permanent creature1 = game.getPermanent(source.getTargets().get(0).getFirstTarget());
Permanent creature2 = game.getPermanent(source.getTargets().get(1).getFirstTarget());
// 20110930 - 701.10
if (creature1 != null && creature2 != null) {
if (creature1.isCreature() && creature2.isCreature()) {
return creature1.fight(creature2, source, game);
}
UUID target1Id = null;
UUID target2Id = null;
// first target is in target pointer, second target is a normal target
if (source.getTargets().size() < 2) {
if (!source.getTargets().get(0).isLegal(source, game)) {
return false;
}
target1Id = getTargetPointer().getFirst(game, source);
target2Id = source.getTargets().getFirstTarget();
if (target1Id == target2Id) {
return false;
}
// two normal targets available, only if both targets are legal the effect will be applied
} else if (source.getTargets().get(0).isLegal(source, game) && source.getTargets().get(1).isLegal(source, game)) {
target1Id = source.getTargets().get(0).getFirstTarget();
target2Id = source.getTargets().get(1).getFirstTarget();
}
Permanent creature1 = game.getPermanent(target1Id);
Permanent creature2 = game.getPermanent(target2Id);
// 20110930 - 701.10
if (creature1 != null && creature2 != null) {
if (creature1.isCreature() && creature2.isCreature()) {
return creature1.fight(creature2, source, game);
}
}
if (!game.isSimulation()) {
game.informPlayers(card.getName() + " has been fizzled.");
}
}
if (!game.isSimulation()) {
game.informPlayers(card.getName() + " has been fizzled.");
}
return false;
}
@Override
public FightTargetsEffect copy() {
public FightTargetsEffect
copy() {
return new FightTargetsEffect(this);
}
@Override
public String getText(Mode mode) {
if (staticText != null && !staticText.isEmpty()) {
public String
getText(Mode mode
) {
if (staticText
!= null && !staticText
.isEmpty()) {
return staticText;
}
return "Target " + mode.getTargets().get(0).getTargetName() + " fights another target " + mode.getTargets().get(1).getTargetName();
return "Target " + mode
.getTargets().get(0).getTargetName() + " fights another target " + mode
.getTargets().get(1).getTargetName();
}
}

View file

@ -6,6 +6,7 @@ import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.PreventionEffectImpl;
import mage.constants.Duration;
import mage.game.Game;
@ -27,6 +28,10 @@ public class PreventDamageToTargetMultiAmountEffect extends PreventionEffectImpl
super(duration, amount, false);
}
public PreventDamageToTargetMultiAmountEffect(Duration duration, int amount, boolean onlyCombat, boolean consumable, DynamicValue dynamicValue) {
super(duration, amount, onlyCombat, consumable, dynamicValue);
}
public PreventDamageToTargetMultiAmountEffect(final PreventDamageToTargetMultiAmountEffect effect) {
super(effect);
}
@ -100,12 +105,15 @@ public class PreventDamageToTargetMultiAmountEffect extends PreventionEffectImpl
@Override
public String getText(Mode mode) {
StringBuilder sb = new StringBuilder();
sb.append("prevent the next ").append(amountToPrevent).append(" damage that would be dealt ");
if (duration == Duration.EndOfTurn) {
sb.append("this turn ");
if (staticText.isEmpty()) {
sb.append("prevent the next ").append(amountToPrevent).append(" damage that would be dealt ");
if (duration == Duration.EndOfTurn) {
sb.append("this turn ");
}
sb.append("to any number of targets, divided as you choose");
return sb.toString();
}
sb.append("to any number of targets, divided as you choose");
return sb.toString();
return staticText;
}
}

View file

@ -1,13 +1,12 @@
package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
/**
*
@ -34,11 +33,9 @@ public class SacrificeSourceEffect extends OneShotEffect {
MageObject sourceObject = source.getSourceObjectIfItStillExists(game);
if (sourceObject == null) {
// Check if the effect was installed by the spell the source was cast by (e.g. Necromancy), if not don't sacrifice the permanent
if (source.getSourceObject(game) instanceof Spell) {
if (game.getState().getZone(source.getSourceId()).equals(Zone.BATTLEFIELD)
&& source.getSourceObjectZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(source.getSourceId())) {
sourceObject = game.getPermanent(source.getSourceId());
if (sourceObject != null && sourceObject.getZoneChangeCounter(game) > source.getSourceObjectZoneChangeCounter() + 1) {
return false;
}
}
}
if (sourceObject instanceof Permanent) {

View file

@ -0,0 +1,55 @@
/*
* 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.ReplacementEffectImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
*
* @author jeffwadsworth
*/
public class SkipCombatStepEffect extends ReplacementEffectImpl {
public SkipCombatStepEffect(Duration duration) {
super(duration, Outcome.Detriment);
staticText = "that player skips their next combat phase";
}
public SkipCombatStepEffect(final SkipCombatStepEffect effect) {
super(effect);
}
@Override
public SkipCombatStepEffect copy() {
return new SkipCombatStepEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.COMBAT_PHASE;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return event.getPlayerId().equals(targetPointer.getFirst(game, source));
}
}

View file

@ -0,0 +1,55 @@
/*
* 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.ReplacementEffectImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
*
* @author jeffwadsworth
*/
public class SkipNextDrawStepTargetEffect extends ReplacementEffectImpl {
public SkipNextDrawStepTargetEffect() {
super(Duration.OneUse, Outcome.Detriment);
staticText = "Target player skips his or her next draw step";
}
public SkipNextDrawStepTargetEffect(final SkipNextDrawStepTargetEffect effect) {
super(effect);
}
@Override
public SkipNextDrawStepTargetEffect copy() {
return new SkipNextDrawStepTargetEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DRAW_STEP;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return event.getPlayerId().equals(source.getFirstTarget());
}
}

View file

@ -5,6 +5,7 @@ import mage.constants.AttachmentType;
import mage.constants.Duration;
import mage.abilities.Ability;
import mage.abilities.effects.RestrictionEffect;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -14,7 +15,7 @@ import mage.game.permanent.Permanent;
public class CantAttackBlockAttachedEffect extends RestrictionEffect {
public CantAttackBlockAttachedEffect(AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield);
super(Duration.WhileOnBattlefield, Outcome.Removal);
this.staticText = attachmentType.verb() + " creature can't attack or block";
}

View file

@ -0,0 +1,79 @@
/*
* 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.continuous;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.CardType;
import mage.constants.DependencyType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author jeffwadsworth
*/
public class BecomesEnchantmentSourceEffect extends ContinuousEffectImpl implements SourceEffect {
public BecomesEnchantmentSourceEffect() {
super(Duration.Custom, Outcome.AddAbility);
staticText = "{this} becomes an Enchantment";
dependencyTypes.add(DependencyType.EnchantmentAddingRemoving);
}
public BecomesEnchantmentSourceEffect(final BecomesEnchantmentSourceEffect effect) {
super(effect);
}
@Override
public BecomesEnchantmentSourceEffect copy() {
return new BecomesEnchantmentSourceEffect(this);
}
@Override
public void init(Ability source, Game game) {
super.init(source, game);
affectedObjectList.add(new MageObjectReference(source.getSourceId(), game));
}
@Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
Permanent permanent = affectedObjectList.get(0).getPermanent(game);
if (permanent != null) {
switch (layer) {
case TypeChangingEffects_4:
if (sublayer == SubLayer.NA) {
permanent.getCardType().clear();
permanent.getSubtype(game).clear();
if (!permanent.getCardType().contains(CardType.ENCHANTMENT)) {
permanent.getCardType().add(CardType.ENCHANTMENT);
}
}
break;
}
return true;
}
this.discard();
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return false;
}
@Override
public boolean hasLayer(Layer layer) {
return Layer.TypeChangingEffects_4 == layer;
}
}

View file

@ -0,0 +1,52 @@
package mage.abilities.effects.common.cost;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.constants.CostModificationType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
public class SpellCostReductionSourceForOpponentsEffect extends CostModificationEffectImpl {
public SpellCostReductionSourceForOpponentsEffect() {
this("undaunted <i>(This spell costs {1} less to cast for each opponent.)</i>");
}
public SpellCostReductionSourceForOpponentsEffect(String newStaticText) {
super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
staticText = newStaticText;
}
public SpellCostReductionSourceForOpponentsEffect(final SpellCostReductionSourceForOpponentsEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
Mana mana = spellAbility.getManaCostsToPay().getMana();
if (mana.getGeneric() > 0) {
int count = game.getOpponents(source.getControllerId()).size();
int newCount = mana.getGeneric() - count;
if (newCount < 0) {
newCount = 0;
}
mana.setGeneric(newCount);
spellAbility.getManaCostsToPay().load(mana.toString());
return true;
}
return false;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
return abilityToModify instanceof SpellAbility && abilityToModify.getSourceId().equals(source.getSourceId());
}
@Override
public SpellCostReductionSourceForOpponentsEffect copy() {
return new SpellCostReductionSourceForOpponentsEffect(this);
}
}

View file

@ -1,4 +1,3 @@
package mage.abilities.effects.common.counter;
import mage.abilities.Ability;

View file

@ -0,0 +1,79 @@
package mage.abilities.keyword;
import mage.abilities.StaticAbility;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Zone;
/**
*
* @author L_J
*/
public class BandsWithOtherAbility extends StaticAbility {
private SubType subtype;
private SuperType supertype;
private String bandingName;
public BandsWithOtherAbility() {
this(null, null, null);
}
public BandsWithOtherAbility(SubType subtype) {
this(subtype, null, null);
}
public BandsWithOtherAbility(SuperType supertype) {
this(null, supertype, null);
}
public BandsWithOtherAbility(String bandingName) {
this(null, null, bandingName);
}
public BandsWithOtherAbility(SubType subtype, SuperType supertype, String bandingName) {
super(Zone.ALL, null);
this.subtype = subtype;
this.supertype = supertype;
this.bandingName = bandingName;
}
public BandsWithOtherAbility(BandsWithOtherAbility ability) {
super(ability);
this.subtype = ability.subtype;
this.supertype = ability.supertype;
this.bandingName = ability.bandingName;
}
@Override
public BandsWithOtherAbility copy() {
return new BandsWithOtherAbility(this);
}
public SubType getSubtype() {
return subtype;
}
public SuperType getSupertype() {
return supertype;
}
public String getName() {
return bandingName;
}
@Override
public String getRule() {
StringBuilder sb = new StringBuilder("bands with other");
if (subtype != null) {
return sb.append(' ').append(subtype.getDescription()).append('s').toString();
} else if (supertype != null) {
return sb.append(' ').append(supertype.toString()).append(" creatures").toString();
} else if (bandingName != null) {
return sb.append(" creatures named ").append(bandingName).toString();
}
return "all \"" + sb.toString() + "\" abilities";
}
}

View file

@ -13,7 +13,6 @@ import mage.game.permanent.Permanent;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author TheElk801
*/
public class MentorAbility extends AttacksTriggeredAbility {
@ -22,7 +21,7 @@ public class MentorAbility extends AttacksTriggeredAbility {
static {
filter.add(new AttackingPredicate());
filter.add(new MentorAbilityPredicate());
filter.add(MentorAbilityPredicate.instance);
}
public MentorAbility() {
@ -46,7 +45,8 @@ public class MentorAbility extends AttacksTriggeredAbility {
}
class MentorAbilityPredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<Card>> {
enum MentorAbilityPredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<Card>> {
instance;
@Override
public boolean apply(ObjectSourcePlayer<Card> input, Game game) {

View file

@ -199,8 +199,8 @@ class RevealNinjutsuCardCost extends CostImpl {
for (CommandObject coj : game.getState().getCommand()) {
if (coj != null && coj.getId().equals(ability.getSourceId())) {
card = game.getCard(ability.getSourceId());
break;
}
break;
}
}
if (card != null) {

View file

@ -5,25 +5,17 @@
*/
package mage.abilities.keyword;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.constants.CostModificationType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.abilities.effects.common.cost.SpellCostReductionSourceForOpponentsEffect;
import mage.constants.Zone;
import mage.game.Game;
/**
*
* @author LevelX2
*/
public class UndauntedAbility extends SimpleStaticAbility {
public UndauntedAbility() {
super(Zone.ALL, new UndauntedEffect());
super(Zone.ALL, new SpellCostReductionSourceForOpponentsEffect("undaunted <i>(This spell costs {1} less to cast for each opponent.)</i>"));
setRuleAtTheTop(true);
}
@ -36,43 +28,4 @@ public class UndauntedAbility extends SimpleStaticAbility {
return new UndauntedAbility(this);
}
}
class UndauntedEffect extends CostModificationEffectImpl {
public UndauntedEffect() {
super(Duration.Custom, Outcome.Benefit, CostModificationType.REDUCE_COST);
staticText = "undaunted <i>(This spell costs {1} less to cast for each opponent.)</i>";
}
public UndauntedEffect(final UndauntedEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
SpellAbility spellAbility = (SpellAbility) abilityToModify;
Mana mana = spellAbility.getManaCostsToPay().getMana();
if (mana.getGeneric() > 0) {
int count = game.getOpponents(source.getControllerId()).size();
int newCount = mana.getGeneric() - count;
if (newCount < 0) {
newCount = 0;
}
mana.setGeneric(newCount);
spellAbility.getManaCostsToPay().load(mana.toString());
return true;
}
return false;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
return abilityToModify instanceof SpellAbility && abilityToModify.getSourceId().equals(source.getSourceId());
}
@Override
public UndauntedEffect copy() {
return new UndauntedEffect(this);
}
}
}

View file

@ -1,6 +1,5 @@
package mage.abilities.meta;
import mage.MageObject;
import mage.abilities.TriggeredAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
@ -14,9 +13,12 @@ import java.util.List;
import java.util.UUID;
/**
* A triggered ability that combines several others and triggers whenever one or more of them would. The abilities
* passed in should have null as their effect, and should have their own targets set if necessary. All other information
* will be passed in from changes to this Ability. Note: this does NOT work with abilities that have intervening if clauses.
* A triggered ability that combines several others and triggers whenever one or
* more of them would. The abilities passed in should have null as their effect,
* and should have their own targets set if necessary. All other information
* will be passed in from changes to this Ability. Note: this does NOT work with
* abilities that have intervening if clauses.
*
* @author noahg
*/
public class OrTriggeredAbility extends TriggeredAbilityImpl {
@ -43,18 +45,17 @@ public class OrTriggeredAbility extends TriggeredAbilityImpl {
public OrTriggeredAbility(OrTriggeredAbility ability) {
super(ability);
this.triggeredAbilities = new TriggeredAbility[ability.triggeredAbilities.length];
for (int i = 0; i < this.triggeredAbilities.length; i++){
for (int i = 0; i < this.triggeredAbilities.length; i++) {
this.triggeredAbilities[i] = ability.triggeredAbilities[i].copy();
}
this.triggeringAbilities = new ArrayList<>(ability.triggeringAbilities);
this.ruleTrigger = ability.ruleTrigger;
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
for (TriggeredAbility ability : triggeredAbilities) {
if (ability.checkEventType(event, game)){
if (ability.checkEventType(event, game)) {
System.out.println("Correct event type (" + event.getType() + ")");
return true;
}
@ -101,7 +102,6 @@ public class OrTriggeredAbility extends TriggeredAbilityImpl {
return sb.toString() + super.getRule();
}
@Override
public void setControllerId(UUID controllerId) {
super.setControllerId(controllerId);
@ -126,11 +126,4 @@ public class OrTriggeredAbility extends TriggeredAbilityImpl {
}
}
@Override
public void setSourceObject(MageObject sourceObject, Game game) {
super.setSourceObject(sourceObject, game);
for (TriggeredAbility ability : triggeredAbilities) {
ability.setSourceObject(sourceObject, game);
}
}
}

View file

@ -39,7 +39,9 @@ public final class DeckImporterUtil {
}
public static DeckImporter getDeckImporter(String file) {
if (file.toLowerCase(Locale.ENGLISH).endsWith("dec")) {
if (file == null) {
return null;
} if (file.toLowerCase(Locale.ENGLISH).endsWith("dec")) {
return new DecDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("mwdeck")) {
return new MWSDeckImporter();

View file

@ -33,6 +33,8 @@ public enum ExpansionRepository {
private Dao<ExpansionInfo, Object> expansionDao;
public boolean instanceInitialized = false;
ExpansionRepository() {
File file = new File("db");
if (!file.exists()) {
@ -48,9 +50,11 @@ public enum ExpansionRepository {
TableUtils.createTableIfNotExists(connectionSource, ExpansionInfo.class);
expansionDao = DaoManager.createDao(connectionSource, ExpansionInfo.class);
instanceInitialized = true;
} catch (SQLException ex) {
ex.printStackTrace();
}
}
public void add(ExpansionInfo expansion) {

View file

@ -9,6 +9,7 @@ public enum CounterType {
AGE("age"),
AIM("aim"),
ARROW("arrow"),
ARROWHEAD("arrowhead"),
AWAKENING("awakening"),
BLAZE("blaze"),

View file

@ -23,7 +23,7 @@ import mage.filter.predicate.permanent.TokenPredicate;
/**
* A class that holds Filter objects that may not be modified without copying
* before. This prevents the creation of thousands of filter objects.
*
* <p>
* Because the filters are used application wide they may not be modified.
* NEVER!!!!! But it's possible, so be careful!
*
@ -42,6 +42,7 @@ public final class StaticFilters {
static {
FILTER_ENCHANTMENT_PERMANENT.setLockedFilter(true);
}
public static final FilterCard FILTER_CARD = new FilterCard("card");
static {
@ -119,6 +120,7 @@ public final class StaticFilters {
static {
FILTER_CARD_NON_LAND.setLockedFilter(true);
}
public static final FilterNonlandCard FILTER_CARD_A_NON_LAND = new FilterNonlandCard("a nonland card");
static {
@ -154,11 +156,13 @@ public final class StaticFilters {
static {
FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT.setLockedFilter(true);
}
public static final FilterCreaturePermanent FILTER_ARTIFACT_CREATURE_PERMANENT = new FilterArtifactCreaturePermanent();
static {
FILTER_ARTIFACT_CREATURE_PERMANENT.setLockedFilter(true);
}
public static final FilterPermanent FILTER_PERMANENT_ARTIFACT_OR_CREATURE = new FilterPermanent("artifact or creature");
static {
@ -168,6 +172,7 @@ public final class StaticFilters {
));
FILTER_PERMANENT_ARTIFACT_OR_CREATURE.setLockedFilter(true);
}
public static final FilterPermanent FILTER_PERMANENT_ARTIFACT_CREATURE_OR_ENCHANTMENT = new FilterPermanent("artifact, creature, or enchantment");
static {
@ -178,6 +183,7 @@ public final class StaticFilters {
));
FILTER_PERMANENT_ARTIFACT_CREATURE_OR_ENCHANTMENT.setLockedFilter(true);
}
public static final FilterPermanent FILTER_PERMANENT_ARTIFACT_CREATURE_ENCHANTMENT_OR_LAND = new FilterPermanent("artifact, creature, enchantment, or land");
static {
@ -195,16 +201,19 @@ public final class StaticFilters {
static {
FILTER_CONTROLLED_PERMANENT.setLockedFilter(true);
}
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_ARTIFACT = new FilterControlledArtifactPermanent();
static {
FILTER_CONTROLLED_PERMANENT_ARTIFACT.setLockedFilter(true);
}
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN = new FilterControlledArtifactPermanent("an artifact");
static {
FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN.setLockedFilter(true);
}
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_ARTIFACT_OR_CREATURE = new FilterControlledPermanent("artifact or creature you control");
static {
@ -214,6 +223,7 @@ public final class StaticFilters {
));
FILTER_CONTROLLED_PERMANENT_ARTIFACT_OR_CREATURE.setLockedFilter(true);
}
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_LAND = new FilterControlledLandPermanent();
static {
@ -232,12 +242,14 @@ public final class StaticFilters {
FILTER_OPPONENTS_PERMANENT.add(new ControllerPredicate(TargetController.OPPONENT));
FILTER_OPPONENTS_PERMANENT.setLockedFilter(true);
}
public static final FilterCreaturePermanent FILTER_OPPONENTS_PERMANENT_CREATURE = new FilterCreaturePermanent("creature an opponent controls");
static {
FILTER_OPPONENTS_PERMANENT_CREATURE.add(new ControllerPredicate(TargetController.OPPONENT));
FILTER_OPPONENTS_PERMANENT_CREATURE.setLockedFilter(true);
}
public static final FilterPermanent FILTER_OPPONENTS_PERMANENT_ARTIFACT = new FilterPermanent("artifact an opponent controls");
static {
@ -245,6 +257,7 @@ public final class StaticFilters {
FILTER_OPPONENTS_PERMANENT_ARTIFACT.add(new CardTypePredicate(CardType.ARTIFACT));
FILTER_OPPONENTS_PERMANENT_ARTIFACT.setLockedFilter(true);
}
public static final FilterPermanent FILTER_OPPONENTS_PERMANENT_ARTIFACT_OR_CREATURE = new FilterPermanent("artifact or creature an opponent controls");
static {
@ -280,12 +293,14 @@ public final class StaticFilters {
static {
FILTER_CONTROLLED_A_CREATURE.setLockedFilter(true);
}
public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_ANOTHER_CREATURE = new FilterControlledCreaturePermanent("another creature");
static {
FILTER_CONTROLLED_ANOTHER_CREATURE.add(new AnotherPredicate());
FILTER_CONTROLLED_ANOTHER_CREATURE.setLockedFilter(true);
}
public static final FilterControlledPermanent FILTER_CONTROLLED_PERMANENT_NON_LAND = new FilterControlledPermanent("nonland permanent");
static {
@ -294,6 +309,7 @@ public final class StaticFilters {
);
FILTER_CONTROLLED_PERMANENT_NON_LAND.setLockedFilter(true);
}
public static final FilterLandPermanent FILTER_LAND = new FilterLandPermanent();
static {
@ -331,6 +347,12 @@ public final class StaticFilters {
FILTER_PERMANENT_CREATURE.setLockedFilter(true);
}
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_A = new FilterCreaturePermanent("a creature");
static {
FILTER_PERMANENT_CREATURE_A.setLockedFilter(true);
}
public static final FilterPermanent FILTER_PERMANENT_CREATURE_OR_PLANESWALKER_A = new FilterPermanent("a creature or planeswalker");
static {
@ -344,33 +366,39 @@ public final class StaticFilters {
static {
FILTER_PERMANENT_A_CREATURE.setLockedFilter(true);
}
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_CONTROLLED = new FilterCreaturePermanent("creature you control");
static {
FILTER_PERMANENT_CREATURE_CONTROLLED.add(new ControllerPredicate(TargetController.YOU));
FILTER_PERMANENT_CREATURE_CONTROLLED.setLockedFilter(true);
}
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURES = new FilterCreaturePermanent("creatures");
static {
FILTER_PERMANENT_CREATURES.setLockedFilter(true);
}
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURES_CONTROLLED = new FilterCreaturePermanent("creatures you control");
static {
FILTER_PERMANENT_CREATURES_CONTROLLED.add(new ControllerPredicate(TargetController.YOU));
FILTER_PERMANENT_CREATURES_CONTROLLED.setLockedFilter(true);
}
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_GOBLINS = new FilterCreaturePermanent(SubType.GOBLIN, "Goblin creatures");
static {
FILTER_PERMANENT_CREATURE_GOBLINS.setLockedFilter(true);
}
public static final FilterCreaturePermanent FILTER_PERMANENT_CREATURE_SLIVERS = new FilterCreaturePermanent(SubType.SLIVER, "all Sliver creatures");
static {
FILTER_PERMANENT_CREATURE_SLIVERS.setLockedFilter(true);
}
public static final FilterPlaneswalkerPermanent FILTER_PERMANENT_PLANESWALKER = new FilterPlaneswalkerPermanent();
static {
@ -388,12 +416,14 @@ public final class StaticFilters {
static {
FILTER_PERMANENTS_NON_LAND.setLockedFilter(true);
}
public static final FilterStackObject FILTER_SPELL_OR_ABILITY_OPPONENTS = new FilterStackObject("spell or ability and opponent controls");
static {
FILTER_SPELL_OR_ABILITY_OPPONENTS.add(new ControllerPredicate(TargetController.OPPONENT));
FILTER_SPELL_OR_ABILITY_OPPONENTS.setLockedFilter(true);
}
public static final FilterStackObject FILTER_SPELL_OR_ABILITY = new FilterStackObject();
static {
@ -405,11 +435,13 @@ public final class StaticFilters {
static {
FILTER_SPELL_A_CREATURE.setLockedFilter(true);
}
public static final FilterCreatureSpell FILTER_SPELL_CREATURE = new FilterCreatureSpell("creature spell");
static {
FILTER_SPELL_CREATURE.setLockedFilter(true);
}
public static final FilterSpell FILTER_SPELL_NON_CREATURE = (FilterSpell) new FilterSpell("noncreature spell").add(Predicates.not(new CardTypePredicate(CardType.CREATURE)));
static {
@ -444,6 +476,7 @@ public final class StaticFilters {
));
FILTER_SPELL_AN_INSTANT_OR_SORCERY.setLockedFilter(true);
}
public static final FilterSpell FILTER_SPELL_INSTANT_OR_SORCERY = new FilterSpell("instant or sorcery spell");
static {
@ -463,6 +496,7 @@ public final class StaticFilters {
));
FILTER_SPELLS_INSTANT_OR_SORCERY.setLockedFilter(true);
}
public static final FilterCreaturePermanent FILTER_CREATURE_TOKENS = new FilterCreaturePermanent("creature tokens");
static {
@ -484,6 +518,7 @@ public final class StaticFilters {
FILTER_PERMANENT_AURA.add(new SubtypePredicate(SubType.AURA));
FILTER_PERMANENT_AURA.setLockedFilter(true);
}
public static final FilterPermanent FILTER_PERMANENT_EQUIPMENT = new FilterPermanent();
static {
@ -491,6 +526,7 @@ public final class StaticFilters {
FILTER_PERMANENT_EQUIPMENT.add(new SubtypePredicate(SubType.EQUIPMENT));
FILTER_PERMANENT_EQUIPMENT.setLockedFilter(true);
}
public static final FilterPermanent FILTER_PERMANENT_FORTIFICATION = new FilterPermanent();
static {
@ -498,6 +534,7 @@ public final class StaticFilters {
FILTER_PERMANENT_FORTIFICATION.add(new SubtypePredicate(SubType.FORTIFICATION));
FILTER_PERMANENT_FORTIFICATION.setLockedFilter(true);
}
public static final FilterPermanent FILTER_PERMANENT_LEGENDARY = new FilterPermanent();
static {

View file

@ -12,6 +12,9 @@ public interface Controllable {
UUID getId();
default boolean isControlledBy(UUID controllerID){
if(getControllerId() == null){
return false;
}
return getControllerId().equals(controllerID);
}
}

View file

@ -1026,6 +1026,7 @@ public abstract class GameImpl implements Game, Serializable {
watchers.add(new BlockedAttackerWatcher());
watchers.add(new DamageDoneWatcher());
watchers.add(new PlanarRollWatcher());
watchers.add(new PlayersAttackedThisTurnWatcher());
//20100716 - 103.5
for (UUID playerId : state.getPlayerList(startingPlayerId)) {
@ -1527,7 +1528,7 @@ public abstract class GameImpl implements Game, Serializable {
@Override
public void addEffect(ContinuousEffect continuousEffect, Ability source) {
Ability newAbility = source.copy();
newAbility.setSourceObject(null, this); // Update the source object to the currently existing Object
newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(source.getSourceId()));
ContinuousEffect newEffect = continuousEffect.copy();
newEffect.newId();
@ -1698,11 +1699,17 @@ public abstract class GameImpl implements Game, Serializable {
if (ability instanceof TriggeredManaAbility || ability instanceof DelayedTriggeredManaAbility) {
// 20110715 - 605.4
Ability manaAbiltiy = ability.copy();
if (manaAbiltiy.getSourceObjectZoneChangeCounter() == 0) {
manaAbiltiy.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(ability.getSourceId()));
}
manaAbiltiy.activate(this, false);
manaAbiltiy.resolve(this);
} else {
TriggeredAbility newAbility = ability.copy();
newAbility.newId();
if (newAbility.getSourceObjectZoneChangeCounter() == 0) {
newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(ability.getSourceId()));
}
state.addTriggeredAbility(newAbility);
}
}
@ -1711,10 +1718,10 @@ public abstract class GameImpl implements Game, Serializable {
public UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility, Ability source) {
delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId());
delayedAbility.setSourceObject(source.getSourceObject(this), this);
// return addDelayedTriggeredAbility(delayedAbility);
DelayedTriggeredAbility newAbility = delayedAbility.copy();
newAbility.newId();
newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(source.getSourceId()));
newAbility.initOnAdding(this);
// ability.init is called as the ability triggeres not now.
// If a FixedTarget pointer is already set from the effect setting up this delayed ability
@ -2594,7 +2601,7 @@ public abstract class GameImpl implements Game, Serializable {
boolean addPlaneAgain = false;
for (Iterator<CommandObject> it = this.getState().getCommand().iterator(); it.hasNext();) {
CommandObject obj = it.next();
if (obj.getControllerId().equals(playerId)) {
if (obj.isControlledBy(playerId)) {
if (obj instanceof Emblem) {
((Emblem) obj).discardEffects();// This may not be the best fix but it works
}

View file

@ -1,4 +1,3 @@
package mage.game.combat;
import mage.MageObject;
@ -6,6 +5,7 @@ import mage.abilities.Ability;
import mage.abilities.effects.RequirementEffect;
import mage.abilities.effects.RestrictionEffect;
import mage.abilities.keyword.BandingAbility;
import mage.abilities.keyword.BandsWithOtherAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.abilities.keyword.special.JohanVigilanceAbility;
import mage.constants.Outcome;
@ -14,8 +14,12 @@ import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.common.FilterCreatureForCombatBlock;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.mageobject.SupertypePredicate;
import mage.filter.predicate.permanent.AttackingSameNotBandedPredicate;
import mage.filter.predicate.permanent.PermanentIdPredicate;
import mage.game.Game;
@ -103,8 +107,8 @@ public class Combat implements Serializable, Copyable<Combat> {
}
/**
* Get all possible defender (players and plainwalkers) That does not mean
* neccessarly mean that they are really attacked
* Get all possible defender (players and planeswalkers) That does not mean
* necessarily mean that they are really attacked
*
* @return
*/
@ -246,11 +250,14 @@ public class Combat implements Serializable, Copyable<Combat> {
game.getCombat().checkAttackRequirements(player, game);
boolean firstTime = true;
do {
if (!firstTime || !game.getPlayer(game.getActivePlayerId()).getAvailableAttackers(game).isEmpty()) {
if (!firstTime
|| !game.getPlayer(game.getActivePlayerId()).getAvailableAttackers(game).isEmpty()) {
player.selectAttackers(game, attackingPlayerId);
}
firstTime = false;
if (game.isPaused() || game.checkIfGameIsOver() || game.executingRollback()) {
if (game.isPaused()
|| game.checkIfGameIsOver()
|| game.executingRollback()) {
return;
}
// because of possible undo during declare attackers it's neccassary to call here the methods with "game.getCombat()." to get the current combat object!!!
@ -293,53 +300,115 @@ public class Combat implements Serializable, Copyable<Combat> {
Permanent attacker = game.getPermanent(creatureId);
if (attacker != null && player != null) {
CombatGroup combatGroup = findGroup(attacker.getId());
if (combatGroup != null && attacker.getAbilities().containsKey(BandingAbility.getInstance().getId()) && attacker.getBandedCards().isEmpty() && getAttackers().size() > 1) {
boolean isBanded = false;
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("attacking creature to band with " + attacker.getLogName());
filter.add(Predicates.not(new PermanentIdPredicate(creatureId)));
filter.add(new AttackingSameNotBandedPredicate(combatGroup.getDefenderId())); // creature that isn't already banded, and is attacking the same player or planeswalker
while (player.canRespond()) {
TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true);
target.setRequired(false);
if (!target.canChoose(attackingPlayerId, game)
|| game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, attackingPlayerId, attackingPlayerId))
|| !player.chooseUse(Outcome.Benefit, "Do you wish to " + (isBanded ? "band " + attacker.getLogName() + " with another " : "form a band with " + attacker.getLogName() + " and an ") + "attacking creature?", null, game)) {
break;
}
if (target.choose(Outcome.Benefit, attackingPlayerId, null, game)) {
isBanded = true;
for (UUID targetId : target.getTargets()) {
Permanent permanent = game.getPermanent(targetId);
if (permanent != null) {
for (UUID bandedId : attacker.getBandedCards()) {
permanent.addBandedCard(bandedId);
Permanent banded = game.getPermanent(bandedId);
if (banded != null) {
banded.addBandedCard(targetId);
}
}
permanent.addBandedCard(creatureId);
attacker.addBandedCard(targetId);
if (!permanent.getAbilities().containsKey(BandingAbility.getInstance().getId())) {
filter.add(new AbilityPredicate(BandingAbility.class));
}
}
}
if (combatGroup != null && attacker.getBandedCards().isEmpty() && getAttackers().size() > 1) {
boolean canBand = attacker.getAbilities().containsKey(BandingAbility.getInstance().getId());
List<Ability> bandsWithOther = new ArrayList<>();
for (Ability ability : attacker.getAbilities()) {
if (ability.getClass().equals(BandsWithOtherAbility.class)) {
bandsWithOther.add(ability);
}
}
if (isBanded) {
StringBuilder sb = new StringBuilder(player.getLogName()).append(" formed a band with ").append((attacker.getBandedCards().size() + 1) + " creatures: ");
sb.append(attacker.getLogName());
for (UUID id : attacker.getBandedCards()) {
sb.append(", ");
Permanent permanent = game.getPermanent(id);
if (permanent != null) {
sb.append(permanent.getLogName());
boolean canBandWithOther = !bandsWithOther.isEmpty();
if (canBand || canBandWithOther) {
boolean isBanded = false;
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("attacking creature to band with " + attacker.getLogName());
filter.add(Predicates.not(new PermanentIdPredicate(creatureId)));
filter.add(new AttackingSameNotBandedPredicate(combatGroup.getDefenderId())); // creature that isn't already banded, and is attacking the same player or planeswalker
List<Predicate<MageObject>> predicates = new ArrayList<>();
if (!canBand && canBandWithOther) {
for (Ability ab : bandsWithOther) {
BandsWithOtherAbility ability = (BandsWithOtherAbility) ab;
if (ability.getSubtype() != null) {
predicates.add(new SubtypePredicate(ability.getSubtype()));
}
if (ability.getSupertype() != null) {
predicates.add(new SupertypePredicate(ability.getSupertype()));
}
if (ability.getName() != null) {
predicates.add(new NamePredicate(ability.getName()));
}
}
filter.add(Predicates.or(predicates));
}
while (player.canRespond()) {
TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true);
target.setRequired(false);
canBand &= target.canChoose(attackingPlayerId, game);
canBandWithOther &= target.canChoose(attackingPlayerId, game);
if (game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, attackingPlayerId, attackingPlayerId))
|| (!canBand && !canBandWithOther)
|| !player.chooseUse(Outcome.Benefit, "Do you wish to " + (isBanded ? "band " + attacker.getLogName() + " with another " : "form a band with " + attacker.getLogName() + " and an ") + "attacking creature?", null, game)) {
break;
}
if (canBand && canBandWithOther) {
if (player.chooseUse(Outcome.Detriment, "Choose type of banding ability to apply:", attacker.getLogName(), "Banding", "Bands with other", null, game)) {
canBandWithOther = false;
} else {
canBand = false;
for (Ability ab : bandsWithOther) {
BandsWithOtherAbility ability = (BandsWithOtherAbility) ab;
if (ability.getSubtype() != null) {
predicates.add(new SubtypePredicate(ability.getSubtype()));
}
if (ability.getSupertype() != null) {
predicates.add(new SupertypePredicate(ability.getSupertype()));
}
if (ability.getName() != null) {
predicates.add(new NamePredicate(ability.getName()));
}
}
filter.add(Predicates.or(predicates));
}
}
if (target.choose(Outcome.Benefit, attackingPlayerId, null, game)) {
isBanded = true;
for (UUID targetId : target.getTargets()) {
Permanent permanent = game.getPermanent(targetId);
if (permanent != null) {
for (UUID bandedId : attacker.getBandedCards()) {
permanent.addBandedCard(bandedId);
Permanent banded = game.getPermanent(bandedId);
if (banded != null) {
banded.addBandedCard(targetId);
}
}
permanent.addBandedCard(creatureId);
attacker.addBandedCard(targetId);
if (canBand) {
if (!permanent.getAbilities().containsKey(BandingAbility.getInstance().getId())) {
filter.add(new AbilityPredicate(BandingAbility.class));
canBandWithOther = false;
}
} else if (canBandWithOther) {
List<Predicate<MageObject>> newPredicates = new ArrayList<>();
for (Predicate<MageObject> predicate : predicates) {
if (predicate.apply(permanent, game)) {
newPredicates.add(predicate);
}
}
filter.add(Predicates.or(newPredicates));
canBand = false;
}
}
}
}
}
game.informPlayers(sb.toString());
if (isBanded) {
StringBuilder sb = new StringBuilder(player.getLogName()).append(" formed a band with ").append((attacker.getBandedCards().size() + 1) + " creatures: ");
sb.append(attacker.getLogName());
for (UUID id : attacker.getBandedCards()) {
sb.append(", ");
Permanent permanent = game.getPermanent(id);
if (permanent != null) {
sb.append(permanent.getLogName());
}
}
game.informPlayers(sb.toString());
}
}
}
}
@ -387,11 +456,16 @@ public class Combat implements Serializable, Copyable<Combat> {
if (defenders.size() == 1) {
player.declareAttacker(creature.getId(), defenders.iterator().next(), game, false);
} else {
TargetDefender target = new TargetDefender(defenders, creature.getId());
target.setRequired(true);
target.setTargetName("planeswalker or player for " + creature.getLogName() + " to attack");
if (player.chooseTarget(Outcome.Damage, target, null, game)) {
player.declareAttacker(creature.getId(), target.getFirstTarget(), game, false);
if (!player.isHuman()) { // computer only for multiple defenders
player.declareAttacker(creature.getId(), defenders.iterator().next(), game, false);
} else { // human players only for multiple defenders
TargetDefender target = new TargetDefender(defenders, creature.getId());
target.setRequired(true);
target.setTargetName("planeswalker or player for " + creature.getLogName() + " to attack");
if (player.chooseTarget(Outcome.Damage, target, null, game)) {
System.out.println("The player " + player.getName() + " declares an attacker here. " + creature.getName());
player.declareAttacker(creature.getId(), target.getFirstTarget(), game, false);
}
}
}
} else {
@ -483,7 +557,7 @@ public class Combat implements Serializable, Copyable<Combat> {
* Handle the blocker selection process
*
* @param blockController player that controlls how to block, if null the
* defender is the controller
* defender is the controller
* @param game
*/
public void selectBlockers(Player blockController, Game game) {
@ -1275,10 +1349,10 @@ public class Combat implements Serializable, Copyable<Combat> {
if (defenderAttackedBy.size() >= defendingPlayer.getMaxAttackedBy()) {
Player attackingPlayer = game.getPlayer(game.getControllerId(attackerId));
if (attackingPlayer != null && !game.isSimulation()) {
game.informPlayer(attackingPlayer, "No more than " +
CardUtil.numberToText(defendingPlayer.getMaxAttackedBy()) +
" creatures can attack " +
defendingPlayer.getLogName());
game.informPlayer(attackingPlayer, "No more than "
+ CardUtil.numberToText(defendingPlayer.getMaxAttackedBy())
+ " creatures can attack "
+ defendingPlayer.getLogName());
}
return false;
}
@ -1308,7 +1382,7 @@ public class Combat implements Serializable, Copyable<Combat> {
* @param playerId
* @param game
* @param solveBanding check whether also add creatures banded with
* attackerId
* attackerId
*/
public void addBlockingGroup(UUID blockerId, UUID attackerId, UUID playerId, Game game, boolean solveBanding) {
Permanent blocker = game.getPermanent(blockerId);

View file

@ -5,10 +5,12 @@ import java.io.Serializable;
import java.util.*;
import java.util.stream.Stream;
import mage.abilities.Ability;
import mage.abilities.common.ControllerAssignCombatDamageToBlockersAbility;
import mage.abilities.common.ControllerDivideCombatDamageAbility;
import mage.abilities.common.DamageAsThoughNotBlockedAbility;
import mage.abilities.keyword.BandingAbility;
import mage.abilities.keyword.BandsWithOtherAbility;
import mage.abilities.keyword.CantBlockAloneAbility;
import mage.abilities.keyword.DeathtouchAbility;
import mage.abilities.keyword.DoubleStrikeAbility;
@ -110,6 +112,56 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
return perm.getAbilities().containsKey(BandingAbility.getInstance().getId());
}
private boolean appliesBandsWithOther(List<UUID> creatureIds, Game game) {
for (UUID creatureId : creatureIds) {
Permanent perm = game.getPermanent(creatureId);
if (perm != null && perm.getBandedCards() != null) {
for (Ability ab : perm.getAbilities()) {
if (ab.getClass().equals(BandsWithOtherAbility.class)) {
BandsWithOtherAbility ability = (BandsWithOtherAbility) ab;
if (ability.getSubtype() != null) {
if (perm.hasSubtype(ability.getSubtype(), game)) {
for (UUID bandedId : creatureIds) {
if (!bandedId.equals(creatureId)) {
Permanent banded = game.getPermanent(bandedId);
if (banded != null && banded.hasSubtype(ability.getSubtype(), game)) {
return true;
}
}
}
}
}
if (ability.getSupertype() != null) {
if (perm.getSuperType().contains(ability.getSupertype())) {
for (UUID bandedId : creatureIds) {
if (!bandedId.equals(creatureId)) {
Permanent banded = game.getPermanent(bandedId);
if (banded != null && banded.getSuperType().contains(ability.getSupertype())) {
return true;
}
}
}
}
}
if (ability.getName() != null) {
if (perm.getName().equals(ability.getName())) {
for (UUID bandedId : creatureIds) {
if (!bandedId.equals(creatureId)) {
Permanent banded = game.getPermanent(bandedId);
if (banded != null && banded.getName().equals(ability.getName())) {
return true;
}
}
}
}
}
}
}
}
}
return false;
}
public void assignDamageToBlockers(boolean first, Game game) {
if (!attackers.isEmpty() && (!first || hasFirstOrDoubleStrike(game))) {
Permanent attacker = game.getPermanent(attackers.get(0));
@ -837,6 +889,9 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
}
}
if (appliesBandsWithOther(attackers, game)) { // 702.21k - both a [quality] creature with bands with other [quality] and another [quality] creature (...)
return true;
}
return false;
}
@ -855,6 +910,9 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
}
}
}
if (appliesBandsWithOther(blockers, game)) { // 702.21j - both a [quality] creature with bands with other [quality] and another [quality] creature (...)
return true;
}
for (Permanent defensiveFormation : game.getBattlefield().getAllActivePermanents(defendingPlayerId)) {
if (defensiveFormation.getAbilities().containsKey(ControllerAssignCombatDamageToBlockersAbility.getInstance().getId())) {
return true;

View file

@ -3,17 +3,16 @@ package mage.game.command;
import java.util.UUID;
import mage.MageObject;
import mage.game.Controllable;
/**
*
* @author Viserion, nantuko
*/
public interface CommandObject extends MageObject {
public interface CommandObject extends MageObject, Controllable {
UUID getSourceId();
UUID getControllerId();
void assignNewId();
MageObject getSourceObject();

View file

@ -1,4 +1,3 @@
package mage.game.command;
import java.util.EnumSet;

View file

@ -1,7 +1,5 @@
package mage.game.command;
import static java.lang.Math.log;
import java.lang.reflect.Constructor;
import java.util.EnumSet;
import java.util.List;
@ -289,7 +287,7 @@ public class Plane implements CommandObject {
if (plane instanceof Plane) {
return (Plane) plane;
}
} catch (Exception ex) {
} catch (Exception ex) {
}
return null;
}

View file

@ -3,11 +3,11 @@ package mage.game.command.emblems;
import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility;
import mage.abilities.effects.common.LoseGameTargetPlayerEffect;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.command.Emblem;
/**
*
* @author TheElk801
*/
public final class VraskaGolgariQueenEmblem extends Emblem {
@ -17,9 +17,9 @@ public final class VraskaGolgariQueenEmblem extends Emblem {
this.setName("Emblem Vraska");
this.setExpansionSetCodeForImage("GRN");
this.getAbilities().add(new DealsDamageToAPlayerAllTriggeredAbility(
new LoseGameTargetPlayerEffect(),
Zone.COMMAND, new LoseGameTargetPlayerEffect(),
StaticFilters.FILTER_CONTROLLED_A_CREATURE,
false, SetTargetPointer.PLAYER, true
false, SetTargetPointer.NONE, true, true
));
}
}

View file

@ -147,7 +147,8 @@ public abstract class DraftImpl implements Draft {
@Override
public void autoPick(UUID playerId) {
this.addPick(playerId, players.get(playerId).getBooster().get(0).getId(), null);
List<Card> booster = players.get(playerId).getBooster();
this.addPick(playerId, booster.get(booster.size()-1).getId(), null);
}
protected void passLeft() {

View file

@ -37,6 +37,7 @@ public class MatchOptions implements Serializable {
protected boolean spectatorsAllowed;
protected boolean planeChase;
protected int quitRatio;
protected int minimumRating;
protected int edhPowerLevel;
protected boolean rated;
protected int numSeatsForMatch;
@ -205,6 +206,10 @@ public class MatchOptions implements Serializable {
this.quitRatio = quitRatio;
}
public int getMinimumRating() { return minimumRating; }
public void setMinimumRating(int minimumRating) { this.minimumRating = minimumRating; }
public int getEdhPowerLevel() {
return edhPowerLevel;
}

View file

@ -20,7 +20,7 @@ public final class GoblinToken extends TokenImpl {
static {
tokenImageSets.addAll(Arrays.asList("10E", "ALA", "SOM", "M10", "NPH", "M13", "RTR",
"MMA", "M15", "C14", "KTK", "EVG", "DTK", "ORI", "DDG", "DDN", "DD3EVG", "MM2",
"MM3", "EMA", "C16", "DOM"));
"MM3", "EMA", "C16", "DOM", "ANA"));
}
public GoblinToken(boolean withHaste) {

View file

@ -10,7 +10,6 @@ import mage.constants.SubType;
import mage.target.TargetPermanent;
/**
*
* @author TheElk801
*/
public final class MaskToken extends TokenImpl {
@ -18,8 +17,8 @@ public final class MaskToken extends TokenImpl {
public MaskToken() {
super(
"Mask", "white Aura enchantment token named Mask "
+ "attached to another target permanent. "
+ "The token has enchant permanent and totem armor."
+ "attached to another target permanent. "
+ "The token has enchant permanent and totem armor."
);
cardType.add(CardType.ENCHANTMENT);
color.setWhite(true);
@ -31,6 +30,7 @@ public final class MaskToken extends TokenImpl {
ability.addEffect(new AttachEffect(Outcome.BoostCreature));
this.addAbility(ability);
// Totem armor
this.addAbility(new TotemArmorAbility());
}

View file

@ -17,7 +17,7 @@ public final class SpiritWhiteToken extends TokenImpl {
final static private List<String> tokenImageSets = new ArrayList<>();
static {
tokenImageSets.addAll(Arrays.asList("AVR", "C14", "CNS", "DDC", "DDK", "FRF", "ISD", "KTK", "M15", "MM2", "SHM", "SOI", "EMA", "C16", "MM3", "CMA", "E01"));
tokenImageSets.addAll(Arrays.asList("AVR", "C14", "CNS", "DDC", "DDK", "FRF", "ISD", "KTK", "M15", "MM2", "SHM", "SOI", "EMA", "C16", "MM3", "CMA", "E01", "ANA"));
}
public SpiritWhiteToken() {

View file

@ -0,0 +1,32 @@
package mage.game.permanent.token;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.MageInt;
import mage.abilities.keyword.BandsWithOtherAbility;
/**
*
* @author L_J
*/
public final class WolvesOfTheHuntToken extends TokenImpl {
public WolvesOfTheHuntToken() {
super("Wolves of the Hunt", "1/1 green Wolf creature token named Wolves of the Hunt");
cardType.add(CardType.CREATURE);
subtype.add(SubType.WOLF);
color.setGreen(true);
power = new MageInt(1);
toughness = new MageInt(1);
this.addAbility(new BandsWithOtherAbility("Wolves of the Hunt"));
}
public WolvesOfTheHuntToken(final WolvesOfTheHuntToken token) {
super(token);
}
public WolvesOfTheHuntToken copy() {
return new WolvesOfTheHuntToken(this);
}
}

View file

@ -518,14 +518,19 @@ public class StackAbility extends StackObjImpl implements Ability {
return this.ability.getSourcePermanentIfItStillExists(game);
}
@Override
public void setSourceObjectZoneChangeCounter(int zoneChangeCounter) {
ability.setSourceObjectZoneChangeCounter(zoneChangeCounter);
}
@Override
public int getSourceObjectZoneChangeCounter() {
return ability.getSourceObjectZoneChangeCounter();
}
@Override
public void setSourceObject(MageObject sourceObject, Game game) {
throw new UnsupportedOperationException("Not supported.");
public Permanent getSourcePermanentOrLKI(Game game) {
return ability.getSourcePermanentOrLKI(game);
}
@Override

View file

@ -24,6 +24,7 @@ public class TournamentOptions implements Serializable {
protected int numberRounds;
protected String password;
protected int quitRatio;
protected int minimumRating;
public TournamentOptions(String name, String matchType, int numSeats) {
this.name = name;
@ -98,4 +99,8 @@ public class TournamentOptions implements Serializable {
public void setQuitRatio(int quitRatio) {
this.quitRatio = quitRatio;
}
public int getMinimumRating() { return minimumRating; }
public void setMinimumRating(int minimumRating) { this.minimumRating = minimumRating; }
}

View file

@ -1,5 +1,7 @@
package mage.players;
import java.io.Serializable;
import java.util.*;
import mage.MageItem;
import mage.MageObject;
import mage.MageObjectReference;
@ -37,9 +39,6 @@ import mage.target.TargetCard;
import mage.target.common.TargetCardInLibrary;
import mage.util.Copyable;
import java.io.Serializable;
import java.util.*;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -74,7 +73,7 @@ public interface Player extends MageItem, Copyable<Player> {
void setLife(int life, Game game, UUID sourceId);
/**
* @param amount amount of life loss
* @param amount amount of life loss
* @param game
* @param atCombat was the source combat damage
* @return
@ -345,7 +344,7 @@ public interface Player extends MageItem, Copyable<Player> {
* @param target
* @param game
* @param targetPlayerId player whose library will be searched
* @param triggerEvents whether searching will trigger any game events
* @param triggerEvents whether searching will trigger any game events
* @return true if search was successful
*/
boolean searchLibrary(TargetCardInLibrary target, Game game, UUID targetPlayerId, boolean triggerEvents);
@ -364,22 +363,23 @@ public interface Player extends MageItem, Copyable<Player> {
/**
* Plays a card if possible
*
* @param card the card that can be cast
* @param card the card that can be cast
* @param game
* @param noMana if it's a spell i can be cast without paying mana
* @param noMana if it's a spell i can be cast without paying mana
* @param ignoreTiming if it's cast during the resolution of another spell
* no sorcery or play land timing restriction are checked. For a land it has
* to be the turn of the player playing that card.
* no sorcery or play land timing restriction are checked. For a land it has
* to be the turn of the player playing that card.
* @param reference mage object that allows to play the card
* @return
*/
boolean playCard(Card card, Game game, boolean noMana, boolean ignoreTiming, MageObjectReference reference);
/**
* @param card the land card to play
* @param card the land card to play
* @param game
* @param ignoreTiming false - it won't be checked if the stack is empty and
* you are able to play a Sorcery. It's still checked, if you are able to
* play a land concerning the numner of lands you already played.
* you are able to play a Sorcery. It's still checked, if you are able to
* play a land concerning the number of lands you already played.
* @return
*/
boolean playLand(Card card, Game game, boolean ignoreTiming);
@ -525,11 +525,11 @@ public interface Player extends MageItem, Copyable<Player> {
/**
* Moves the cards from cards to the bottom of the players library.
*
* @param cards - list of cards that have to be moved
* @param game - game
* @param cards - list of cards that have to be moved
* @param game - game
* @param anyOrder - true if player can determine the order of the cards
* else random order
* @param source - source ability
* else random order
* @param source - source ability
* @return
*/
boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder);
@ -550,10 +550,10 @@ public interface Player extends MageItem, Copyable<Player> {
/**
* Moves the cards from cards to the top of players library.
*
* @param cards - list of cards that have to be moved
* @param game - game
* @param cards - list of cards that have to be moved
* @param game - game
* @param anyOrder - true if player can determine the order of the cards
* @param source - source ability
* @param source - source ability
* @return
*/
boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder);
@ -579,8 +579,8 @@ public interface Player extends MageItem, Copyable<Player> {
/**
* Choose the order in which blockers get damage assigned to
*
* @param blockers list of blockers where to choose the next one from
* @param combatGroup the concerning combat group
* @param blockers list of blockers where to choose the next one from
* @param combatGroup the concerning combat group
* @param blockerOrder the already set order of blockers
* @param game
* @return blocker next to add to the blocker order
@ -719,11 +719,11 @@ public interface Player extends MageItem, Copyable<Player> {
* @param toZone
* @param source
* @param game
* @param tapped the cards are tapped on the battlefield
* @param faceDown the cards are face down in the to zone
* @param byOwner the card is moved (or put onto battlefield) by the owner
* of the card and if target zone is battlefield controls the permanent
* (instead of the controller of the source)
* @param tapped the cards are tapped on the battlefield
* @param faceDown the cards are face down in the to zone
* @param byOwner the card is moved (or put onto battlefield) by the owner
* of the card and if target zone is battlefield controls the permanent
* (instead of the controller of the source)
* @param appliedEffects
* @return
*/
@ -759,7 +759,7 @@ public interface Player extends MageItem, Copyable<Player> {
* list of applied effects is not saved
*
* @param card
* @param exileId exile zone id (optional)
* @param exileId exile zone id (optional)
* @param exileName name of exile zone (optional)
* @param sourceId
* @param game
@ -801,7 +801,7 @@ public interface Player extends MageItem, Copyable<Player> {
* @param sourceId
* @param game
* @param fromZone if null, this info isn't postet
* @param toTop to the top of the library else to the bottom
* @param toTop to the top of the library else to the bottom
* @param withName show the card name in the log
* @return
*/
@ -826,10 +826,10 @@ public interface Player extends MageItem, Copyable<Player> {
* without mana (null) or the mana set to manaCosts instead of its normal
* mana costs.
*
* @param sourceId the source that can be cast without mana
* @param sourceId the source that can be cast without mana
* @param manaCosts alternate ManaCost, null if it can be cast without mana
* cost
* @param costs alternate other costs you need to pay
* cost
* @param costs alternate other costs you need to pay
*/
void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts<ManaCost> manaCosts, Costs<Cost> costs);

View file

@ -1041,15 +1041,15 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public boolean cast(SpellAbility ability, Game game, boolean noMana, MageObjectReference permittingObject) {
if (game == null || ability == null) {
public boolean cast(SpellAbility originalAbility, Game game, boolean noMana, MageObjectReference permittingObject) {
if (game == null || originalAbility == null) {
return false;
}
// Use ability copy to avoid problems with targets and costs on recast (issue https://github.com/magefree/mage/issues/5189).
ability = ability.copy();
SpellAbility ability = originalAbility.copy();
ability.setControllerId(getId());
ability.setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(ability.getSourceId()));
if (ability.getSpellAbilityType() != SpellAbilityType.BASE) {
ability = chooseSpellAbilityForCast(ability, game, noMana);
if (ability == null) {
@ -1073,6 +1073,8 @@ public abstract class PlayerImpl implements Player, Serializable {
logger.error("Got no spell from stack. ability: " + ability.getRule());
return false;
}
// Update the zcc to the stack
ability.setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(ability.getSourceId()));
// some effects set sourceId to cast without paying mana costs or other costs
if (ability.getSourceId().equals(getCastSourceIdWithAlternateMana())) {
Ability spellAbility = spell.getSpellAbility();
@ -1143,8 +1145,14 @@ public abstract class PlayerImpl implements Player, Serializable {
}
//20091005 - 114.2a
ActivationStatus activationStatus = playLandAbility.canActivate(this.playerId, game);
if (!ignoreTiming && !activationStatus.canActivate()) {
return false;
if (ignoreTiming) {
if (!canPlayLand()) {
return false; // ignore timing does not mean that more lands than normal can be played
}
} else {
if (!activationStatus.canActivate()) {
return false;
}
}
//20091005 - 305.1
@ -2364,7 +2372,9 @@ public abstract class PlayerImpl implements Player, Serializable {
setStoredBookmark(game.bookmarkState()); // makes it possible to UNDO a declared attacker with costs from e.g. Propaganda
}
Permanent attacker = game.getPermanent(attackerId);
if (attacker != null && attacker.canAttack(defenderId, game) && attacker.isControlledBy(playerId)) {
if (attacker != null
&& attacker.canAttack(defenderId, game)
&& attacker.isControlledBy(playerId)) {
if (!game.getCombat().declareAttacker(attackerId, defenderId, playerId, game)) {
game.undo(playerId);
}
@ -2463,13 +2473,14 @@ public abstract class PlayerImpl implements Player, Serializable {
for (UUID targetId : newTarget.getTargets()) {
target.add(targetId, game);
}
if (triggerEvents) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LIBRARY_SEARCHED, targetPlayerId, playerId));
}
} else if (targetPlayerId.equals(playerId) && handleLibraryCastableCards(library, game, targetPlayerId)) { // for handling Panglacial Wurm
newTarget.clearChosen();
continue;
}
if (triggerEvents) {
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LIBRARY_SEARCHED, targetPlayerId, playerId));
}
break;
} while (true);
return true;

View file

@ -68,7 +68,8 @@ public class TargetDefender extends TargetImpl {
}
}
for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) {
if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game))
if ((notTarget
|| permanent.canBeTargetedBy(targetSource, sourceControllerId, game))
&& filter.match(permanent, game)) {
count++;
if (count >= this.minNumberOfTargets) {
@ -84,7 +85,8 @@ public class TargetDefender extends TargetImpl {
int count = 0;
for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
Player player = game.getPlayer(playerId);
if (player != null && filter.match(player, game)) {
if (player != null
&& filter.match(player, game)) {
count++;
if (count >= this.minNumberOfTargets) {
return true;
@ -109,13 +111,15 @@ public class TargetDefender extends TargetImpl {
for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
Player player = game.getPlayer(playerId);
if (player != null
&& (notTarget || player.canBeTargetedBy(targetSource, sourceControllerId, game))
&& (notTarget
|| player.canBeTargetedBy(targetSource, sourceControllerId, game))
&& filter.match(player, game)) {
possibleTargets.add(playerId);
}
}
for (Permanent permanent : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, sourceControllerId, game)) {
if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game))
if ((notTarget
|| permanent.canBeTargetedBy(targetSource, sourceControllerId, game))
&& filter.match(permanent, game)) {
possibleTargets.add(permanent.getId());
}
@ -128,7 +132,8 @@ public class TargetDefender extends TargetImpl {
Set<UUID> possibleTargets = new HashSet<>();
for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
Player player = game.getPlayer(playerId);
if (player != null && filter.match(player, game)) {
if (player != null
&& filter.match(player, game)) {
possibleTargets.add(playerId);
}
}
@ -162,7 +167,8 @@ public class TargetDefender extends TargetImpl {
return filter.match(player, game);
}
Permanent permanent = game.getPermanent(id);
return permanent != null && filter.match(permanent, game);
return permanent != null
&& filter.match(permanent, game);
}
@Override
@ -170,7 +176,8 @@ public class TargetDefender extends TargetImpl {
Player player = game.getPlayer(id);
MageObject targetSource = game.getObject(attackerId);
if (player != null) {
return (notTarget || player.canBeTargetedBy(targetSource, (source == null ? null : source.getControllerId()), game))
return (notTarget
|| player.canBeTargetedBy(targetSource, (source == null ? null : source.getControllerId()), game))
&& filter.match(player, game);
}
Permanent permanent = game.getPermanent(id); // planeswalker
@ -180,7 +187,8 @@ public class TargetDefender extends TargetImpl {
if (source != null) {
controllerId = source.getControllerId();
}
return (notTarget || permanent.canBeTargetedBy(targetSource, controllerId, game))
return (notTarget
|| permanent.canBeTargetedBy(targetSource, controllerId, game))
&& filter.match(permanent, game);
}
return false;

View file

@ -362,7 +362,7 @@ public final class CardUtil {
/**
* Parse card number as int (support base [123] and alternative numbers
* [123b]).
* [123b], [U123]).
*
* @param cardNumber origin card number
* @return int
@ -373,10 +373,15 @@ public final class CardUtil {
throw new IllegalArgumentException("Card number is empty.");
}
if (Character.isDigit(cardNumber.charAt(cardNumber.length() - 1))) {
return Integer.parseInt(cardNumber);
} else {
if (!Character.isDigit(cardNumber.charAt(0))) {
// U123
return Integer.parseInt(cardNumber.substring(1, cardNumber.length()));
} else if (!Character.isDigit(cardNumber.charAt(cardNumber.length() - 1))) {
// 123b
return Integer.parseInt(cardNumber.substring(0, cardNumber.length() - 1));
} else {
// 123
return Integer.parseInt(cardNumber);
}
}

View file

@ -33,8 +33,8 @@ public final class ClassScanner {
if(classLoader == null) classLoader = Thread.currentThread().getContextClassLoader();
assert classLoader != null;
HashMap<String, String> dirs = new HashMap<>();
TreeSet<String> jars = new TreeSet<>();
Map<String, String> dirs = new HashMap<>();
Set<String> jars = new TreeSet<>();
for (String packageName : packages) {
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
@ -51,8 +51,8 @@ public final class ClassScanner {
}
}
for (String filePath : dirs.keySet()) {
cards.addAll(findClasses(classLoader, new File(filePath), dirs.get(filePath), type));
for (Map.Entry<String, String> dir : dirs.entrySet()) {
cards.addAll(findClasses(classLoader, new File(dir.getKey()), dir.getValue(), type));
}
for (String filePath : jars) {
@ -66,7 +66,7 @@ public final class ClassScanner {
private static List<Class> findClasses(ClassLoader classLoader, File directory, String packageName, Class<?> type) {
List<Class> cards = new ArrayList<>();
if (!directory.exists()) return cards;
if (directory == null || !directory.exists()) return cards;
for (File file : directory.listFiles()) {
if (file.getName().endsWith(".class")) {

View file

@ -0,0 +1,91 @@
package mage.watchers.common;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.PlayerList;
import mage.watchers.Watcher;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author JayDi85
*/
public class PlayersAttackedThisTurnWatcher extends Watcher {
// how many players or opponents each player attacked this turn
private final Map<UUID, PlayerList> playersAttackedThisTurn = new HashMap<>();
private final Map<UUID, PlayerList> opponentsAttackedThisTurn = new HashMap<>();
public PlayersAttackedThisTurnWatcher() {
super(PlayersAttackedThisTurnWatcher.class.getSimpleName(), WatcherScope.GAME);
}
public PlayersAttackedThisTurnWatcher(final PlayersAttackedThisTurnWatcher watcher) {
super(watcher);
for (Map.Entry<UUID, PlayerList> entry : watcher.playersAttackedThisTurn.entrySet()) {
this.playersAttackedThisTurn.put(entry.getKey(), entry.getValue());
}
for (Map.Entry<UUID, PlayerList> entry : watcher.opponentsAttackedThisTurn.entrySet()) {
this.opponentsAttackedThisTurn.put(entry.getKey(), entry.getValue());
}
}
@Override
public PlayersAttackedThisTurnWatcher copy() {
return new PlayersAttackedThisTurnWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.BEGINNING_PHASE_PRE) {
playersAttackedThisTurn.clear();
opponentsAttackedThisTurn.clear();
}
if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED) {
// players
PlayerList playersAttacked = playersAttackedThisTurn.get(event.getPlayerId());
if (playersAttacked == null) {
playersAttacked = new PlayerList();
}
UUID playerDefender = game.getCombat().getDefendingPlayerId(event.getSourceId(), game);
if (playerDefender != null) {
playersAttacked.add(playerDefender);
}
playersAttackedThisTurn.put(event.getPlayerId(), playersAttacked);
// opponents
PlayerList opponentsAttacked = opponentsAttackedThisTurn.get(event.getPlayerId());
if (opponentsAttacked == null) {
opponentsAttacked = new PlayerList();
}
UUID opponentDefender = game.getCombat().getDefendingPlayerId(event.getSourceId(), game);
if (opponentDefender != null && game.getOpponents(event.getPlayerId()).contains(opponentDefender)) {
opponentsAttacked.add(opponentDefender);
}
opponentsAttackedThisTurn.put(event.getPlayerId(), opponentsAttacked);
}
}
public int getAttackedPlayersCount(UUID playerID) {
PlayerList defendersList = playersAttackedThisTurn.getOrDefault(playerID, null);
if (defendersList != null) {
return defendersList.size();
}
return 0;
}
public int getAttackedOpponentsCount(UUID playerID) {
PlayerList defendersList = opponentsAttackedThisTurn.getOrDefault(playerID, null);
if (defendersList != null) {
return defendersList.size();
}
return 0;
}
}