mirror of
https://github.com/correl/mage.git
synced 2024-12-26 11:09:27 +00:00
Merge pull request #6585 from magefree/abilities_refactor
Abilities refactor and some fixes
This commit is contained in:
commit
37b197eab7
104 changed files with 613 additions and 307 deletions
|
@ -1267,7 +1267,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
//play a land that will allow us to play an unplayable
|
||||
for (Mana mana : unplayable.keySet()) {
|
||||
for (Card card : lands) {
|
||||
for (ActivatedManaAbilityImpl ability : card.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD)) {
|
||||
for (ActivatedManaAbilityImpl ability : card.getAbilities(game).getActivatedManaAbilities(Zone.BATTLEFIELD)) {
|
||||
for (Mana netMana : ability.getNetMana(game)) {
|
||||
if (netMana.enough(mana)) {
|
||||
this.playLand(card, game, false);
|
||||
|
@ -1281,7 +1281,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
//play a land that will get us closer to playing an unplayable
|
||||
for (Mana mana : unplayable.keySet()) {
|
||||
for (Card card : lands) {
|
||||
for (ActivatedManaAbilityImpl ability : card.getAbilities().getActivatedManaAbilities(Zone.BATTLEFIELD)) {
|
||||
for (ActivatedManaAbilityImpl ability : card.getAbilities(game).getActivatedManaAbilities(Zone.BATTLEFIELD)) {
|
||||
for (Mana netMana : ability.getNetMana(game)) {
|
||||
if (mana.contains(netMana)) {
|
||||
this.playLand(card, game, false);
|
||||
|
@ -1321,7 +1321,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
if (ability != null && ability.canActivate(playerId, game).canActivate()
|
||||
&& !game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getSourceId(), ability.getSourceId(), playerId), ability, game, true)) {
|
||||
if (card.getCardType().contains(CardType.INSTANT)
|
||||
|| card.hasAbility(FlashAbility.getInstance().getId(), game)) {
|
||||
|| card.hasAbility(FlashAbility.getInstance(), game)) {
|
||||
playableInstant.add(card);
|
||||
} else {
|
||||
playableNonInstant.add(card);
|
||||
|
@ -1362,7 +1362,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
}
|
||||
for (Card card : graveyard.getCards(game)) {
|
||||
for (ActivatedAbility ability : card.getAbilities().getActivatedAbilities(Zone.GRAVEYARD)) {
|
||||
for (ActivatedAbility ability : card.getAbilities(game).getActivatedAbilities(Zone.GRAVEYARD)) {
|
||||
if (ability.canActivate(playerId, game).canActivate()) {
|
||||
ManaOptions abilityOptions = ability.getManaCosts().getOptions();
|
||||
if (abilityOptions.isEmpty()) {
|
||||
|
|
|
@ -225,9 +225,7 @@ class AnimateDeadChangeAbilityEffect extends ContinuousEffectImpl implements Sou
|
|||
abilityToRemove = ability;
|
||||
}
|
||||
}
|
||||
if (abilityToRemove != null) {
|
||||
permanent.getAbilities().remove(abilityToRemove);
|
||||
}
|
||||
permanent.removeAbility(abilityToRemove, source.getSourceId(), game);
|
||||
permanent.addAbility(newAbility, source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ class AnjeFalkenrathTriggeredAbility extends TriggeredAbilityImpl {
|
|||
if (card == null) {
|
||||
return false;
|
||||
}
|
||||
return card.getAbilities(game).stream().anyMatch(ability -> ability instanceof MadnessAbility);
|
||||
return card.getAbilities(game).containsClass(MadnessAbility.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -63,7 +63,7 @@ class ArtisanOfFormsApplyToPermanent extends ApplyToPermanent {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) {
|
||||
permanent.addAbility(ArtisanOfForms.createAbility(), game);
|
||||
permanent.addAbility(ArtisanOfForms.createAbility(), source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
package mage.cards.b;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
|
@ -70,7 +72,13 @@ class BloodSunEffect extends ContinuousEffectImpl {
|
|||
for (Permanent permanent : game.getState().getBattlefield().getActivePermanents(StaticFilters.FILTER_LANDS, player.getId(), source.getSourceId(), game)) {
|
||||
switch (layer) {
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
permanent.getAbilities().removeIf(ability -> ability.getAbilityType() != AbilityType.MANA);
|
||||
List<Ability> toRemove = new ArrayList<>();
|
||||
permanent.getAbilities().forEach(a -> {
|
||||
if (a.getAbilityType() != AbilityType.MANA) {
|
||||
toRemove.add(a);
|
||||
}
|
||||
});
|
||||
permanent.removeAbilities(toRemove, source.getSourceId(), game);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,8 +122,8 @@ class BludgeonBrawlGainAbilityEffect extends ContinuousEffectImpl {
|
|||
Permanent permanent = game.getPermanent(permanentId);
|
||||
if (permanent != null) {
|
||||
int convertedManaCost = permanent.getConvertedManaCost();
|
||||
permanent.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(convertedManaCost)), game);
|
||||
permanent.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(convertedManaCost, 0)), game);
|
||||
permanent.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(convertedManaCost)), source.getSourceId(), game);
|
||||
permanent.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(convertedManaCost, 0)), source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -133,17 +133,14 @@ class BronzehideLionContinuousEffect extends ContinuousEffectImpl {
|
|||
if (game.getState().getZoneChangeCounter(source.getSourceId()) > zoneChangeCounter) {
|
||||
discard();
|
||||
}
|
||||
MageObject sourceObject = game.getPermanent(source.getSourceId());
|
||||
Permanent sourceObject = game.getPermanent(source.getSourceId());
|
||||
if (sourceObject == null) {
|
||||
sourceObject = game.getPermanentEntering(source.getSourceId());
|
||||
}
|
||||
if (sourceObject == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(sourceObject instanceof Permanent)) {
|
||||
return true;
|
||||
}
|
||||
Permanent lion = (Permanent) sourceObject;
|
||||
Permanent lion = sourceObject;
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
lion.getCardType().clear();
|
||||
|
@ -158,7 +155,7 @@ class BronzehideLionContinuousEffect extends ContinuousEffectImpl {
|
|||
toRemove.add(ability);
|
||||
}
|
||||
}
|
||||
lion.getAbilities(game).removeAll(toRemove);
|
||||
lion.removeAbilities(toRemove, source.getSourceId(), game);
|
||||
|
||||
lion.getSpellAbility().getTargets().clear();
|
||||
lion.getSpellAbility().getEffects().clear();
|
||||
|
|
|
@ -89,7 +89,7 @@ class GainReboundEffect extends ContinuousEffectImpl {
|
|||
|
||||
private void addReboundAbility(Card card, Game game) {
|
||||
if (CastThroughTime.filter.match(card, game)) {
|
||||
boolean found = card.getAbilities(game).stream().anyMatch(ability -> ability instanceof ReboundAbility);
|
||||
boolean found = card.getAbilities(game).containsClass(ReboundAbility.class);
|
||||
if (!found) {
|
||||
Ability ability = new ReboundAbility();
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
|
|
|
@ -82,7 +82,7 @@ class ChaosphereEffect extends RestrictionEffect {
|
|||
if (attacker == null) {
|
||||
return true;
|
||||
}
|
||||
return attacker.hasAbility(FlyingAbility.getInstance().getId(), game);
|
||||
return attacker.hasAbility(FlyingAbility.getInstance(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -71,7 +71,7 @@ class CrimsonRocTriggeredAbility extends TriggeredAbilityImpl {
|
|||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
return permanent != null
|
||||
&& permanent.isCreature()
|
||||
&& !permanent.getAbilities(game).contains(FlyingAbility.getInstance());
|
||||
&& !permanent.hasAbility(FlyingAbility.getInstance(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -56,8 +56,8 @@ class DacksDuplicateApplyToPermanent extends ApplyToPermanent {
|
|||
* 29/05/2014 The ability of Dack's Duplicate doesn't target the
|
||||
* creature.
|
||||
*/
|
||||
permanent.addAbility(new DethroneAbility(), game);
|
||||
permanent.addAbility(HasteAbility.getInstance(), game);
|
||||
permanent.addAbility(new DethroneAbility(), source.getSourceId(), game);
|
||||
permanent.addAbility(HasteAbility.getInstance(), source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -240,9 +240,7 @@ class DanceOfTheDeadChangeAbilityEffect extends ContinuousEffectImpl implements
|
|||
abilityToRemove = ability;
|
||||
}
|
||||
}
|
||||
if (abilityToRemove != null) {
|
||||
permanent.getAbilities().remove(abilityToRemove);
|
||||
}
|
||||
permanent.removeAbility(abilityToRemove, source.getSourceId(), game);
|
||||
permanent.addAbility(newAbility, source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -22,8 +22,12 @@ public final class DeadlyRecluse extends CardImpl {
|
|||
|
||||
this.subtype.add(SubType.SPIDER);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Reach (This creature can block creatures with flying.)
|
||||
this.addAbility(ReachAbility.getInstance());
|
||||
|
||||
// Deathtouch (Any amount of damage this deals to a creature is enough to destroy it.)
|
||||
this.addAbility(DeathtouchAbility.getInstance());
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ class DenseCanopyCantBlockEffect extends RestrictionEffect {
|
|||
if (attacker == null) {
|
||||
return true;
|
||||
}
|
||||
return attacker.hasAbility(FlyingAbility.getInstance().getId(), game);
|
||||
return attacker.hasAbility(FlyingAbility.getInstance(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -69,7 +69,7 @@ class EvilTwinApplyToPermanent extends ApplyToPermanent {
|
|||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{U}{B}"));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||
permanent.addAbility(ability, game);
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ class ExcavatorEffect extends ContinuousEffectImpl implements SourceEffect {
|
|||
if (permanent != null) {
|
||||
for(Ability ability : abilities)
|
||||
{
|
||||
permanent.addAbility(ability, source.getSourceId(), game, false);
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,8 +51,6 @@ enum FlameSweepPredicate implements ObjectPlayerPredicate<ObjectPlayer<Permanent
|
|||
Permanent object = input.getObject();
|
||||
UUID playerId = input.getPlayerId();
|
||||
return !(object.isControlledBy(playerId)
|
||||
&& object.getAbilities(game).stream().anyMatch(
|
||||
ability -> ability.getClass().equals(FlyingAbility.class)
|
||||
));
|
||||
&& object.getAbilities(game).containsClass(FlyingAbility.class));
|
||||
}
|
||||
}
|
|
@ -66,7 +66,7 @@ class GargoyleSentinelEffect extends ContinuousEffectImpl {
|
|||
switch (layer) {
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
permanent.getAbilities().removeIf(entry -> entry.getId().equals(DefenderAbility.getInstance().getId()));
|
||||
permanent.removeAbility(DefenderAbility.getInstance(), source.getSourceId(), game);
|
||||
permanent.getAbilities().add(FlyingAbility.getInstance());
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -100,7 +100,7 @@ class GravityWellEffect extends ContinuousEffectImpl {
|
|||
switch (layer) {
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
permanent.getAbilities().removeIf(entry -> entry.getId().equals(FlyingAbility.getInstance().getId()));
|
||||
permanent.removeAbility(FlyingAbility.getInstance(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ class HisokasGuardGainAbilityTargetEffect extends ContinuousEffectImpl {
|
|||
if (hisokasGuard != null && !hisokasGuard.getConnectedCards("HisokasGuard").isEmpty()) {
|
||||
Permanent guardedCreature = game.getPermanent(hisokasGuard.getConnectedCards("HisokasGuard").get(0));
|
||||
if (guardedCreature != null && hisokasGuard.isTapped()) {
|
||||
guardedCreature.addAbility(ability, game);
|
||||
guardedCreature.addAbility(ability, source.getSourceId(), game);
|
||||
return true;
|
||||
} else {
|
||||
// if guard isn't tapped, the effect is no more valid
|
||||
|
|
|
@ -66,7 +66,7 @@ class MartyrdomGainAbilityTargetEffect extends ContinuousEffectImpl {
|
|||
if (permanent != null) {
|
||||
ActivatedAbilityImpl ability = new MartyrdomActivatedAbility(source.getControllerId());
|
||||
ability.setMayActivate(TargetController.ANY);
|
||||
permanent.addAbility(ability, source.getSourceId(), game, false);
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -149,7 +149,7 @@ class MeliraSylvokOutcastEffect3 extends ContinuousEffectImpl {
|
|||
Set<UUID> opponents = game.getOpponents(source.getControllerId());
|
||||
for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) {
|
||||
if (opponents.contains(perm.getControllerId())) {
|
||||
perm.getAbilities().remove(InfectAbility.getInstance());
|
||||
perm.removeAbility(InfectAbility.getInstance(), source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -75,6 +75,6 @@ enum MomentumRumblerCondition implements Condition {
|
|||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
return hasAbility == permanent.getAbilities(game).containsKey(FirstStrikeAbility.getInstance().getId());
|
||||
return hasAbility == permanent.hasAbility(FirstStrikeAbility.getInstance(), game);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
package mage.cards.m;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Abilities;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -20,8 +17,10 @@ import mage.filter.common.FilterCreaturePermanent;
|
|||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author anonymous
|
||||
*/
|
||||
public final class MuragandaPetroglyphs extends CardImpl {
|
||||
|
@ -54,6 +53,12 @@ public final class MuragandaPetroglyphs extends CardImpl {
|
|||
|
||||
class NoAbilityPredicate implements Predicate<MageObject> {
|
||||
|
||||
// Muraganda Petroglyphs gives a bonus only to creatures that have no rules text at all. This includes true vanilla
|
||||
// creatures (such as Grizzly Bears), face-down creatures, many tokens, and creatures that have lost their abilities
|
||||
// (due to Ovinize, for example). Any ability of any kind, whether or not the ability functions in the on the
|
||||
// battlefield zone, including things like “Cycling {2}” means the creature doesn’t get the bonus.
|
||||
// (2007-05-01)
|
||||
|
||||
@Override
|
||||
public boolean apply(MageObject input, Game game) {
|
||||
boolean isFaceDown = false;
|
||||
|
@ -65,8 +70,20 @@ class NoAbilityPredicate implements Predicate<MageObject> {
|
|||
abilities = input.getAbilities();
|
||||
}
|
||||
if (isFaceDown) {
|
||||
// Some Auras and Equipment grant abilities to creatures, meaning the affected creature would no longer
|
||||
// get the +2/+2 bonus. For example, Flight grants flying to the enchanted creature. Other Auras and
|
||||
// Equipment do not, meaning the affected creature would continue to get the +2/+2 bonus. For example,
|
||||
// Dehydration states something now true about the enchanted creature, but doesn’t give it any abilities.
|
||||
// Auras and Equipment that grant abilities will use the words “gains” or “has,” and they’ll list a keyword
|
||||
// ability or an ability in quotation marks.
|
||||
// (2007-05-01)
|
||||
|
||||
for (Ability ability : abilities) {
|
||||
if (!ability.getSourceId().equals(input.getId()) && !ability.getClass().equals(JohanVigilanceAbility.class)) {
|
||||
if (ability.getWorksFaceDown()) {
|
||||
// inner face down abilities like turn up and becomes creature
|
||||
continue;
|
||||
}
|
||||
if (!Objects.equals(ability.getClass(), SpellAbility.class) && !ability.getClass().equals(JohanVigilanceAbility.class)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ class ProteanThaumaturgeApplyToPermanent extends ApplyToPermanent {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Permanent permanent, Ability source, UUID copyToObjectId) {
|
||||
permanent.addAbility(ProteanThaumaturge.createAbility(), game);
|
||||
permanent.addAbility(ProteanThaumaturge.createAbility(), source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ class QuartzwoodCrasherTriggeredAbility extends TriggeredAbilityImpl {
|
|||
&& ((DamagedPlayerEvent) event).isCombatDamage()) {
|
||||
Permanent creature = game.getPermanent(event.getSourceId());
|
||||
if (creature != null && creature.isControlledBy(controllerId)
|
||||
&& creature.hasAbility(TrampleAbility.getInstance().getId(), game)
|
||||
&& creature.hasAbility(TrampleAbility.getInstance(), game)
|
||||
&& !damagedPlayerIds.contains(event.getTargetId())) {
|
||||
damagedPlayerIds.add(event.getTargetId());
|
||||
this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game));
|
||||
|
@ -143,7 +143,7 @@ class QuartzwoodCrasherWatcher extends Watcher {
|
|||
return;
|
||||
}
|
||||
Permanent creature = game.getPermanent(event.getSourceId());
|
||||
if (creature == null || !creature.getAbilities(game).containsKey(TrampleAbility.getInstance().getId())) {
|
||||
if (creature == null || !creature.hasAbility(TrampleAbility.getInstance(), game)) {
|
||||
return;
|
||||
}
|
||||
damageMap
|
||||
|
|
|
@ -88,7 +88,7 @@ class ReturnFromExtinctionTarget extends TargetCardInYourGraveyard {
|
|||
return false;
|
||||
}
|
||||
for (Card card : player.getGraveyard().getCards(filter, sourceId, sourceControllerId, game)) {
|
||||
if (card.isAllCreatureTypes() || card.getAbilities(game).contains(ChangelingAbility.getInstance())) {
|
||||
if (card.isAllCreatureTypes() || card.hasAbility(ChangelingAbility.getInstance(), game)) {
|
||||
if (!subTypes.isEmpty()) {
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -64,7 +64,7 @@ class SakashimaTheImpostorApplier extends ApplyToPermanent {
|
|||
permanent.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
|
||||
new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnToHandSourceEffect(true)), false),
|
||||
new ManaCostsImpl("{2}{U}{U}")
|
||||
), game);
|
||||
), source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ class ShieldsOfVelisVelGainEffect extends ContinuousEffectImpl {
|
|||
for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext();) {
|
||||
Permanent permanent = it.next().getPermanent(game);
|
||||
if (permanent != null) {
|
||||
permanent.addAbility(ChangelingAbility.getInstance(), source.getSourceId(), game, false);
|
||||
permanent.addAbility(ChangelingAbility.getInstance(), source.getSourceId(), game);
|
||||
} else {
|
||||
it.remove();
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ class ShoalSerpentEffect extends ContinuousEffectImpl {
|
|||
switch (layer) {
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
permanent.getAbilities().removeIf(entry -> entry.getId().equals(DefenderAbility.getInstance().getId()));
|
||||
permanent.removeAbility(DefenderAbility.getInstance(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -34,8 +33,6 @@ public final class Showstopper extends CardImpl {
|
|||
public Showstopper (UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B}{R}");
|
||||
|
||||
|
||||
|
||||
// Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls."
|
||||
TriggeredAbility ability = new DiesTriggeredAbility(new DamageTargetEffect(2, "it"), false);
|
||||
Target target = new TargetCreaturePermanent(filter2);
|
||||
|
|
|
@ -80,8 +80,8 @@ class SidarKondoOfJamuraaCantBlockCreaturesSourceEffect extends RestrictionEffec
|
|||
|
||||
@Override
|
||||
public boolean applies(Permanent permanent, Ability source, Game game) {
|
||||
if (permanent.hasAbility(FlyingAbility.getInstance().getId(), game)
|
||||
|| permanent.hasAbility(ReachAbility.getInstance().getId(), game)) {
|
||||
if (permanent.hasAbility(FlyingAbility.getInstance(), game)
|
||||
|| permanent.hasAbility(ReachAbility.getInstance(), game)) {
|
||||
return false;
|
||||
}
|
||||
return game.getOpponents(source.getControllerId()).contains(permanent.getControllerId());
|
||||
|
|
|
@ -61,7 +61,7 @@ enum SlingbowTrapCondition implements Condition {
|
|||
for (UUID attackingCreatureId : game.getCombat().getAttackers()) {
|
||||
Permanent attackingCreature = game.getPermanent(attackingCreatureId);
|
||||
if (attackingCreature != null) {
|
||||
if (attackingCreature.getColor(game).isBlack() && attackingCreature.hasAbility(FlyingAbility.getInstance().getId(), game)) {
|
||||
if (attackingCreature.getColor(game).isBlack() && attackingCreature.hasAbility(FlyingAbility.getInstance(), game)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ public final class StormtideLeviathan extends CardImpl {
|
|||
// land abilities are intrinsic, so add them here, not in layer 6
|
||||
if (!land.hasSubtype(SubType.ISLAND, game)) {
|
||||
land.getSubtype(game).add(SubType.ISLAND);
|
||||
if (!land.getAbilities(game).contains(new BlueManaAbility())) {
|
||||
if (!land.getAbilities(game).containsClass(BlueManaAbility.class)) {
|
||||
land.addAbility(new BlueManaAbility(), source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ class TaigamOjutaiMasterGainReboundEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
|
||||
private void addReboundAbility(Card card, Ability source, Game game) {
|
||||
boolean found = card.getAbilities(game).stream().anyMatch(ability -> ability instanceof ReboundAbility);
|
||||
boolean found = card.getAbilities(game).containsClass(ReboundAbility.class);
|
||||
if (!found) {
|
||||
Ability ability = new ReboundAbility();
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
|
|
|
@ -111,7 +111,7 @@ class TestamentOfFaithBecomesCreatureSourceEffect extends ContinuousEffectImpl i
|
|||
if (sublayer == SubLayer.NA) {
|
||||
if (!token.getAbilities().isEmpty()) {
|
||||
for (Ability ability: token.getAbilities()) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game, false);
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ public final class UncheckedGrowth extends CardImpl {
|
|||
for (UUID permanentId : targetPointer.getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanent(permanentId);
|
||||
if (permanent != null && permanent.hasSubtype(SubType.SPIRIT, game)) {
|
||||
permanent.addAbility(TrampleAbility.getInstance(), game);
|
||||
permanent.addAbility(TrampleAbility.getInstance(), source.getSourceId(), game);
|
||||
affectedTargets++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ class UnstableShapeshifterEffect extends OneShotEffect {
|
|||
if (targetCreature != null && permanent != null) {
|
||||
Permanent blueprintPermanent = game.copyPermanent(Duration.Custom, targetCreature, permanent.getId(), source, new EmptyApplyToPermanent());
|
||||
blueprintPermanent.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD,
|
||||
new UnstableShapeshifterEffect(), filterAnotherCreature, false, SetTargetPointer.PERMANENT, ""), game);
|
||||
new UnstableShapeshifterEffect(), filterAnotherCreature, false, SetTargetPointer.PERMANENT, ""), source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -86,7 +86,7 @@ class VraskaTheUnseenGainAbilityEffect extends ContinuousEffectImpl {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
permanent.addAbility(ability, game);
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -68,7 +68,7 @@ class WishfulMerfolkEffect extends ContinuousEffectImpl {
|
|||
switch (layer) {
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
if (sublayer == SubLayer.NA) {
|
||||
permanent.getAbilities().removeIf(entry -> entry.getId().equals(DefenderAbility.getInstance().getId()));
|
||||
permanent.removeAbility(DefenderAbility.getInstance(), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
case TypeChangingEffects_4:
|
||||
|
|
|
@ -274,9 +274,12 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
|
||||
// Check if a Megamorph card is manifested and turned face up by their megamorph ability
|
||||
// it gets the +1/+1 counter.
|
||||
// 701.33c
|
||||
// If a card with morph is manifested, its controller may turn that card face up using
|
||||
// either the procedure described in rule 702.36e to turn a face-down permanent with morph face up
|
||||
// or the procedure described above to turn a manifested permanent face up.
|
||||
@Test
|
||||
public void testManifestMegamorph() {
|
||||
|
||||
public void testManifestMegamorph_TurnUpByMegamorphCost() {
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Forest", 6);
|
||||
// {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library.
|
||||
|
@ -295,6 +298,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
|
||||
activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{5}{G}: Turn");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
@ -310,7 +314,45 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
assertPowerToughness(playerB, "Aerie Bowmasters", 4, 5); // 3/4 and the +1/+1 counter from Megamorph
|
||||
Permanent aerie = getPermanent("Aerie Bowmasters", playerB);
|
||||
Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testManifestMegamorph_TurnUpBySimpleCost() {
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Forest", 4);
|
||||
// {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library.
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
|
||||
|
||||
// {2}{G}{G}
|
||||
// Reach (This creature can block creatures with flying.)
|
||||
// Megamorph {5}{G}
|
||||
addCard(Zone.LIBRARY, playerB, "Aerie Bowmasters", 1);
|
||||
addCard(Zone.LIBRARY, playerB, "Mountain", 1);
|
||||
|
||||
skipInitShuffling();
|
||||
|
||||
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature");
|
||||
setChoice(playerB, "Silvercoat Lion");
|
||||
|
||||
activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{2}{G}{G}: Turn");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
// no life gain
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
|
||||
assertGraveyardCount(playerB, "Silvercoat Lion", 1);
|
||||
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerB, "Aerie Bowmasters", 1);
|
||||
assertPowerToughness(playerB, "Aerie Bowmasters", 3, 4); // 3/4 without counter (megamorph not used)
|
||||
Permanent aerie = getPermanent("Aerie Bowmasters", playerB);
|
||||
Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package org.mage.test.cards.abilities.other;
|
||||
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.permanent.Permanent;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class GainAbilitiesTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void test_AttachmentSingleton() {
|
||||
// {2}{W}
|
||||
// Enchanted creature gets +2/+2.
|
||||
// Enchanted creature has vigilance as long as you control a black or green permanent.
|
||||
addCard(Zone.HAND, playerA, "Abzan Runemark@attach", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains", 6);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears@bear", 1); // 2/2
|
||||
|
||||
// attach all
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.1", "@bear");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.2", "@bear");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@bear", VigilanceAbility.class, true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
Permanent permanent = getPermanent("Balduvian Bears");
|
||||
Assert.assertEquals("must have only 1 singleton ability instance from two attachments",
|
||||
1, permanent.getAbilities(currentGame).stream().filter(a -> a instanceof VigilanceAbility).count());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_AttachmentUnique() {
|
||||
// {R}
|
||||
// Enchanted creature has "{R}, {T}, Discard a card: Draw a card."
|
||||
addCard(Zone.HAND, playerA, "Epiphany Storm@attach", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears@bear", 1); // 2/2
|
||||
|
||||
// attach all
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.1", "@bear");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "@attach.2", "@bear");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
//checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@bear", VigilanceAbility.class, true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
Permanent permanent = getPermanent("Balduvian Bears");
|
||||
Assert.assertEquals("must have 2 dynamic ability instances from two attachments",
|
||||
2, permanent.getAbilities(currentGame).stream().filter(
|
||||
a -> a.getEffects().stream().anyMatch(e -> e instanceof DrawCardSourceControllerEffect)
|
||||
).count());
|
||||
}
|
||||
|
||||
}
|
|
@ -113,7 +113,6 @@ public class CommandersCastTest extends CardTestCommander4Players {
|
|||
waitStackResolved(5, PhaseStep.POSTCOMBAT_MAIN);
|
||||
checkPermanentCount("after cast 2", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Academy Ruins", 1);
|
||||
|
||||
// showBattlefield("end battlefield", 5, PhaseStep.END_TURN, playerA);
|
||||
setStopAt(5, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
execute();
|
||||
|
|
|
@ -14,20 +14,17 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
*
|
||||
* Playing a commander game. Opponent had a Magus of the Moon, and I later
|
||||
* dropped a Chromatic Lantern.
|
||||
*
|
||||
* <p>
|
||||
* I was not allowed to use the Chromatic Lantern's ability. Since layers
|
||||
* are tricky I asked on the Judge's chat to confirm and the user "luma"
|
||||
* said it should work on this scenario.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testMagusOfTheMoonAndChromaticLantern() {
|
||||
|
@ -42,8 +39,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Chromatic Lantern");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerB, "Chromatic Lantern", 1);
|
||||
|
||||
|
@ -66,8 +65,11 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
|
|||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Chromatic Lantern");
|
||||
|
||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Magus of the Moon");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerB, "Chromatic Lantern", 1);
|
||||
assertPermanentCount(playerA, "Magus of the Moon", 1);
|
||||
|
@ -97,8 +99,11 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aquitect's Will", "Forbidding Watchtower");
|
||||
|
||||
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{W}:");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertGraveyardCount(playerA, "Aquitect's Will", 1);
|
||||
|
||||
|
@ -128,8 +133,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bloodmoon);
|
||||
playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, urborgtoy);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, bloodmoon, 1);
|
||||
assertPermanentCount(playerA, urborgtoy, 1);
|
||||
|
@ -157,8 +164,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
|
|||
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, urborgtoy);
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, bloodmoon);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, bloodmoon, 1);
|
||||
assertPermanentCount(playerA, urborgtoy, 1);
|
||||
|
@ -245,8 +254,10 @@ public class LandTypeChangingEffectsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerA, "Stormtide Leviathan"); // all lands are islands in addition to their other types
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Darksteel Citadel"); // land has indestructible ability
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
Permanent darksteel = getPermanent("Darksteel Citadel", playerA.getId());
|
||||
Assert.assertNotNull(darksteel);
|
||||
|
|
|
@ -40,7 +40,7 @@ public class WonderTest extends CardTestPlayerBase {
|
|||
// check no flying in graveyard
|
||||
for (Card card : playerA.getGraveyard().getCards(currentGame)) {
|
||||
if (card.getName().equals("Runeclaw Bear")) {
|
||||
Assert.assertFalse(card.getAbilities().contains(FlyingAbility.getInstance()));
|
||||
Assert.assertFalse(card.hasAbility(FlyingAbility.getInstance(), currentGame));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.cards.planeswalker;
|
||||
|
||||
import mage.abilities.keyword.IndestructibleAbility;
|
||||
|
@ -11,7 +10,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class GideonTest extends CardTestPlayerBase {
|
||||
|
@ -116,16 +114,23 @@ public class GideonTest extends CardTestPlayerBase {
|
|||
// Equip {2}
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Stitcher's Graft", 1);
|
||||
|
||||
// transform
|
||||
attack(2, playerB, "Kytheon, Hero of Akros");
|
||||
attack(2, playerB, "Silvercoat Lion");
|
||||
attack(2, playerB, "Pillarfield Ox");
|
||||
checkPermanentCount("after transform", 2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Gideon, Battle-Forged", 1);
|
||||
|
||||
// become creature and equip
|
||||
activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerB, "0: Until ");
|
||||
waitStackResolved(4, PhaseStep.PRECOMBAT_MAIN);
|
||||
activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Equip {2}", "Gideon, Battle-Forged");
|
||||
|
||||
attack(4, playerB, "Gideon, Battle-Forged"); // 7 damage
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(5, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerB, "Silvercoat Lion", 1);
|
||||
assertLife(playerA, 7);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.mage.test.cards.single.fut;
|
||||
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.constants.EmptyNames;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
|
@ -31,8 +32,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
|
|||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, "Grizzly Bears", 4, 4, Filter.ComparisonScope.Any);
|
||||
assertPowerToughness(playerB, "Grizzly Bears", 4, 4, Filter.ComparisonScope.Any);
|
||||
|
@ -41,15 +44,20 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
|
|||
|
||||
@Test
|
||||
public void faceDownCreaturesTest() {
|
||||
// Morph {4}{G}
|
||||
addCard(Zone.HAND, playerA, "Pine Walker");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
|
||||
//
|
||||
// Creatures with no abilities get +2/+2.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker");
|
||||
setChoice(playerA, "Yes"); // cast it face down as 2/2 creature
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 4, 4);
|
||||
|
@ -57,21 +65,26 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
|
|||
|
||||
@Test
|
||||
public void faceDownGainedAbilityTest() {
|
||||
// Morph {4}{G}
|
||||
addCard(Zone.HAND, playerA, "Pine Walker");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mass Hysteria"); // All creatures have haste.
|
||||
|
||||
// Creatures with no abilities get +2/+2.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker");
|
||||
setChoice(playerA, "Yes"); // cast it face down as 2/2 creature
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
//assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); // no boost (permanent have haste)
|
||||
assertAbility(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), HasteAbility.getInstance(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -83,8 +96,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raise the Alarm");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, "Soldier", 3, 3);
|
||||
}
|
||||
|
@ -99,8 +114,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ovinize", "Goblin Guide");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerB, "Goblin Guide", 2, 3);
|
||||
}
|
||||
|
@ -110,8 +127,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerA, "Hundroog", 1); // Cycling {3}, 4/7
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, "Hundroog", 4, 7);
|
||||
}
|
||||
|
@ -126,11 +145,12 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerA, "Muraganda Petroglyphs", 1);
|
||||
addCard(Zone.HAND, playerA, "Vastwood Zendikon");
|
||||
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vastwood Zendikon", "Forest");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, "Forest", 6, 4);
|
||||
|
||||
|
@ -160,8 +180,10 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dehydration", "Runeclaw Bear");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, "Grizzly Bears", 4, 2);
|
||||
|
||||
|
@ -179,11 +201,14 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1);
|
||||
|
||||
addCard(Zone.HAND, playerA, "Shadow Slice"); // {4}{B}
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shadow Slice");
|
||||
setChoice(playerA, "Grizzly Bears");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shadow Slice", playerB);
|
||||
setChoice(playerA, "Yes"); // do cipher
|
||||
addTarget(playerA, "Grizzly Bears");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPowerToughness(playerA, "Grizzly Bears", 2, 2);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package org.mage.test.cards.triggers.dies;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
|
@ -7,7 +6,6 @@ import org.junit.Test;
|
|||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
|
@ -16,10 +14,9 @@ public class ShowstopperTest extends CardTestPlayerBase {
|
|||
/**
|
||||
* Tests that the dies triggered ability of silvercoat lion (gained by Showstopper)
|
||||
* triggers as he dies from Lightning Bolt
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testDiesTriggeredAbility() {
|
||||
public void test_OneTrigger() {
|
||||
// Showstopper Instant {1}{B}{R}
|
||||
// Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls."
|
||||
addCard(Zone.HAND, playerA, "Showstopper");
|
||||
|
@ -30,10 +27,14 @@ public class ShowstopperTest extends CardTestPlayerBase {
|
|||
addCard(Zone.BATTLEFIELD, playerB, "Ornithopter", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper");
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion");
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Silvercoat Lion");
|
||||
addTarget(playerA, "Ornithopter");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
|
@ -43,12 +44,13 @@ public class ShowstopperTest extends CardTestPlayerBase {
|
|||
|
||||
assertGraveyardCount(playerB, "Ornithopter", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if Showstopper is called twice
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testTwoDiesTriggeredAbilities() {
|
||||
public void test_TwoTriggers() {
|
||||
// Showstopper Instant {1}{B}{R}
|
||||
// Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls."
|
||||
addCard(Zone.HAND, playerA, "Showstopper", 2);
|
||||
|
@ -62,12 +64,16 @@ public class ShowstopperTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper");
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion");
|
||||
setChoice(playerA, "When {this} dies"); // choose from two triggers
|
||||
addTarget(playerA, "Ornithopter");
|
||||
addTarget(playerA, "Grizzly Bears");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
|
@ -79,4 +85,65 @@ public class ShowstopperTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerB, "Ornithopter", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_TwoTriggersAndCopies() {
|
||||
// Showstopper Instant {1}{B}{R}
|
||||
// Until end of turn, creatures you control gain "When this creature dies, it deals 2 damage to target creature an opponent controls."
|
||||
addCard(Zone.HAND, playerA, "Showstopper", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
addCard(Zone.HAND, playerB, "Lightning Bolt");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Ornithopter", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Alchemist's Apprentice", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton", 1);
|
||||
//
|
||||
// When you next cast an instant spell, cast a sorcery spell, or activate a loyalty ability this turn, copy that spell or ability twice.
|
||||
// You may choose new targets for the copies.
|
||||
addCard(Zone.HAND, playerA, "Repeated Reverberation", 1); // {2}{R}{R}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
|
||||
|
||||
// first spell
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 2);
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper");
|
||||
|
||||
// prepare copy
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 4);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Repeated Reverberation");
|
||||
|
||||
// second spell with 2x copy
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Showstopper");
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Silvercoat Lion");
|
||||
setChoice(playerA, "When {this} dies"); // choose from 4 triggers
|
||||
setChoice(playerA, "When {this} dies"); // choose from 4 triggers
|
||||
setChoice(playerA, "When {this} dies"); // choose from 4 triggers
|
||||
addTarget(playerA, "Ornithopter");
|
||||
addTarget(playerA, "Grizzly Bears");
|
||||
addTarget(playerA, "Alchemist's Apprentice");
|
||||
addTarget(playerA, "Augmenting Automaton");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
|
||||
assertGraveyardCount(playerA, "Showstopper", 2);
|
||||
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||
|
||||
assertGraveyardCount(playerB, "Grizzly Bears", 1);
|
||||
assertGraveyardCount(playerB, "Ornithopter", 1);
|
||||
assertGraveyardCount(playerB, "Alchemist's Apprentice", 1);
|
||||
assertGraveyardCount(playerB, "Augmenting Automaton", 1);
|
||||
}
|
||||
|
||||
}
|
|
@ -40,7 +40,7 @@ public class SerializationTest extends CardTestPlayerBase {
|
|||
currentGame.addPermanent(permanent);
|
||||
|
||||
// mark damage from infected ability
|
||||
permanent.addAbility(InfectAbility.getInstance(), currentGame);
|
||||
permanent.addAbility(InfectAbility.getInstance(), null, currentGame);
|
||||
permanent.markDamage(1, permanent.getId(), currentGame, false, false);
|
||||
|
||||
// test compress (it uses default java serialization)
|
||||
|
|
|
@ -41,9 +41,13 @@ public interface MageObject extends MageItem, Serializable {
|
|||
|
||||
Set<SuperType> getSuperType();
|
||||
|
||||
/**
|
||||
* For cards: return basic abilities (without dynamic added)
|
||||
* For permanents: return all abilities (dynamic ability inserts into permanent)
|
||||
*/
|
||||
Abilities<Ability> getAbilities();
|
||||
|
||||
boolean hasAbility(UUID abilityId, Game game);
|
||||
boolean hasAbility(Ability ability, Game game);
|
||||
|
||||
ObjectColor getColor(Game game);
|
||||
|
||||
|
@ -180,9 +184,9 @@ public interface MageObject extends MageItem, Serializable {
|
|||
}
|
||||
|
||||
if (this.isCreature() && otherCard.isCreature()) {
|
||||
if (this.getAbilities().contains(ChangelingAbility.getInstance())
|
||||
if (this.hasAbility(ChangelingAbility.getInstance(), game)
|
||||
|| this.isAllCreatureTypes()
|
||||
|| otherCard.getAbilities().contains(ChangelingAbility.getInstance())
|
||||
|| otherCard.hasAbility(ChangelingAbility.getInstance(), game)
|
||||
|| otherCard.isAllCreatureTypes()) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -132,12 +132,12 @@ public abstract class MageObjectImpl implements MageObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
if (this.getAbilities().containsKey(abilityId)) {
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
if (this.getAbilities().contains(ability)) {
|
||||
return true;
|
||||
}
|
||||
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(getId());
|
||||
return otherAbilities != null && otherAbilities.containsKey(abilityId);
|
||||
return otherAbilities != null && otherAbilities.contains(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
package mage.abilities;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import mage.abilities.keyword.ProtectionAbility;
|
||||
import mage.abilities.mana.ActivatedManaAbilityImpl;
|
||||
import mage.constants.Zone;
|
||||
|
@ -255,7 +258,8 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
boolean containsAll(Abilities<T> abilities);
|
||||
|
||||
/**
|
||||
* Searches this set of abilities for the existence of the give class
|
||||
* Searches this set of abilities for the existence of the given class
|
||||
* Warning, it doesn't work with inherited classes (e.g. it's not equal to instanceOf command)
|
||||
*
|
||||
* @param classObject
|
||||
* @return True if the passed in class is also in this set of abilities.
|
||||
|
@ -271,4 +275,13 @@ public interface Abilities<T extends Ability> extends List<T>, Serializable {
|
|||
Abilities<T> copy();
|
||||
|
||||
String getValue();
|
||||
|
||||
@Deprecated // use permanent.removeAbility instead
|
||||
boolean remove(Object o);
|
||||
|
||||
@Deprecated // use permanent.removeAbility instead
|
||||
boolean removeAll(Collection<?> c);
|
||||
|
||||
@Deprecated // use permanent.removeAbility instead
|
||||
boolean removeIf(Predicate<? super T> filter);
|
||||
}
|
||||
|
|
|
@ -232,7 +232,8 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
if (ability.getId().equals(test.getId())) {
|
||||
return true;
|
||||
}
|
||||
if (ability.getOriginalId().equals(test.getId())) {
|
||||
if (ability.getOriginalId().equals(test.getOriginalId())) {
|
||||
// on ability resolve: engine creates ability's copy and generates newId(), so you must use originalId to find that ability in card later
|
||||
return true;
|
||||
}
|
||||
if (ability instanceof MageSingleton && test instanceof MageSingleton && ability.getRule().equals(test.getRule())) {
|
||||
|
@ -243,7 +244,7 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean containsRule(T ability) {
|
||||
public boolean containsRule(T ability) { // TODO: remove
|
||||
return stream().anyMatch(rule -> rule.getRule().equals(ability.getRule()));
|
||||
}
|
||||
|
||||
|
@ -262,7 +263,7 @@ public class AbilitiesImpl<T extends Ability> extends ArrayList<T> implements Ab
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(UUID abilityId) {
|
||||
public boolean containsKey(UUID abilityId) { // TODO: remove
|
||||
return stream().anyMatch(ability -> abilityId.equals(ability.getId()));
|
||||
}
|
||||
|
||||
|
|
|
@ -522,4 +522,11 @@ public interface Ability extends Controllable, Serializable {
|
|||
Ability addCustomOutcome(Outcome customOutcome);
|
||||
|
||||
Outcome getCustomOutcome();
|
||||
|
||||
/**
|
||||
* For mtg's instances search, see rules example in 112.10b
|
||||
* @param ability
|
||||
* @return
|
||||
*/
|
||||
boolean isSameInstance(Ability ability);
|
||||
}
|
||||
|
|
|
@ -938,6 +938,10 @@ public abstract class AbilityImpl implements Ability {
|
|||
|
||||
@Override
|
||||
public boolean hasSourceObjectAbility(Game game, MageObject source, GameEvent event) {
|
||||
// if source object have this ability
|
||||
// uses for ability.isInUseableZone
|
||||
// replacement and other continues effects can be without source, but active (must return true)
|
||||
|
||||
MageObject object = source;
|
||||
// for singleton abilities like Flying we can't rely on abilities' source because it's only once in continuous effects
|
||||
// so will use the sourceId of the object itself that came as a parameter if it is not null
|
||||
|
@ -949,16 +953,10 @@ public abstract class AbilityImpl implements Ability {
|
|||
}
|
||||
if (object != null) {
|
||||
if (object instanceof Permanent) {
|
||||
if (!((Permanent) object).getAbilities(game).contains(this)) {
|
||||
return false;
|
||||
}
|
||||
return ((Permanent) object).isPhasedIn();
|
||||
} else if (object instanceof Card) {
|
||||
return ((Card) object).getAbilities(game).contains(this);
|
||||
} else if (!object.getAbilities().contains(this)) { // not sure which object it can still be
|
||||
// check if it's an ability that is temporary gained to a card
|
||||
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(this.getSourceId());
|
||||
return otherAbilities != null && otherAbilities.contains(this);
|
||||
return object.hasAbility(this, game) && ((Permanent) object).isPhasedIn();
|
||||
} else {
|
||||
// cards and other objects
|
||||
return object.hasAbility(this, game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -1264,4 +1262,17 @@ public abstract class AbilityImpl implements Ability {
|
|||
public Outcome getCustomOutcome() {
|
||||
return this.customOutcome;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameInstance(Ability ability) {
|
||||
// same instance (by mtg rules) = same object, ID or class+text (you can't check class only cause it can be different by params/text)
|
||||
if (ability == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (this == ability)
|
||||
|| (this.getId().equals(ability.getId()))
|
||||
|| (this.getOriginalId().equals(ability.getOriginalId()))
|
||||
|| (this.getClass() == ability.getClass() && this.getRule(true).equals(ability.getRule(true)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ public class SpellAbility extends ActivatedAbilityImpl {
|
|||
}
|
||||
return null != game.getContinuousEffects().asThough(sourceId, AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game) // check this first to allow Offering in main phase
|
||||
|| timing == TimingRule.INSTANT
|
||||
|| object.hasAbility(FlashAbility.getInstance().getId(), game)
|
||||
|| object.hasAbility(FlashAbility.getInstance(), game)
|
||||
|| game.canPlaySorcery(playerId);
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,8 @@ class LicidContinuousEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
}
|
||||
}
|
||||
licid.getAbilities(game).removeAll(toRemove);
|
||||
licid.removeAbilities(toRemove, source.getSourceId(), game);
|
||||
|
||||
Ability ability = new EnchantAbility("creature");
|
||||
ability.setRuleAtTheTop(true);
|
||||
licid.addAbility(ability, source.getSourceId(), game);
|
||||
|
|
|
@ -40,6 +40,7 @@ public class TurnFaceUpAbility extends SpecialAction {
|
|||
this.usesStack = false;
|
||||
this.abilityType = AbilityType.SPECIAL_ACTION;
|
||||
this.setRuleVisible(false); // will be made visible only to controller in CardView
|
||||
this.setWorksFaceDown(true);
|
||||
}
|
||||
|
||||
public TurnFaceUpAbility(final TurnFaceUpAbility ability) {
|
||||
|
|
|
@ -17,7 +17,7 @@ public enum BuybackCondition implements Condition {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
return card.getAbilities().stream()
|
||||
return card.getAbilities(game).stream()
|
||||
.filter(a -> a instanceof BuybackAbility)
|
||||
.anyMatch(a -> ((BuybackAbility) a).isBuybackActivated(game));
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@ public enum DashedCondition implements Condition {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
return card.getAbilities().stream()
|
||||
return card.getAbilities(game).stream()
|
||||
.filter(a -> a instanceof DashAbility)
|
||||
.anyMatch(d -> ((DashAbility)d).isActivated(source, game));
|
||||
.anyMatch(d -> ((DashAbility) d).isActivated(source, game));
|
||||
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -22,7 +22,7 @@ public enum EvokedCondition implements Condition {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
return card.getAbilities().stream()
|
||||
return card.getAbilities(game).stream()
|
||||
.filter(ab -> ab instanceof EvokeAbility)
|
||||
.anyMatch(evoke -> ((EvokeAbility) evoke).isActivated(source, game));
|
||||
}
|
||||
|
|
|
@ -27,10 +27,10 @@ public enum SuspendedCondition implements Condition {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
boolean found = card.getAbilities().stream().anyMatch(ability -> ability instanceof SuspendAbility);
|
||||
boolean found = card.getAbilities(game).containsClass(SuspendAbility.class);
|
||||
|
||||
if (!found) {
|
||||
found = game.getState().getAllOtherAbilities(source.getSourceId()).stream().anyMatch(ability -> ability instanceof SuspendAbility);
|
||||
found = game.getState().getAllOtherAbilities(source.getSourceId()).containsClass(SuspendAbility.class);
|
||||
|
||||
}
|
||||
if (found) {
|
||||
|
|
|
@ -57,7 +57,7 @@ public class ContinuousEffects implements Serializable {
|
|||
// private final PlaneswalkerRedirectionEffect planeswalkerRedirectionEffect;
|
||||
private final AuraReplacementEffect auraReplacementEffect;
|
||||
|
||||
private final List<ContinuousEffect> previous = new ArrayList<>();
|
||||
private final Map<String, List<ContinuousEffect>> lastEffectsListOnLayer = new HashMap<>(); // helps to find out new effect timestamps
|
||||
|
||||
// note all effect/abilities that were only added temporary
|
||||
private final Map<ContinuousEffect, Set<Ability>> temporaryEffects = new HashMap<>();
|
||||
|
@ -178,6 +178,16 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
|
||||
public synchronized List<ContinuousEffect> getLayeredEffects(Game game) {
|
||||
return getLayeredEffects(game, "main");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return effects list ordered by timestamps (timestamps are automaticity generates from new/old lists on same layer)
|
||||
*
|
||||
* @param timestampGroupName workaround to fix broken timestamps on effect's add/remove between different layers
|
||||
* @return effects list ordered by timestamp
|
||||
*/
|
||||
public synchronized List<ContinuousEffect> getLayeredEffects(Game game, String timestampGroupName) {
|
||||
List<ContinuousEffect> layerEffects = new ArrayList<>();
|
||||
for (ContinuousEffect effect : layeredEffects) {
|
||||
switch (effect.getDuration()) {
|
||||
|
@ -202,9 +212,14 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
updateTimestamps(layerEffects);
|
||||
updateTimestamps(timestampGroupName, layerEffects);
|
||||
layerEffects.sort(Comparator.comparingLong(ContinuousEffect::getOrder));
|
||||
/* debug effects apply order:
|
||||
if (game.getStep() != null) System.out.println("layr - " + game.getTurnNum() + "." + game.getStep().getType() + ": layers " + layerEffects.size()
|
||||
+ " - " + layerEffects.stream().map(l -> l.getClass().getSimpleName()).collect(Collectors.joining(", "))
|
||||
+ " - " + callName);
|
||||
//*/
|
||||
|
||||
Collections.sort(layerEffects, Comparator.comparingLong(ContinuousEffect::getOrder));
|
||||
return layerEffects;
|
||||
}
|
||||
|
||||
|
@ -215,17 +230,23 @@ public class ContinuousEffects implements Serializable {
|
|||
* Ability.#isInUseableZone(Game, boolean) method in
|
||||
* #getLayeredEffects(Game).
|
||||
*
|
||||
* It must be called with different timestamp group name (otherwise sort order will be changed for add/remove effects, see Urborg and Bloodmoon test)
|
||||
*
|
||||
* @param layerEffects
|
||||
*/
|
||||
private synchronized void updateTimestamps(List<ContinuousEffect> layerEffects) {
|
||||
private synchronized void updateTimestamps(String timestampGroupName, List<ContinuousEffect> layerEffects) {
|
||||
if (!lastEffectsListOnLayer.containsKey(timestampGroupName)) {
|
||||
lastEffectsListOnLayer.put(timestampGroupName, new ArrayList<>());
|
||||
}
|
||||
List<ContinuousEffect> prevs = lastEffectsListOnLayer.get(timestampGroupName);
|
||||
for (ContinuousEffect continuousEffect : layerEffects) {
|
||||
// check if it's new, then set order
|
||||
if (!previous.contains(continuousEffect)) {
|
||||
if (!prevs.contains(continuousEffect)) {
|
||||
setOrder(continuousEffect);
|
||||
}
|
||||
}
|
||||
previous.clear();
|
||||
previous.addAll(layerEffects);
|
||||
prevs.clear();
|
||||
prevs.addAll(layerEffects);
|
||||
}
|
||||
|
||||
public void setOrder(ContinuousEffect effect) {
|
||||
|
@ -425,12 +446,12 @@ public class ContinuousEffects implements Serializable {
|
|||
return false;
|
||||
}
|
||||
boolean exists = true;
|
||||
if (!object.getAbilities().contains(ability)) {
|
||||
if (!object.hasAbility(ability, game)) {
|
||||
exists = false;
|
||||
if (object instanceof PermanentCard) {
|
||||
PermanentCard permanent = (PermanentCard) object;
|
||||
if (permanent.isTransformable() && event.getType() == GameEvent.EventType.TRANSFORMED) {
|
||||
exists = permanent.getCard().getAbilities().contains(ability);
|
||||
exists = permanent.getCard().hasAbility(ability, game);
|
||||
}
|
||||
}
|
||||
} else if (object instanceof PermanentCard) {
|
||||
|
@ -903,7 +924,7 @@ public class ContinuousEffects implements Serializable {
|
|||
//20091005 - 613
|
||||
public synchronized void apply(Game game) {
|
||||
removeInactiveEffects(game);
|
||||
List<ContinuousEffect> activeLayerEffects = getLayeredEffects(game);
|
||||
List<ContinuousEffect> activeLayerEffects = getLayeredEffects(game); // main call
|
||||
|
||||
List<ContinuousEffect> layer = filterLayeredEffects(activeLayerEffects, Layer.CopyEffects_1);
|
||||
for (ContinuousEffect effect : layer) {
|
||||
|
@ -914,7 +935,7 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
//Reload layerEffect if copy effects were applied
|
||||
if (!layer.isEmpty()) {
|
||||
activeLayerEffects = getLayeredEffects(game);
|
||||
activeLayerEffects = getLayeredEffects(game, "layer_1");
|
||||
}
|
||||
|
||||
layer = filterLayeredEffects(activeLayerEffects, Layer.ControlChangingEffects_2);
|
||||
|
@ -936,16 +957,16 @@ public class ContinuousEffects implements Serializable {
|
|||
game.getBattlefield().resetPermanentsControl();
|
||||
}
|
||||
|
||||
applyLayer(activeLayerEffects, Layer.TextChangingEffects_3, game);
|
||||
applyLayer(activeLayerEffects, Layer.TypeChangingEffects_4, game);
|
||||
applyLayer(activeLayerEffects, Layer.ColorChangingEffects_5, game);
|
||||
applyLayer(activeLayerEffects, Layer.TextChangingEffects_3, game, "layer_3");
|
||||
applyLayer(activeLayerEffects, Layer.TypeChangingEffects_4, game, "layer_4");
|
||||
applyLayer(activeLayerEffects, Layer.ColorChangingEffects_5, game, "layer_5");
|
||||
|
||||
Map<ContinuousEffect, List<Ability>> appliedEffectAbilities = new HashMap<>();
|
||||
boolean done = false;
|
||||
Map<ContinuousEffect, Set<UUID>> waitingEffects = new LinkedHashMap<>();
|
||||
Set<UUID> appliedEffects = new HashSet<>();
|
||||
applyCounters.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, null, game);
|
||||
activeLayerEffects = getLayeredEffects(game);
|
||||
activeLayerEffects = getLayeredEffects(game, "layer_6");
|
||||
|
||||
while (!done) { // loop needed if a added effect adds again an effect (e.g. Level 5- of Joraga Treespeaker)
|
||||
done = true;
|
||||
|
@ -1000,7 +1021,7 @@ public class ContinuousEffects implements Serializable {
|
|||
effect.apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game);
|
||||
done = false;
|
||||
// list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities)
|
||||
activeLayerEffects = getLayeredEffects(game);
|
||||
activeLayerEffects = getLayeredEffects(game, "apply");
|
||||
}
|
||||
appliedEffects.add(effect.getId());
|
||||
|
||||
|
@ -1027,7 +1048,7 @@ public class ContinuousEffects implements Serializable {
|
|||
entry.getKey().apply(Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, ability, game);
|
||||
done = false;
|
||||
// list must be updated after each applied effect (eg. if "Turn to Frog" removes abilities)
|
||||
activeLayerEffects = getLayeredEffects(game);
|
||||
activeLayerEffects = getLayeredEffects(game, "apply");
|
||||
}
|
||||
appliedEffects.add(entry.getKey().getId());
|
||||
iterator.remove();
|
||||
|
@ -1083,10 +1104,10 @@ public class ContinuousEffects implements Serializable {
|
|||
|
||||
private boolean abilityActive(Ability ability, Game game) {
|
||||
MageObject object = game.getObject(ability.getSourceId());
|
||||
return object != null && object.hasAbility(ability.getId(), game);
|
||||
return object != null && object.hasAbility(ability, game);
|
||||
}
|
||||
|
||||
private void applyLayer(List<ContinuousEffect> activeLayerEffects, Layer currentLayer, Game game) {
|
||||
private void applyLayer(List<ContinuousEffect> activeLayerEffects, Layer currentLayer, Game game, String timestampGroupName) {
|
||||
List<ContinuousEffect> layer = filterLayeredEffects(activeLayerEffects, currentLayer);
|
||||
// layer is a list of all effects at the current layer
|
||||
if (!layer.isEmpty()) {
|
||||
|
@ -1109,7 +1130,7 @@ public class ContinuousEffects implements Serializable {
|
|||
applyContinuousEffect(effect, currentLayer, game);
|
||||
// add it to the applied effects list
|
||||
appliedEffects.add(effect.getId());
|
||||
layer = getLayeredEffects(game);
|
||||
layer = getLayeredEffects(game, timestampGroupName);
|
||||
|
||||
// check waiting effects to see if it has anything to check
|
||||
if (!waitingEffects.isEmpty()) {
|
||||
|
@ -1120,7 +1141,7 @@ public class ContinuousEffects implements Serializable {
|
|||
applyContinuousEffect(entry.getKey(), currentLayer, game);
|
||||
// add it to the applied effects list
|
||||
appliedEffects.add(entry.getKey().getId());
|
||||
layer = getLayeredEffects(game);
|
||||
layer = getLayeredEffects(game, timestampGroupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1131,7 +1152,7 @@ public class ContinuousEffects implements Serializable {
|
|||
applyContinuousEffect(entry.getKey(), currentLayer, game);
|
||||
// add it to the applied effects list
|
||||
appliedEffects.add(entry.getKey().getId());
|
||||
layer = getLayeredEffects(game);
|
||||
layer = getLayeredEffects(game, timestampGroupName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1154,7 +1175,7 @@ public class ContinuousEffects implements Serializable {
|
|||
if (!(effect instanceof BecomesFaceDownCreatureEffect)
|
||||
&& (effect != null && !effect.getDuration().equals(Duration.Custom))) { // Custom effects do not depend on the creating permanent
|
||||
if (card != null) {
|
||||
return card.getAbilities(game).contains(ability);
|
||||
return card.hasAbility(ability, game);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ public class GainAbilitySpellsEffect extends ContinuousEffectImpl {
|
|||
if (stackObject.isControlledBy(source.getControllerId())) {
|
||||
Card card = game.getCard(stackObject.getSourceId());
|
||||
if (card != null && filter.match(card, game)) {
|
||||
if (!card.getAbilities().contains(ability)) {
|
||||
if (!card.hasAbility(ability, game)) {
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,11 +115,11 @@ public class CopyEffect extends ContinuousEffectImpl {
|
|||
permanent.removeAllAbilities(source.getSourceId(), game);
|
||||
if (copyFromObject instanceof Permanent) {
|
||||
for (Ability ability : ((Permanent) copyFromObject).getAbilities(game)) {
|
||||
permanent.addAbility(ability, getSourceId(), game, false); // no new Id so consumed replacement effects are known while new continuousEffects.apply happen.
|
||||
permanent.addAbility(ability, getSourceId(), game);
|
||||
}
|
||||
} else {
|
||||
for (Ability ability : copyFromObject.getAbilities()) {
|
||||
permanent.addAbility(ability, getSourceId(), game, false); // no new Id so consumed replacement effects are known while new continuousEffects.apply happen.
|
||||
permanent.addAbility(ability, getSourceId(), game);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ public class CopyTokenEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
permanent.getAbilities().clear();
|
||||
for (Ability ability : token.getAbilities()) {
|
||||
permanent.addAbility(ability, game);
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
permanent.getPower().setValue(token.getPower().getValue());
|
||||
permanent.getToughness().setValue(token.getToughness().getValue());
|
||||
|
|
|
@ -33,7 +33,7 @@ public class CanBlockOnlyFlyingAttachedEffect extends RestrictionEffect {
|
|||
if (attacker == null) {
|
||||
return true;
|
||||
}
|
||||
return attacker.getAbilities().contains(FlyingAbility.getInstance());
|
||||
return attacker.hasAbility(FlyingAbility.getInstance(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,7 +33,7 @@ public class CanBlockOnlyFlyingEffect extends RestrictionEffect {
|
|||
if (attacker == null) {
|
||||
return true;
|
||||
}
|
||||
return attacker.getAbilities().contains(FlyingAbility.getInstance());
|
||||
return attacker.hasAbility(FlyingAbility.getInstance(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
|
||||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import mage.MageObjectReference;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -13,23 +7,20 @@ import mage.abilities.common.TurnFaceUpAbility;
|
|||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.keyword.MorphAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
||||
public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl implements SourceEffect {
|
||||
|
||||
protected Map<UUID,Ability> turnFaceUpAbilityMap = new HashMap<>();
|
||||
protected Map<UUID, Ability> turnFaceUpAbilityMap = new HashMap<>();
|
||||
protected FilterPermanent filter;
|
||||
|
||||
public BecomesFaceDownCreatureAllEffect(FilterPermanent filter) {
|
||||
|
@ -40,7 +31,7 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple
|
|||
|
||||
public BecomesFaceDownCreatureAllEffect(final BecomesFaceDownCreatureAllEffect effect) {
|
||||
super(effect);
|
||||
for (Map.Entry<UUID,Ability> entry: effect.turnFaceUpAbilityMap.entrySet()) {
|
||||
for (Map.Entry<UUID, Ability> entry : effect.turnFaceUpAbilityMap.entrySet()) {
|
||||
this.turnFaceUpAbilityMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
this.filter = effect.filter.copy();
|
||||
|
@ -54,16 +45,16 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple
|
|||
@Override
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
for (Permanent perm: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
|
||||
for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
|
||||
if (!perm.isFaceDown(game) && !perm.isTransformable()) {
|
||||
affectedObjectList.add(new MageObjectReference(perm, game));
|
||||
perm.setFaceDown(true, game);
|
||||
// check for Morph
|
||||
Card card = game.getCard(perm.getId());
|
||||
if (card != null) {
|
||||
for (Ability ability: card.getAbilities()) {
|
||||
for (Ability ability : card.getAbilities()) {
|
||||
if (ability instanceof MorphAbility) {
|
||||
this.turnFaceUpAbilityMap.put(card.getId(), new TurnFaceUpAbility(((MorphAbility)ability).getMorphCosts()));
|
||||
this.turnFaceUpAbilityMap.put(card.getId(), new TurnFaceUpAbility(((MorphAbility) ability).getMorphCosts()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +65,7 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple
|
|||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
boolean targetExists = false;
|
||||
for (MageObjectReference mor: affectedObjectList) {
|
||||
for (MageObjectReference mor : affectedObjectList) {
|
||||
Permanent permanent = mor.getPermanent(game);
|
||||
if (permanent != null && permanent.isFaceDown(game)) {
|
||||
targetExists = true;
|
||||
|
@ -92,27 +83,35 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple
|
|||
break;
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
Card card = game.getCard(permanent.getId()); //
|
||||
List<Ability> abilities = new ArrayList<>();
|
||||
List<Ability> abilitiesToRemove = new ArrayList<>();
|
||||
for (Ability ability : permanent.getAbilities()) {
|
||||
|
||||
// keep gained abilities from other sources, removes only own (card text)
|
||||
if (card != null && !card.getAbilities().contains(ability)) {
|
||||
// gained abilities from other sources won't be removed
|
||||
continue;
|
||||
}
|
||||
// TODO: Add flag "works also face down" to ability and use it to control ability removement instead of instanceof check
|
||||
|
||||
// 701.33c
|
||||
// If a card with morph is manifested, its controller may turn that card face up using
|
||||
// either the procedure described in rule 702.36e to turn a face-down permanent with morph face up
|
||||
// or the procedure described above to turn a manifested permanent face up.
|
||||
//
|
||||
// so keep all tune face up abilities and other face down compatible
|
||||
if (ability.getWorksFaceDown()) {
|
||||
ability.setRuleVisible(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) {
|
||||
if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureAllEffect) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
abilities.add(ability);
|
||||
abilitiesToRemove.add(ability);
|
||||
}
|
||||
permanent.getAbilities().removeAll(abilities);
|
||||
permanent.removeAbilities(abilitiesToRemove, source.getSourceId(), game);
|
||||
if (turnFaceUpAbilityMap.containsKey(permanent.getId())) {
|
||||
permanent.addAbility(turnFaceUpAbilityMap.get(permanent.getId()), game);
|
||||
permanent.addAbility(turnFaceUpAbilityMap.get(permanent.getId()), source.getSourceId(), game);
|
||||
}
|
||||
break;
|
||||
case PTChangingEffects_7:
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import mage.MageObjectReference;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
|
@ -12,14 +9,13 @@ import mage.abilities.costs.Costs;
|
|||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This effect lets the card be a 2/2 face-down creature, with no text, no name,
|
||||
* no subtypes, and no mana cost, if it's face down on the battlefield. And it
|
||||
|
@ -149,21 +145,31 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen
|
|||
Card card = game.getCard(permanent.getId()); //
|
||||
List<Ability> abilitiesToRemove = new ArrayList<>();
|
||||
for (Ability ability : permanent.getAbilities()) {
|
||||
|
||||
// keep gained abilities from other sources, removes only own (card text)
|
||||
if (card != null && !card.getAbilities().contains(ability)) {
|
||||
// gained abilities from other sources won't be removed
|
||||
continue;
|
||||
}
|
||||
|
||||
// 701.33c
|
||||
// If a card with morph is manifested, its controller may turn that card face up using
|
||||
// either the procedure described in rule 702.36e to turn a face-down permanent with morph face up
|
||||
// or the procedure described above to turn a manifested permanent face up.
|
||||
//
|
||||
// so keep all tune face up abilities and other face down compatible
|
||||
if (ability.getWorksFaceDown()) {
|
||||
ability.setRuleVisible(false);
|
||||
continue;
|
||||
} else if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) {
|
||||
}
|
||||
|
||||
if (!ability.getRuleVisible() && !ability.getEffects().isEmpty()) {
|
||||
if (ability.getEffects().get(0) instanceof BecomesFaceDownCreatureEffect) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
abilitiesToRemove.add(ability);
|
||||
}
|
||||
permanent.getAbilities().removeAll(abilitiesToRemove);
|
||||
permanent.removeAbilities(abilitiesToRemove, source.getSourceId(), game);
|
||||
if (turnFaceUpAbility != null) {
|
||||
permanent.addAbility(turnFaceUpAbility, source.getSourceId(), game);
|
||||
}
|
||||
|
|
|
@ -45,9 +45,7 @@ public class CreaturesCantGetOrHaveAbilityEffect extends ContinuousEffectImpl {
|
|||
if (controller != null) {
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
|
||||
if (permanent != null) {
|
||||
while (permanent.getAbilities().remove(ability)) {
|
||||
// repeat as long as ability can be removed
|
||||
}
|
||||
permanent.removeAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -87,7 +87,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl {
|
|||
for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext(); ) { // filter may not be used again, because object can have changed filter relevant attributes but still geets boost
|
||||
Permanent permanent = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets (e.g. Showstopper)
|
||||
if (permanent != null) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game, false);
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
} else {
|
||||
it.remove(); // no longer on the battlefield, remove reference to object
|
||||
if (affectedObjectList.isEmpty()) {
|
||||
|
@ -99,7 +99,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl {
|
|||
setRuntimeData(source, game);
|
||||
for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
|
||||
if (!(excludeSource && perm.getId().equals(source.getSourceId())) && selectedByRuntimeData(perm, source, game)) {
|
||||
perm.addAbility(ability, source.getSourceId(), game, false);
|
||||
perm.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
// still as long as the prev. permanent is known to the LKI (e.g. Mikaeus, the Unhallowed) so gained dies triggered ability will trigger
|
||||
|
@ -109,7 +109,7 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl {
|
|||
Permanent perm = (Permanent) mageObject;
|
||||
if (!(excludeSource && perm.getId().equals(source.getSourceId())) && selectedByRuntimeData(perm, source, game)) {
|
||||
if (filter.match(perm, source.getSourceId(), source.getControllerId(), game)) {
|
||||
perm.addAbility(ability, source.getSourceId(), game, false);
|
||||
perm.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
}
|
||||
if (permanent != null) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game, false);
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl {
|
|||
Permanent perm = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets (e.g. Showstopper)
|
||||
if (perm != null) {
|
||||
for (Ability abilityToAdd : ability) {
|
||||
perm.addAbility(abilityToAdd, source.getSourceId(), game, false);
|
||||
perm.addAbility(abilityToAdd, source.getSourceId(), game);
|
||||
}
|
||||
} else {
|
||||
it.remove();
|
||||
|
@ -100,7 +100,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl {
|
|||
for (Permanent perm : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) {
|
||||
if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
|
||||
for (Ability abilityToAdd : ability) {
|
||||
perm.addAbility(abilityToAdd, source.getSourceId(), game, false);
|
||||
perm.addAbility(abilityToAdd, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ public class GainAbilityControlledEffect extends ContinuousEffectImpl {
|
|||
if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
|
||||
if (filter.match(perm, source.getSourceId(), source.getControllerId(), game)) {
|
||||
for (Ability abilityToAdd : ability) {
|
||||
perm.addAbility(abilityToAdd, source.getSourceId(), game, false);
|
||||
perm.addAbility(abilityToAdd, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl {
|
|||
if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.isControlledBy(source.getControllerId())) {
|
||||
Spell spell = (Spell) stackObject;
|
||||
if (filter.match(spell, game)) {
|
||||
if (!spell.getAbilities().contains(ability)) {
|
||||
if (!spell.hasAbility(ability, game)) {
|
||||
game.getState().addOtherAbility(spell.getCard(), ability);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public class GainAbilityPairedEffect extends ContinuousEffectImpl {
|
|||
Permanent paired = permanent.getPairedCard().getPermanent(game);
|
||||
if (paired != null && paired.getPairedCard() != null && paired.getPairedCard().equals(new MageObjectReference(permanent, game))) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
paired.addAbility(ability, source.getSourceId(), game, false);
|
||||
paired.addAbility(ability, source.getSourceId(), game);
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
|
|
@ -103,7 +103,7 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl implements Sou
|
|||
permanent = game.getPermanent(source.getSourceId());
|
||||
}
|
||||
if (permanent != null) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game, false);
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@ public class GainAbilityTargetEffect extends ContinuousEffectImpl {
|
|||
for (UUID permanentId : targetPointer.getTargets(game, source)) {
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(permanentId);
|
||||
if (permanent != null) {
|
||||
permanent.addAbility(ability, source.getSourceId(), game, false);
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
affectedTargets++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ public class GainProtectionFromTypeTargetEffect extends GainAbilityTargetEffect
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Permanent creature = game.getPermanent(source.getFirstTarget());
|
||||
if (creature != null) {
|
||||
creature.addAbility(ability, game);
|
||||
creature.addAbility(ability, source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -85,9 +85,7 @@ public class LoseAbilityAllEffect extends ContinuousEffectImpl {
|
|||
for (Iterator<MageObjectReference> it = affectedObjectList.iterator(); it.hasNext();) { // filter may not be used again, because object can have changed filter relevant attributes but still geets boost
|
||||
Permanent perm = it.next().getPermanentOrLKIBattlefield(game); //LKI is neccessary for "dies triggered abilities" to work given to permanets (e.g. Showstopper)
|
||||
if (perm != null) {
|
||||
for (Ability ability : ability) {
|
||||
perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId()));
|
||||
}
|
||||
perm.removeAbilities(ability, source.getSourceId(), game);
|
||||
} else {
|
||||
it.remove();
|
||||
if (affectedObjectList.isEmpty()) {
|
||||
|
@ -99,9 +97,7 @@ public class LoseAbilityAllEffect extends ContinuousEffectImpl {
|
|||
for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
|
||||
if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
|
||||
System.out.println(game.getTurn() + ", " + game.getPhase() + ": " + "remove from size " + perm.getAbilities().size());
|
||||
for (Ability ability : ability) {
|
||||
perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId()));
|
||||
}
|
||||
perm.removeAbilities(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
// still as long as the prev. permanent is known to the LKI (e.g. Mikaeus, the Unhallowed) so gained dies triggered ability will trigger
|
||||
|
@ -111,9 +107,7 @@ public class LoseAbilityAllEffect extends ContinuousEffectImpl {
|
|||
Permanent perm = (Permanent) mageObject;
|
||||
if (!(excludeSource && perm.getId().equals(source.getSourceId()))) {
|
||||
if (filter.match(perm, source.getSourceId(), source.getControllerId(), game)) {
|
||||
for (Ability ability : ability) {
|
||||
perm.getAbilities().removeIf(entry -> entry.getId().equals(ability.getId()));
|
||||
}
|
||||
perm.removeAbilities(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,12 +43,7 @@ public class LoseAbilityAttachedEffect extends ContinuousEffectImpl {
|
|||
if (equipment != null && equipment.getAttachedTo() != null) {
|
||||
Permanent creature = game.getPermanent(equipment.getAttachedTo());
|
||||
if (creature != null) {
|
||||
while (creature.getAbilities().contains(ability)) {
|
||||
if (!creature.getAbilities().remove(ability)) {
|
||||
// Something went wrong - ability has an other id?
|
||||
logger.warn("ability" + ability.getRule() + "couldn't be removed.");
|
||||
}
|
||||
}
|
||||
creature.removeAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -63,13 +63,9 @@ public class LoseAbilityOrAnotherAbilityTargetEffect extends LoseAbilityTargetEf
|
|||
if (player.choose(outcome, chooseAbility, game)) {
|
||||
String chosenAbility = chooseAbility.getChoice();
|
||||
if (chosenAbility.equals(ability.getRule())) {
|
||||
while (permanent.getAbilities().contains(ability)) {
|
||||
permanent.getAbilities().remove(ability);
|
||||
}
|
||||
permanent.removeAbility(ability, source.getSourceId(), game);
|
||||
} else if (chosenAbility.equals(ability2.getRule())) {
|
||||
while (permanent.getAbilities().contains(ability2)) {
|
||||
permanent.getAbilities().remove(ability2);
|
||||
}
|
||||
permanent.removeAbility(ability2, source.getSourceId(), game);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
|
|
|
@ -56,10 +56,7 @@ public class LoseAbilitySourceEffect extends ContinuousEffectImpl {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
// 112.10
|
||||
while (permanent.getAbilities().remove(ability)) {
|
||||
|
||||
}
|
||||
permanent.removeAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -45,18 +45,7 @@ public class LoseAbilityTargetEffect extends ContinuousEffectImpl {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (permanent != null) {
|
||||
if (ability instanceof MageSingleton) {
|
||||
while (permanent.getAbilities().contains(ability)) {
|
||||
permanent.getAbilities().remove(ability);
|
||||
}
|
||||
} else {
|
||||
for (Iterator<Ability> iter = permanent.getAbilities().iterator(); iter.hasNext();) {
|
||||
Ability ab = iter.next();
|
||||
if (ab.getClass().equals(ability.getClass())) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
permanent.removeAbility(ability, source.getSourceId(), game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -213,7 +213,7 @@ public class SuspendAbility extends SpecialAction {
|
|||
}
|
||||
MageObject object = game.getObject(sourceId);
|
||||
return new ActivationStatus(object.isInstant()
|
||||
|| object.hasAbility(FlashAbility.getInstance().getId(), game)
|
||||
|| object.hasAbility(FlashAbility.getInstance(), game)
|
||||
|| null != game.getContinuousEffects().asThough(sourceId,
|
||||
AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game)
|
||||
|| game.canPlaySorcery(playerId), null);
|
||||
|
@ -356,6 +356,13 @@ class SuspendPlayCardEffect extends OneShotEffect {
|
|||
}
|
||||
}
|
||||
// remove the abilities from the card
|
||||
// TODO: will not work with Adventure Cards and another auto-generated abilities list
|
||||
// TODO: is it work after blink or return to hand?
|
||||
/*
|
||||
bug example:
|
||||
Epochrasite bug: It comes out of suspend, is cast and enters the battlefield. THEN if it's returned to
|
||||
its owner's hand from battlefield, the bounced Epochrasite can't be cast for the rest of the game.
|
||||
*/
|
||||
card.getAbilities().removeAll(abilitiesToRemove);
|
||||
}
|
||||
// cast the card for free
|
||||
|
|
|
@ -38,7 +38,7 @@ public class TransformAbility extends SimpleStaticAbility {
|
|||
return "";
|
||||
}
|
||||
|
||||
public static void transform(Permanent permanent, Card sourceCard, Game game) {
|
||||
public static void transform(Permanent permanent, Card sourceCard, Game game, Ability source) {
|
||||
|
||||
if (sourceCard == null) {
|
||||
return;
|
||||
|
@ -63,7 +63,7 @@ public class TransformAbility extends SimpleStaticAbility {
|
|||
permanent.setExpansionSetCode(sourceCard.getExpansionSetCode());
|
||||
permanent.getAbilities().clear();
|
||||
for (Ability ability : sourceCard.getAbilities()) {
|
||||
permanent.addAbility(ability, game);
|
||||
permanent.addAbility(ability, source == null ? null : source.getSourceId(), game);
|
||||
}
|
||||
permanent.getPower().modifyBaseValue(sourceCard.getPower().getValue());
|
||||
permanent.getToughness().modifyBaseValue(sourceCard.getToughness().getValue());
|
||||
|
@ -105,7 +105,7 @@ class TransformEffect extends ContinuousEffectImpl {
|
|||
return false;
|
||||
}
|
||||
|
||||
TransformAbility.transform(permanent, card, game);
|
||||
TransformAbility.transform(permanent, card, game, source);
|
||||
|
||||
return true;
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@ public interface Card extends MageObject {
|
|||
|
||||
void setOwnerId(UUID ownerId);
|
||||
|
||||
/**
|
||||
* For cards: return all basic and dynamic abilities
|
||||
* For permanents: return all basic and dynamic abilities
|
||||
*/
|
||||
Abilities<Ability> getAbilities(Game game);
|
||||
|
||||
void setSpellAbility(SpellAbility ability);
|
||||
|
|
|
@ -285,7 +285,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
|
||||
/**
|
||||
* Gets all current abilities - includes additional abilities added by other
|
||||
* cards or effects
|
||||
* cards or effects. Warning, you can't modify that list.
|
||||
*
|
||||
* @param game
|
||||
* @return A list of {@link Ability} - this collection is not modifiable
|
||||
|
@ -295,15 +295,23 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
if (game == null) {
|
||||
return abilities; // deck editor with empty game
|
||||
}
|
||||
|
||||
CardState cardState = game.getState().getCardState(this.getId());
|
||||
if (!cardState.hasLostAllAbilities() && (cardState.getAbilities() == null || cardState.getAbilities().isEmpty())) {
|
||||
if (cardState == null) {
|
||||
return abilities;
|
||||
}
|
||||
|
||||
// collects all abilities
|
||||
Abilities<Ability> all = new AbilitiesImpl<>();
|
||||
|
||||
// basic
|
||||
if (!cardState.hasLostAllAbilities()) {
|
||||
all.addAll(abilities);
|
||||
}
|
||||
|
||||
// dynamic
|
||||
all.addAll(cardState.getAbilities());
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
|
@ -314,6 +322,12 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
|
|||
cardState.getAbilities().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
// getAbilities(game) searches all abilities from base and dynamic lists (other)
|
||||
return this.getAbilities(game).contains(ability);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public in order to support adding abilities to SplitCardHalf's
|
||||
*
|
||||
|
|
|
@ -171,8 +171,8 @@ public abstract class Designation implements MageObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
return abilites.containsKey(abilityId);
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
return this.getAbilities().contains(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1647,7 +1647,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
}
|
||||
newBluePrint.assignNewId();
|
||||
if (copyFromPermanent.isTransformed()) {
|
||||
TransformAbility.transform(newBluePrint, newBluePrint.getSecondCardFace(), this);
|
||||
TransformAbility.transform(newBluePrint, newBluePrint.getSecondCardFace(), this, source);
|
||||
}
|
||||
}
|
||||
if (applier != null) {
|
||||
|
|
|
@ -306,7 +306,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
|| getAttackers().size() <= 1) {
|
||||
return;
|
||||
}
|
||||
boolean canBand = attacker.getAbilities().containsKey(BandingAbility.getInstance().getId());
|
||||
boolean canBand = attacker.hasAbility(BandingAbility.getInstance(), game);
|
||||
List<Ability> bandsWithOther = new ArrayList<>();
|
||||
for (Ability ability : attacker.getAbilities()) {
|
||||
if (ability.getClass().equals(BandsWithOtherAbility.class)) {
|
||||
|
@ -390,7 +390,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
permanent.addBandedCard(creatureId);
|
||||
attacker.addBandedCard(targetId);
|
||||
if (canBand) {
|
||||
if (!permanent.getAbilities().containsKey(BandingAbility.getInstance().getId())) {
|
||||
if (!permanent.hasAbility(BandingAbility.getInstance(), game)) {
|
||||
filter.add(new AbilityPredicate(BandingAbility.class));
|
||||
canBandWithOther = false;
|
||||
}
|
||||
|
@ -1289,7 +1289,8 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
if (attacker != null) {
|
||||
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKER, defenderId, creatureId, playerId))) {
|
||||
if (addAttackerToCombat(creatureId, defenderId, game)) {
|
||||
if (!attacker.getAbilities().containsKey(VigilanceAbility.getInstance().getId()) && !attacker.getAbilities().containsKey(JohanVigilanceAbility.getInstance().getId())) {
|
||||
if (!attacker.hasAbility(VigilanceAbility.getInstance(), game)
|
||||
&& !attacker.hasAbility(JohanVigilanceAbility.getInstance(), game)) {
|
||||
if (!attacker.isTapped()) {
|
||||
attacker.setTapped(true);
|
||||
attackersTappedByAttack.add(attacker.getId());
|
||||
|
|
|
@ -158,12 +158,12 @@ public class Commander implements CommandObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
if (this.getAbilities().containsKey(abilityId)) {
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
if (this.getAbilities().contains(ability)) {
|
||||
return true;
|
||||
}
|
||||
Abilities<Ability> otherAbilities = game.getState().getAllOtherAbilities(getId());
|
||||
return otherAbilities != null && otherAbilities.containsKey(abilityId);
|
||||
return otherAbilities != null && otherAbilities.contains(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -173,8 +173,8 @@ public class Emblem implements CommandObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
return abilites.containsKey(abilityId);
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
return getAbilities().contains(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -182,8 +182,8 @@ public class Plane implements CommandObject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAbility(UUID abilityId, Game game) {
|
||||
return abilites.containsKey(abilityId);
|
||||
public boolean hasAbility(Ability ability, Game game) {
|
||||
return getAbilities().contains(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -120,7 +120,7 @@ class TrailOfTheMageRingsReboundEffect extends ContinuousEffectImpl {
|
|||
|
||||
private void addReboundAbility(Card card, Ability source, Game game) {
|
||||
if (filter.match(card, game)) {
|
||||
boolean found = card.getAbilities().stream().anyMatch(ability -> ability instanceof ReboundAbility);
|
||||
boolean found = card.getAbilities(game).containsClass(ReboundAbility.class);
|
||||
if (!found) {
|
||||
Ability ability = new ReboundAbility();
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
|
|
|
@ -327,16 +327,16 @@ public class Battlefield implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
public List<Permanent> getPhasedIn(UUID controllerId) {
|
||||
public List<Permanent> getPhasedIn(Game game, UUID controllerId) {
|
||||
return field.values()
|
||||
.stream()
|
||||
.filter(perm -> perm.getAbilities().containsKey(PhasingAbility.getInstance().getId())
|
||||
.filter(perm -> perm.hasAbility(PhasingAbility.getInstance(), game)
|
||||
&& perm.isPhasedIn()
|
||||
&& perm.isControlledBy(controllerId))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Permanent> getPhasedOut(UUID controllerId) {
|
||||
public List<Permanent> getPhasedOut(Game game, UUID controllerId) {
|
||||
return field.values()
|
||||
.stream()
|
||||
.filter(perm -> !perm.isPhasedIn() && perm.isControlledBy(controllerId))
|
||||
|
|
|
@ -154,15 +154,14 @@ public interface Permanent extends Card, Controllable {
|
|||
|
||||
String getValue(GameState state);
|
||||
|
||||
@Deprecated
|
||||
void addAbility(Ability ability, Game game);
|
||||
|
||||
void addAbility(Ability ability, UUID sourceId, Game game);
|
||||
|
||||
void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId);
|
||||
|
||||
void removeAllAbilities(UUID sourceId, Game game);
|
||||
|
||||
void removeAbility(Ability abilityToRemove, UUID sourceId, Game game);
|
||||
|
||||
void removeAbilities(List<Ability> abilitiesToRemove, UUID sourceId, Game game);
|
||||
|
||||
void addLoyaltyUsed();
|
||||
|
||||
boolean canLoyaltyBeUsed(Game game);
|
||||
|
|
|
@ -53,7 +53,7 @@ public class PermanentCard extends PermanentImpl {
|
|||
if (game.getState().getValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId()) != null) {
|
||||
game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + getId(), null);
|
||||
setTransformed(true);
|
||||
TransformAbility.transform(this, getSecondCardFace(), game);
|
||||
TransformAbility.transform(this, getSecondCardFace(), game, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -351,62 +351,61 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
|
||||
@Override
|
||||
public Abilities<Ability> getAbilities() {
|
||||
return abilities;
|
||||
return super.getAbilities();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Abilities<Ability> getAbilities(Game game) {
|
||||
return abilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ability
|
||||
* @param game
|
||||
*/
|
||||
@Override
|
||||
public void addAbility(Ability ability, Game game) {
|
||||
if (!abilities.containsKey(ability.getId())) {
|
||||
Ability copyAbility = ability.copy();
|
||||
copyAbility.setControllerId(controllerId);
|
||||
copyAbility.setSourceId(objectId);
|
||||
if (game != null) {
|
||||
game.getState().addAbility(copyAbility, this);
|
||||
}
|
||||
abilities.add(copyAbility);
|
||||
}
|
||||
return super.getAbilities(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAbility(Ability ability, UUID sourceId, Game game) {
|
||||
addAbility(ability, sourceId, game, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAbility(Ability ability, UUID sourceId, Game game, boolean createNewId) {
|
||||
// singleton abilities -- only one instance
|
||||
// other abilities -- any amount of instances
|
||||
if (!abilities.containsKey(ability.getId())) {
|
||||
Ability copyAbility = ability.copy();
|
||||
if (createNewId) {
|
||||
copyAbility.newId(); // needed so that source can get an ability multiple times (e.g. Raging Ravine)
|
||||
}
|
||||
copyAbility.newId(); // needed so that source can get an ability multiple times (e.g. Raging Ravine)
|
||||
copyAbility.setControllerId(controllerId);
|
||||
copyAbility.setSourceId(objectId);
|
||||
// triggered abilities must be added to the state().triggers
|
||||
// still as long as the prev. permanent is known to the LKI (e.g. Showstopper) so gained dies triggered ability will trigger
|
||||
game.getState().addAbility(copyAbility, sourceId, this);
|
||||
abilities.add(copyAbility);
|
||||
} else if (!createNewId) {
|
||||
// triggered abilities must be added to the state().triggerdAbilities
|
||||
// still as long as the prev. permanent is known to the LKI (e.g. Showstopper) so gained dies triggered ability will trigger
|
||||
if (!game.getBattlefield().containsPermanent(this.getId())) {
|
||||
Ability copyAbility = ability.copy();
|
||||
copyAbility.setControllerId(controllerId);
|
||||
copyAbility.setSourceId(objectId);
|
||||
game.getState().addAbility(copyAbility, sourceId, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllAbilities(UUID sourceId, Game game) {
|
||||
getAbilities().clear();
|
||||
// TODO: what about triggered abilities? See addAbility above -- triggers adds to GameState
|
||||
abilities.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAbility(Ability abilityToRemove, UUID sourceId, Game game) {
|
||||
if (abilityToRemove == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 112.10b Effects that remove an ability remove all instances of it.
|
||||
List<Ability> toRemove = new ArrayList<>();
|
||||
abilities.forEach(a -> {
|
||||
if (a.isSameInstance(abilityToRemove)) {
|
||||
toRemove.add(a);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: what about triggered abilities? See addAbility above -- triggers adds to GameState
|
||||
toRemove.forEach(r -> abilities.remove(r));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAbilities(List<Ability> abilitiesToRemove, UUID sourceId, Game game){
|
||||
if (abilitiesToRemove == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
abilitiesToRemove.forEach(a -> removeAbility(a, sourceId, game));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -728,7 +727,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
game.fireEvent(new GameEvent(EventType.GAINED_CONTROL, objectId, objectId, controllerId));
|
||||
|
||||
return true;
|
||||
} else if (isCopy()) {// Because the previous copied abilities can be from another controller chnage controller in any case for abilities
|
||||
} else if (isCopy()) {// Because the previous copied abilities can be from another controller - change controller in any case for abilities
|
||||
this.getAbilities(game).setControllerId(controllerId);
|
||||
game.getContinuousEffects().setController(objectId, controllerId);
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ public class PermanentToken extends PermanentImpl {
|
|||
} else {
|
||||
// first time -> create ContinuousEffects only once
|
||||
for (Ability ability : token.getAbilities()) {
|
||||
this.addAbility(ability, game);
|
||||
this.addAbility(ability, null, game);
|
||||
}
|
||||
}
|
||||
this.abilities.setControllerId(this.controllerId);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue