mirror of
https://github.com/magefree/mage.git
synced 2025-12-24 12:31:59 -08:00
Merge branch 'master' into add-minimum-rating-option
This commit is contained in:
commit
e7d129a074
568 changed files with 21414 additions and 6453 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, ";
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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, ";
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.common.delayed;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import mage.abilities.keyword.SpliceOntoArcaneAbility;
|
|||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.SplitCardHalf;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicate;
|
||||
|
|
@ -488,15 +489,23 @@ public class ContinuousEffects implements Serializable {
|
|||
*/
|
||||
public MageObjectReference asThough(UUID objectId, AsThoughEffectType type, Ability affectedAbility, UUID controllerId, Game game) {
|
||||
List<AsThoughEffect> asThoughEffectsList = getApplicableAsThoughEffects(type, game);
|
||||
for (AsThoughEffect effect : asThoughEffectsList) {
|
||||
Set<Ability> abilities = asThoughEffectsMap.get(type).getAbility(effect.getId());
|
||||
for (Ability ability : abilities) {
|
||||
if (affectedAbility == null) {
|
||||
if (effect.applies(objectId, ability, controllerId, game)) {
|
||||
if (!asThoughEffectsList.isEmpty()) {
|
||||
UUID idToCheck;
|
||||
if (affectedAbility != null && affectedAbility.getSourceObject(game) instanceof SplitCardHalf) {
|
||||
idToCheck = ((SplitCardHalf) affectedAbility.getSourceObject(game)).getParentCard().getId();
|
||||
} else {
|
||||
idToCheck = objectId;
|
||||
}
|
||||
for (AsThoughEffect effect : asThoughEffectsList) {
|
||||
Set<Ability> abilities = asThoughEffectsMap.get(type).getAbility(effect.getId());
|
||||
for (Ability ability : abilities) {
|
||||
if (affectedAbility == null) {
|
||||
if (effect.applies(idToCheck, ability, controllerId, game)) {
|
||||
return new MageObjectReference(ability.getSourceObject(game), game);
|
||||
}
|
||||
} else if (effect.applies(idToCheck, affectedAbility, ability, game, controllerId)) {
|
||||
return new MageObjectReference(ability.getSourceObject(game), game);
|
||||
}
|
||||
} else if (effect.applies(objectId, affectedAbility, ability, game, controllerId)) {
|
||||
return new MageObjectReference(ability.getSourceObject(game), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -39,7 +38,9 @@ public class AttachEffect extends OneShotEffect {
|
|||
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
|
||||
if (sourcePermanent != null) {
|
||||
int zcc = game.getState().getZoneChangeCounter(sourcePermanent.getId());
|
||||
if (zcc == source.getSourceObjectZoneChangeCounter() || zcc == source.getSourceObjectZoneChangeCounter() + 1) {
|
||||
if (zcc == source.getSourceObjectZoneChangeCounter()
|
||||
|| zcc == source.getSourceObjectZoneChangeCounter() + 1
|
||||
|| zcc == source.getSourceObjectZoneChangeCounter() + 2) {
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (permanent != null) {
|
||||
return permanent.addAttachment(source.getSourceId(), game);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package mage.abilities.effects.common.asthought;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class PlayFromNotOwnHandZoneAllEffect extends AsThoughEffectImpl {
|
||||
|
||||
private final FilterCard filter;
|
||||
private final Zone fromZone;
|
||||
private final boolean onlyOwnedCards;
|
||||
private final TargetController allowedCaster;
|
||||
|
||||
public PlayFromNotOwnHandZoneAllEffect(FilterCard filter, Zone fromZone, boolean onlyOwnedCards, TargetController allowedCaster, Duration duration) {
|
||||
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit);
|
||||
this.filter = filter;
|
||||
this.fromZone = fromZone;
|
||||
this.onlyOwnedCards = onlyOwnedCards;
|
||||
this.allowedCaster = allowedCaster;
|
||||
}
|
||||
|
||||
public PlayFromNotOwnHandZoneAllEffect(final PlayFromNotOwnHandZoneAllEffect effect) {
|
||||
super(effect);
|
||||
this.filter = effect.filter;
|
||||
this.fromZone = effect.fromZone;
|
||||
this.onlyOwnedCards = effect.onlyOwnedCards;
|
||||
this.allowedCaster = effect.allowedCaster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayFromNotOwnHandZoneAllEffect copy() {
|
||||
return new PlayFromNotOwnHandZoneAllEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
Card card = game.getCard(objectId);
|
||||
if (card != null) {
|
||||
switch (allowedCaster) {
|
||||
case YOU:
|
||||
if (affectedControllerId != source.getControllerId()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case OPPONENT:
|
||||
if (!game.getOpponents(source.getControllerId()).contains(affectedControllerId)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return !onlyOwnedCards || card.getOwnerId().equals(source.getControllerId())
|
||||
&& filter.match(card, game)
|
||||
&& game.getState().getZone(card.getId()).match(fromZone);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package mage.abilities.effects.common.asthought;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.constants.AsThoughEffectType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class PlayFromNotOwnHandZoneTargetEffect extends AsThoughEffectImpl {
|
||||
|
||||
private final Zone fromZone;
|
||||
private final TargetController allowedCaster;
|
||||
|
||||
public PlayFromNotOwnHandZoneTargetEffect() {
|
||||
this(Duration.EndOfTurn);
|
||||
}
|
||||
|
||||
public PlayFromNotOwnHandZoneTargetEffect(Duration duration) {
|
||||
this(Zone.ALL, TargetController.YOU, duration);
|
||||
}
|
||||
|
||||
public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, Duration duration) {
|
||||
this(fromZone, TargetController.YOU, duration);
|
||||
}
|
||||
|
||||
public PlayFromNotOwnHandZoneTargetEffect(Zone fromZone, TargetController allowedCaster, Duration duration) {
|
||||
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, duration, Outcome.Benefit);
|
||||
this.fromZone = fromZone;
|
||||
this.allowedCaster = allowedCaster;
|
||||
}
|
||||
|
||||
public PlayFromNotOwnHandZoneTargetEffect(final PlayFromNotOwnHandZoneTargetEffect effect) {
|
||||
super(effect);
|
||||
this.fromZone = effect.fromZone;
|
||||
this.allowedCaster = effect.allowedCaster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayFromNotOwnHandZoneTargetEffect copy() {
|
||||
return new PlayFromNotOwnHandZoneTargetEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
switch (allowedCaster) {
|
||||
case YOU:
|
||||
if (affectedControllerId != source.getControllerId()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case OPPONENT:
|
||||
if (!game.getOpponents(source.getControllerId()).contains(affectedControllerId)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case ANY:
|
||||
break;
|
||||
}
|
||||
List<UUID> targets = getTargetPointer().getTargets(game, source);
|
||||
if (targets.isEmpty()) {
|
||||
this.discard();
|
||||
return false;
|
||||
}
|
||||
return targets.contains(objectId)
|
||||
&& affectedControllerId.equals(source.getControllerId())
|
||||
&& game.getState().getZone(objectId).match(fromZone);
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.effects.common.counter;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.abilities.keyword;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -68,16 +67,19 @@ class AftermathCastFromGraveyard extends AsThoughEffectImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
if (objectId.equals(source.getSourceId())
|
||||
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID affectedControllerId) {
|
||||
if (affectedAbility != null && affectedAbility.getSourceId().equals(source.getSourceId())
|
||||
&& affectedControllerId.equals(source.getControllerId())) {
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) {
|
||||
return true;
|
||||
}
|
||||
return game.getState().getZone(objectId).equals(Zone.GRAVEYARD);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AftermathCantCastFromHand extends ContinuousRuleModifyingEffectImpl {
|
||||
|
|
@ -168,17 +170,14 @@ class AftermathExileAsResolvesFromGraveyard extends ReplacementEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
UUID sourceId = source.getSourceId();
|
||||
Card sourceCard = game.getCard(source.getSourceId());
|
||||
if (sourceCard instanceof SplitCardHalf) {
|
||||
sourceCard = ((SplitCardHalf) sourceCard).getParentCard();
|
||||
sourceId = sourceCard.getId();
|
||||
}
|
||||
|
||||
if (sourceCard != null) {
|
||||
Player player = game.getPlayer(sourceCard.getOwnerId());
|
||||
if (player != null) {
|
||||
return player.moveCardToExileWithInfo(sourceCard, null, "", sourceId, game, ((ZoneChangeEvent) event).getFromZone(), true);
|
||||
return player.moveCards(sourceCard, Zone.EXILED, source, game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ public final class CardGraphicInfo {
|
|||
|
||||
private final ObjectColor frameColor;
|
||||
private final FrameStyle frameStyle;
|
||||
private final boolean useVariousArt;
|
||||
private final boolean useVariousArt; // card in set have multiple images (use to store images files)
|
||||
|
||||
public CardGraphicInfo(FrameStyle frameStyle, boolean useVariousArt) {
|
||||
this(null, frameStyle, useVariousArt);
|
||||
|
|
|
|||
|
|
@ -21,12 +21,7 @@ public final class CardSetInfo implements Serializable {
|
|||
this.expansionSetCode = expansionSetCode;
|
||||
this.cardNumber = cardNumber;
|
||||
this.rarity = rarity;
|
||||
if (graphicInfo == null && Rarity.LAND == rarity) {
|
||||
// Workaround to get images of basic land permanents loaded
|
||||
this.graphicInfo = ExpansionSet.NON_FULL_USE_VARIOUS;
|
||||
} else {
|
||||
this.graphicInfo = graphicInfo;
|
||||
}
|
||||
this.graphicInfo = graphicInfo;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ public abstract class ExpansionSet implements Serializable {
|
|||
protected Date releaseDate;
|
||||
protected ExpansionSet parentSet;
|
||||
protected SetType setType;
|
||||
|
||||
// TODO: 03.10.2018, hasBasicLands can be removed someday -- it's uses to optimize lands search in deck generation and lands adding (search all available lands from sets)
|
||||
protected boolean hasBasicLands = true;
|
||||
|
||||
protected String blockName;
|
||||
|
|
@ -559,6 +561,7 @@ public abstract class ExpansionSet implements Serializable {
|
|||
if (savedCardsInfos == null) {
|
||||
CardCriteria criteria = new CardCriteria();
|
||||
if (rarity == Rarity.LAND) {
|
||||
// get basic lands from parent set if current haven't it
|
||||
criteria.setCodes(!hasBasicLands && parentSet != null ? parentSet.code : this.code);
|
||||
} else {
|
||||
criteria.setCodes(this.code);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public enum CardRepository {
|
|||
// raise this if db structure was changed
|
||||
private static final long CARD_DB_VERSION = 51;
|
||||
// raise this if new cards were added to the server
|
||||
private static final long CARD_CONTENT_VERSION = 120;
|
||||
private static final long CARD_CONTENT_VERSION = 122;
|
||||
private Dao<CardInfo, Object> cardDao;
|
||||
private Set<String> classNames;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -1,29 +1,30 @@
|
|||
package mage.constants;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public enum Rarity {
|
||||
|
||||
LAND ("Land", "common", "C", 1),
|
||||
COMMON ("Common", "common", "C", 1),
|
||||
UNCOMMON ("Uncommon", "uncommon", "U", 2),
|
||||
RARE ("Rare", "rare", "R", 3),
|
||||
MYTHIC ("Mythic", "mythic", "M", 3),
|
||||
SPECIAL ("Special", "special", "Special", 3),
|
||||
BONUS ("Bonus", "bonus", "Bonus", 3);
|
||||
LAND("Land", "common", "C", 1, 1),
|
||||
COMMON("Common", "common", "C", 1, 2),
|
||||
UNCOMMON("Uncommon", "uncommon", "U", 2, 3),
|
||||
RARE("Rare", "rare", "R", 3, 4),
|
||||
MYTHIC("Mythic", "mythic", "M", 3, 5),
|
||||
SPECIAL("Special", "special", "Special", 3, 6),
|
||||
BONUS("Bonus", "bonus", "Bonus", 3, 7);
|
||||
|
||||
private final String text;
|
||||
private final String symbolCode;
|
||||
private final String code;
|
||||
private final int rating;
|
||||
private final int sorting;
|
||||
|
||||
Rarity(String text, String symbolCode, String code, int rating) {
|
||||
Rarity(String text, String symbolCode, String code, int rating, int sorting) {
|
||||
this.text = text;
|
||||
this.symbolCode = symbolCode;
|
||||
this.code = code;
|
||||
this.rating = rating;
|
||||
this.sorting = sorting;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -42,4 +43,8 @@ public enum Rarity {
|
|||
public int getRating() {
|
||||
return rating;
|
||||
}
|
||||
|
||||
public int getSorting() {
|
||||
return sorting;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ public enum SubType {
|
|||
CHIMERA("Chimera", SubTypeSet.CreatureType),
|
||||
CHISS("Chiss", SubTypeSet.CreatureType, true),
|
||||
CITIZEN("Citizen", SubTypeSet.CreatureType),
|
||||
CLAMFOLK("Clamfolk", SubTypeSet.CreatureType, true), // Unglued
|
||||
CLERIC("Cleric", SubTypeSet.CreatureType),
|
||||
COCKATRICE("Cockatrice", SubTypeSet.CreatureType),
|
||||
CONSTRUCT("Construct", SubTypeSet.CreatureType),
|
||||
|
|
@ -131,6 +132,7 @@ public enum SubType {
|
|||
ELK("Elk", SubTypeSet.CreatureType),
|
||||
EYE("Eye", SubTypeSet.CreatureType),
|
||||
EWOK("Ewok", SubTypeSet.CreatureType, true), // Star Wars
|
||||
EXPANSION_SYMBOL("Expansion-Symbol", SubTypeSet.CreatureType, true), // Unhinged
|
||||
// F
|
||||
FAERIE("Faerie", SubTypeSet.CreatureType),
|
||||
FERRET("Ferret", SubTypeSet.CreatureType),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ public enum CounterType {
|
|||
|
||||
AGE("age"),
|
||||
AIM("aim"),
|
||||
ARROW("arrow"),
|
||||
ARROWHEAD("arrowhead"),
|
||||
AWAKENING("awakening"),
|
||||
BLAZE("blaze"),
|
||||
|
|
@ -19,6 +20,7 @@ public enum CounterType {
|
|||
CAGE("cage"),
|
||||
CARRION("carrion"),
|
||||
CHARGE("charge"),
|
||||
CHIP("chip"),
|
||||
CORPSE("corpse"),
|
||||
CREDIT("credit"),
|
||||
CRYSTAL("crystal"),
|
||||
|
|
@ -44,6 +46,7 @@ public enum CounterType {
|
|||
FEATHER("feather"),
|
||||
FILIBUSTER("filibuster"),
|
||||
FLOOD("flood"),
|
||||
FUNK("funk"),
|
||||
FURY("fury"),
|
||||
FUNGUS("fungus"),
|
||||
FUSE("fuse"),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ public interface Controllable {
|
|||
UUID getId();
|
||||
|
||||
default boolean isControlledBy(UUID controllerID){
|
||||
if(getControllerId() == null){
|
||||
return false;
|
||||
}
|
||||
return getControllerId().equals(controllerID);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.game.command;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
package mage.game.permanent.token;
|
||||
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class ElfKnightToken extends TokenImpl {
|
||||
|
||||
public ElfKnightToken() {
|
||||
super("Knight Ally", "2/2 green and white Elf Knight creature token with vigilance");
|
||||
super("Elf Knight", "2/2 green and white Elf Knight creature token with vigilance");
|
||||
this.setExpansionSetCodeForImage("GRN");
|
||||
cardType.add(CardType.CREATURE);
|
||||
color.setGreen(true);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
package mage.game.permanent.token;
|
||||
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.MageInt;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public final class ExpansionSymbolToken extends TokenImpl {
|
||||
|
||||
public ExpansionSymbolToken() {
|
||||
super("Expansion-Symbol", "1/1 colorless Expansion-Symbol creature token");
|
||||
cardType.add(CardType.CREATURE);
|
||||
subtype.add(SubType.EXPANSION_SYMBOL);
|
||||
power = new MageInt(1);
|
||||
toughness = new MageInt(1);
|
||||
}
|
||||
|
||||
public ExpansionSymbolToken(final ExpansionSymbolToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
public ExpansionSymbolToken copy() {
|
||||
return new ExpansionSymbolToken(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
package mage.game.permanent.token;
|
||||
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.MageInt;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public final class RabidSheepToken extends TokenImpl {
|
||||
|
||||
public RabidSheepToken() {
|
||||
super("Rabid Sheep", "2/2 green Sheep creature token named Rabid Sheep");
|
||||
cardType.add(CardType.CREATURE);
|
||||
subtype.add(SubType.SHEEP);
|
||||
color.setGreen(true);
|
||||
power = new MageInt(2);
|
||||
toughness = new MageInt(2);
|
||||
}
|
||||
|
||||
public RabidSheepToken(final RabidSheepToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
public RabidSheepToken copy() {
|
||||
return new RabidSheepToken(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
package mage.game.permanent.token;
|
||||
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.MageInt;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public final class UktabiKongApeToken extends TokenImpl {
|
||||
|
||||
public UktabiKongApeToken() {
|
||||
super("Ape", "1/1 green Ape creature token");
|
||||
cardType.add(CardType.CREATURE);
|
||||
subtype.add(SubType.APE);
|
||||
color.setGreen(true);
|
||||
power = new MageInt(1);
|
||||
toughness = new MageInt(1);
|
||||
}
|
||||
|
||||
public UktabiKongApeToken(final UktabiKongApeToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
public UktabiKongApeToken copy() {
|
||||
return new UktabiKongApeToken(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -355,22 +354,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);
|
||||
|
|
@ -516,11 +516,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);
|
||||
|
|
@ -541,10 +541,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);
|
||||
|
|
@ -570,8 +570,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
|
||||
|
|
@ -710,11 +710,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
|
||||
*/
|
||||
|
|
@ -750,7 +750,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
|
||||
|
|
@ -792,7 +792,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
|
||||
*/
|
||||
|
|
@ -817,10 +817,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);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
package mage.players;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import mage.ConditionalMana;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
|
|
@ -68,11 +72,6 @@ import mage.util.GameLog;
|
|||
import mage.util.RandomUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public abstract class PlayerImpl implements Player, Serializable {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(PlayerImpl.class);
|
||||
|
|
@ -1042,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) {
|
||||
|
|
@ -1074,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();
|
||||
|
|
@ -1144,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
|
||||
|
|
@ -2365,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);
|
||||
}
|
||||
|
|
@ -2464,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;
|
||||
|
|
@ -2563,7 +2573,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
/**
|
||||
* @param game
|
||||
* @param appliedEffects
|
||||
* @param numSides Number of sides the dice has
|
||||
* @param numSides Number of sides the dice has
|
||||
* @return the number that the player rolled
|
||||
*/
|
||||
@Override
|
||||
|
|
@ -2597,10 +2607,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
/**
|
||||
* @param game
|
||||
* @param appliedEffects
|
||||
* @param numberChaosSides The number of chaos sides the planar die
|
||||
* currently has (normally 1 but can be 5)
|
||||
* @param numberChaosSides The number of chaos sides the planar die
|
||||
* currently has (normally 1 but can be 5)
|
||||
* @param numberPlanarSides The number of chaos sides the planar die
|
||||
* currently has (normally 1)
|
||||
* currently has (normally 1)
|
||||
* @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
|
||||
* or NilRoll
|
||||
*/
|
||||
|
|
@ -2757,7 +2767,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
/**
|
||||
* @param ability
|
||||
* @param available if null, it won't be checked if enough mana is available
|
||||
* @param available if null, it won't be checked if enough mana is available
|
||||
* @param sourceObject
|
||||
* @param game
|
||||
* @return
|
||||
|
|
@ -2927,7 +2937,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
|
||||
private void getPlayableFromGraveyardCard(Game game, Card card, Abilities<Ability> candidateAbilities, ManaOptions availableMana, List<Ability> output) {
|
||||
MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, this.getId(), game);
|
||||
MageObjectReference permittingObject = game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), this.getId(), game);
|
||||
for (ActivatedAbility ability : candidateAbilities.getActivatedAbilities(Zone.ALL)) {
|
||||
boolean possible = false;
|
||||
if (ability.getZone().match(Zone.GRAVEYARD)) {
|
||||
|
|
@ -3033,7 +3043,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (player != null) {
|
||||
if (/*player.isTopCardRevealed() &&*/player.getLibrary().hasCards()) {
|
||||
Card card = player.getLibrary().getFromTop(game);
|
||||
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, null, getId(), game)) {
|
||||
if (null != game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, card.getSpellAbility(), getId(), game)) {
|
||||
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.HAND)) {
|
||||
if (ability instanceof SpellAbility || ability instanceof PlayLandAbility) {
|
||||
playable.add(ability);
|
||||
|
|
@ -3309,7 +3319,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
|
||||
UUID controllerId, Game game
|
||||
UUID controllerId, Game game
|
||||
) {
|
||||
return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
|
||||
}
|
||||
|
|
@ -3457,8 +3467,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCards(Card card, Zone toZone,
|
||||
Ability source, Game game,
|
||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||
Ability source, Game game,
|
||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||
) {
|
||||
Set<Card> cardList = new HashSet<>();
|
||||
if (card != null) {
|
||||
|
|
@ -3469,22 +3479,22 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCards(Cards cards, Zone toZone,
|
||||
Ability source, Game game
|
||||
Ability source, Game game
|
||||
) {
|
||||
return moveCards(cards.getCards(game), toZone, source, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveCards(Set<Card> cards, Zone toZone,
|
||||
Ability source, Game game
|
||||
Ability source, Game game
|
||||
) {
|
||||
return moveCards(cards, toZone, source, game, false, false, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveCards(Set<Card> cards, Zone toZone,
|
||||
Ability source, Game game,
|
||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||
Ability source, Game game,
|
||||
boolean tapped, boolean faceDown, boolean byOwner, List<UUID> appliedEffects
|
||||
) {
|
||||
if (cards.isEmpty()) {
|
||||
return true;
|
||||
|
|
@ -3570,8 +3580,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardsToExile(Card card, Ability source,
|
||||
Game game, boolean withName, UUID exileId,
|
||||
String exileZoneName
|
||||
Game game, boolean withName, UUID exileId,
|
||||
String exileZoneName
|
||||
) {
|
||||
Set<Card> cards = new HashSet<>();
|
||||
cards.add(card);
|
||||
|
|
@ -3580,8 +3590,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardsToExile(Set<Card> cards, Ability source,
|
||||
Game game, boolean withName, UUID exileId,
|
||||
String exileZoneName
|
||||
Game game, boolean withName, UUID exileId,
|
||||
String exileZoneName
|
||||
) {
|
||||
if (cards.isEmpty()) {
|
||||
return true;
|
||||
|
|
@ -3596,14 +3606,14 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
|
||||
Game game
|
||||
Game game
|
||||
) {
|
||||
return this.moveCardToHandWithInfo(card, sourceId, game, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
|
||||
Game game, boolean withName
|
||||
Game game, boolean withName
|
||||
) {
|
||||
boolean result = false;
|
||||
Zone fromZone = game.getState().getZone(card.getId());
|
||||
|
|
@ -3628,7 +3638,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public Set<Card> moveCardsToGraveyardWithInfo(Set<Card> allCards, Ability source,
|
||||
Game game, Zone fromZone
|
||||
Game game, Zone fromZone
|
||||
) {
|
||||
UUID sourceId = source == null ? null : source.getSourceId();
|
||||
Set<Card> movedCards = new LinkedHashSet<>();
|
||||
|
|
@ -3636,7 +3646,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
// identify cards from one owner
|
||||
Cards cards = new CardsImpl();
|
||||
UUID ownerId = null;
|
||||
for (Iterator<Card> it = allCards.iterator(); it.hasNext(); ) {
|
||||
for (Iterator<Card> it = allCards.iterator(); it.hasNext();) {
|
||||
Card card = it.next();
|
||||
if (cards.isEmpty()) {
|
||||
ownerId = card.getOwnerId();
|
||||
|
|
@ -3697,7 +3707,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
|
||||
Game game, Zone fromZone
|
||||
Game game, Zone fromZone
|
||||
) {
|
||||
if (card == null) {
|
||||
return false;
|
||||
|
|
@ -3726,8 +3736,8 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
|
||||
Game game, Zone fromZone,
|
||||
boolean toTop, boolean withName
|
||||
Game game, Zone fromZone,
|
||||
boolean toTop, boolean withName
|
||||
) {
|
||||
if (card == null) {
|
||||
return false;
|
||||
|
|
@ -3761,7 +3771,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
|
||||
@Override
|
||||
public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
|
||||
Game game, Zone fromZone, boolean withName) {
|
||||
Game game, Zone fromZone, boolean withName) {
|
||||
if (card == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
|
||||
package mage.target;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.filter.FilterPlayer;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ public class FixedTarget implements TargetPointer {
|
|||
this.initialized = false;
|
||||
}
|
||||
|
||||
public FixedTarget(Card card, Game game) {
|
||||
this.targetId = card.getId();
|
||||
this.zoneChangeCounter = card.getZoneChangeCounter(game);
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
public FixedTarget(Permanent permanent, Game game) {
|
||||
this.targetId = permanent.getId();
|
||||
this.zoneChangeCounter = permanent.getZoneChangeCounter(game);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue