mirror of
https://github.com/correl/mage.git
synced 2024-11-28 19:19:55 +00:00
[AFR] Implementing Class enchantments (ready for review) (#7992)
* [AFR] Implemented Druid Class * [AFR] Implemented Wizard Class * [AFR] Implemented Cleric Class * [AFR] Implemented Fighter Class * reworked class ability implementation * fixed an error with setting class level * small reworking of class triggers * added class level hint * added tests * small change * added common class for reminder text
This commit is contained in:
parent
d00765c2d5
commit
5b88484cb6
17 changed files with 884 additions and 5 deletions
141
Mage.Sets/src/mage/cards/c/ClericClass.java
Normal file
141
Mage.Sets/src/mage/cards/c/ClericClass.java
Normal file
|
@ -0,0 +1,141 @@
|
|||
package mage.cards.c;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BecomesClassLevelTriggeredAbility;
|
||||
import mage.abilities.common.GainLifeControllerTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.continuous.GainClassAbilitySourceEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.abilities.keyword.ClassLevelAbility;
|
||||
import mage.abilities.keyword.ClassReminderAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class ClericClass extends CardImpl {
|
||||
|
||||
public ClericClass(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}");
|
||||
|
||||
this.subtype.add(SubType.CLASS);
|
||||
|
||||
// (Gain the next level as a sorcery to add its ability.)
|
||||
this.addAbility(new ClassReminderAbility());
|
||||
|
||||
// If you would gain life, you gain that much life plus 1 instead.
|
||||
this.addAbility(new SimpleStaticAbility(new ClericClassLifeEffect()));
|
||||
|
||||
// {3}{W}: Level 2
|
||||
this.addAbility(new ClassLevelAbility(2, "{3}{W}"));
|
||||
|
||||
// Whenever you gain life, put a +1/+1 counter on target creature you control.
|
||||
Ability ability = new GainLifeControllerTriggeredAbility(
|
||||
new AddCountersTargetEffect(CounterType.P1P1.createInstance())
|
||||
);
|
||||
ability.addTarget(new TargetControlledCreaturePermanent());
|
||||
this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(ability, 2)));
|
||||
|
||||
// {4}{W}: Level 3
|
||||
this.addAbility(new ClassLevelAbility(3, "{4}{W}"));
|
||||
|
||||
// When this Class becomes level 3, return target creature card from your graveyard to the battlefield. You gain life equal to its toughness.
|
||||
ability = new BecomesClassLevelTriggeredAbility(new ClericClassReturnEffect(), 3);
|
||||
ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private ClericClass(final ClericClass card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClericClass copy() {
|
||||
return new ClericClass(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ClericClassLifeEffect extends ReplacementEffectImpl {
|
||||
|
||||
ClericClassLifeEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Benefit);
|
||||
staticText = "If you would gain life, you gain that much life plus 1 instead";
|
||||
}
|
||||
|
||||
private ClericClassLifeEffect(final ClericClassLifeEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClericClassLifeEffect copy() {
|
||||
return new ClericClassLifeEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
event.setAmount(event.getAmount() + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.GAIN_LIFE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return source.isControlledBy(event.getPlayerId());
|
||||
}
|
||||
}
|
||||
|
||||
class ClericClassReturnEffect extends OneShotEffect {
|
||||
|
||||
ClericClassReturnEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "return target creature card from your graveyard to the battlefield. " +
|
||||
"You gain life equal to its toughness";
|
||||
}
|
||||
|
||||
private ClericClassReturnEffect(final ClericClassReturnEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClericClassReturnEffect copy() {
|
||||
return new ClericClassReturnEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(source.getFirstTarget());
|
||||
if (player == null || card == null) {
|
||||
return false;
|
||||
}
|
||||
player.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
Permanent permanent = game.getPermanent(card.getId());
|
||||
int toughness = permanent != null ? permanent.getToughness().getValue() : card.getToughness().getValue();
|
||||
player.gainLife(toughness, game, source);
|
||||
return true;
|
||||
}
|
||||
}
|
97
Mage.Sets/src/mage/cards/d/DruidClass.java
Normal file
97
Mage.Sets/src/mage/cards/d/DruidClass.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BecomesClassLevelTriggeredAbility;
|
||||
import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.dynamicvalue.common.LandsYouControlCount;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.GainClassAbilitySourceEffect;
|
||||
import mage.abilities.effects.common.continuous.PlayAdditionalLandsControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect;
|
||||
import mage.abilities.keyword.ClassLevelAbility;
|
||||
import mage.abilities.keyword.ClassReminderAbility;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.permanent.token.TokenImpl;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class DruidClass extends CardImpl {
|
||||
|
||||
public DruidClass(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}");
|
||||
|
||||
this.subtype.add(SubType.CLASS);
|
||||
|
||||
// (Gain the next level as a sorcery to add its ability.)
|
||||
this.addAbility(new ClassReminderAbility());
|
||||
|
||||
// Whenever a land enters the battlefield under your control, you gain 1 life.
|
||||
this.addAbility(new EntersBattlefieldControlledTriggeredAbility(
|
||||
new GainLifeEffect(1), StaticFilters.FILTER_CONTROLLED_LAND_SHORT_TEXT
|
||||
));
|
||||
|
||||
// {2}{G}: Level 2
|
||||
this.addAbility(new ClassLevelAbility(2, "{2}{G}"));
|
||||
|
||||
// You may play an additional land on each of your turns.
|
||||
this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(
|
||||
new PlayAdditionalLandsControllerEffect(1, Duration.WhileOnBattlefield), 2
|
||||
)));
|
||||
|
||||
// {4}{G}: Level 3
|
||||
this.addAbility(new ClassLevelAbility(3, "{4}{G}"));
|
||||
|
||||
// When this Class becomes level 3, target land you control becomes a creature with haste and "This creature's power and toughness are each equal to the number of lands you control." It's still a land.
|
||||
Ability ability = new BecomesClassLevelTriggeredAbility(new BecomesCreatureTargetEffect(
|
||||
new DruidClassToken(), false, true, Duration.Custom
|
||||
), 3);
|
||||
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private DruidClass(final DruidClass card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DruidClass copy() {
|
||||
return new DruidClass(this);
|
||||
}
|
||||
}
|
||||
|
||||
class DruidClassToken extends TokenImpl {
|
||||
|
||||
DruidClassToken() {
|
||||
super("", "creature with haste and \"This creature's power and toughness are each equal to the number of lands you control.\"");
|
||||
this.cardType.add(CardType.CREATURE);
|
||||
this.power = new MageInt(0);
|
||||
this.toughness = new MageInt(0);
|
||||
|
||||
this.addAbility(HasteAbility.getInstance());
|
||||
this.addAbility(new SimpleStaticAbility(new SetPowerToughnessSourceEffect(
|
||||
LandsYouControlCount.instance, Duration.EndOfGame, SubLayer.SetPT_7b
|
||||
).setText("this creature's power and toughness are each equal to the number of lands you control")));
|
||||
}
|
||||
|
||||
private DruidClassToken(final DruidClassToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
||||
public DruidClassToken copy() {
|
||||
return new DruidClassToken(this);
|
||||
}
|
||||
}
|
161
Mage.Sets/src/mage/cards/f/FighterClass.java
Normal file
161
Mage.Sets/src/mage/cards/f/FighterClass.java
Normal file
|
@ -0,0 +1,161 @@
|
|||
package mage.cards.f;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.RequirementEffect;
|
||||
import mage.abilities.effects.common.continuous.GainClassAbilitySourceEffect;
|
||||
import mage.abilities.effects.common.cost.AbilitiesCostReductionControllerEffect;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
|
||||
import mage.abilities.keyword.ClassLevelAbility;
|
||||
import mage.abilities.keyword.ClassReminderAbility;
|
||||
import mage.abilities.keyword.EquipAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class FighterClass extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("an Equipment card");
|
||||
|
||||
static {
|
||||
filter.add(SubType.EQUIPMENT.getPredicate());
|
||||
}
|
||||
|
||||
public FighterClass(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}{W}");
|
||||
|
||||
this.subtype.add(SubType.CLASS);
|
||||
|
||||
// (Gain the next level as a sorcery to add its ability.)
|
||||
this.addAbility(new ClassReminderAbility());
|
||||
|
||||
// When Fighter Class enters the battlefield, search your library for an Equipment card, reveal it, put it into your hand, then shuffle.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(
|
||||
new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter))
|
||||
));
|
||||
|
||||
// {1}{R}{W}: Level 2
|
||||
this.addAbility(new ClassLevelAbility(2, "{1}{R}{W}"));
|
||||
|
||||
// Equip abilities you activate cost {2} less to activate.
|
||||
this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(
|
||||
new AbilitiesCostReductionControllerEffect(EquipAbility.class, "Equip")
|
||||
.setText("\"equip abilities you activate cost {2} less to activate\""),
|
||||
2
|
||||
)));
|
||||
|
||||
// {3}{R}{W}: Level 3
|
||||
this.addAbility(new ClassLevelAbility(3, "{3}{R}{W}"));
|
||||
|
||||
// Whenever a creature you control attacks, up to one target creature blocks it this combat if able.
|
||||
Ability ability = new AttacksCreatureYouControlTriggeredAbility(new FighterClassEffect(), false);
|
||||
ability.addTarget(new TargetCreaturePermanent(0, 1));
|
||||
this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(ability, 3)));
|
||||
}
|
||||
|
||||
private FighterClass(final FighterClass card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FighterClass copy() {
|
||||
return new FighterClass(this);
|
||||
}
|
||||
}
|
||||
|
||||
class FighterClassEffect extends OneShotEffect {
|
||||
|
||||
FighterClassEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "up to one target creature blocks it this combat if able";
|
||||
}
|
||||
|
||||
private FighterClassEffect(final FighterClassEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FighterClassEffect copy() {
|
||||
return new FighterClassEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
MageObjectReference attackerRef = (MageObjectReference) getValue("attackerRef");
|
||||
if (attackerRef == null) {
|
||||
return false;
|
||||
}
|
||||
Permanent attacker = attackerRef.getPermanent(game);
|
||||
Permanent permanent = game.getPermanent(source.getFirstTarget());
|
||||
if (attacker == null || permanent == null) {
|
||||
return false;
|
||||
}
|
||||
game.addEffect(new FighterClassRequirementEffect(attackerRef).setTargetPointer(new FixedTarget(permanent, game)), source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class FighterClassRequirementEffect extends RequirementEffect {
|
||||
|
||||
private final MageObjectReference mor;
|
||||
|
||||
public FighterClassRequirementEffect(MageObjectReference mor) {
|
||||
super(Duration.EndOfCombat);
|
||||
this.mor = mor;
|
||||
}
|
||||
|
||||
public FighterClassRequirementEffect(final FighterClassRequirementEffect effect) {
|
||||
super(effect);
|
||||
this.mor = effect.mor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Permanent permanent, Ability source, Game game) {
|
||||
Permanent attacker = mor.getPermanent(game);
|
||||
if (attacker == null) {
|
||||
discard();
|
||||
return false;
|
||||
}
|
||||
return permanent != null
|
||||
&& permanent.getId().equals(this.getTargetPointer().getFirst(game, source))
|
||||
&& permanent.canBlock(source.getSourceId(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mustAttack(Game game) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mustBlock(Game game) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID mustBlockAttacker(Ability source, Game game) {
|
||||
return mor.getSourceId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FighterClassRequirementEffect copy() {
|
||||
return new FighterClassRequirementEffect(this);
|
||||
}
|
||||
}
|
67
Mage.Sets/src/mage/cards/w/WizardClass.java
Normal file
67
Mage.Sets/src/mage/cards/w/WizardClass.java
Normal file
|
@ -0,0 +1,67 @@
|
|||
package mage.cards.w;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BecomesClassLevelTriggeredAbility;
|
||||
import mage.abilities.common.DrawCardControllerTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.GainClassAbilitySourceEffect;
|
||||
import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.abilities.keyword.ClassLevelAbility;
|
||||
import mage.abilities.keyword.ClassReminderAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class WizardClass extends CardImpl {
|
||||
|
||||
public WizardClass(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}");
|
||||
|
||||
this.subtype.add(SubType.CLASS);
|
||||
|
||||
// (Gain the next level as a sorcery to add its ability.)
|
||||
this.addAbility(new ClassReminderAbility());
|
||||
|
||||
// You have no maximum hand size.
|
||||
this.addAbility(new SimpleStaticAbility(new MaximumHandSizeControllerEffect(
|
||||
Integer.MAX_VALUE, Duration.WhileOnBattlefield,
|
||||
MaximumHandSizeControllerEffect.HandSizeModification.SET
|
||||
)));
|
||||
|
||||
// {2}{U}: Level 2
|
||||
this.addAbility(new ClassLevelAbility(2, "{2}{U}"));
|
||||
|
||||
// When this Class becomes level 2, draw two cards.
|
||||
this.addAbility(new BecomesClassLevelTriggeredAbility(new DrawCardSourceControllerEffect(2), 2));
|
||||
|
||||
// {4}{U}: Level 3
|
||||
this.addAbility(new ClassLevelAbility(3, "{4}{U}"));
|
||||
|
||||
// Whenever you draw a card, put a +1/+1 counter on target creature you control.
|
||||
Ability ability = new DrawCardControllerTriggeredAbility(
|
||||
new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false
|
||||
);
|
||||
ability.addTarget(new TargetControlledCreaturePermanent());
|
||||
this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(ability, 3)));
|
||||
}
|
||||
|
||||
private WizardClass(final WizardClass card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WizardClass copy() {
|
||||
return new WizardClass(this);
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ public final class AdventuresInTheForgottenRealms extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Circle of Dreams Druid", 176, Rarity.RARE, mage.cards.c.CircleOfDreamsDruid.class));
|
||||
cards.add(new SetCardInfo("Circle of the Moon Druid", 177, Rarity.COMMON, mage.cards.c.CircleOfTheMoonDruid.class));
|
||||
cards.add(new SetCardInfo("Clattering Skeletons", 93, Rarity.COMMON, mage.cards.c.ClatteringSkeletons.class));
|
||||
cards.add(new SetCardInfo("Cleric Class", 6, Rarity.UNCOMMON, mage.cards.c.ClericClass.class));
|
||||
cards.add(new SetCardInfo("Clever Conjurer", 51, Rarity.COMMON, mage.cards.c.CleverConjurer.class));
|
||||
cards.add(new SetCardInfo("Cloister Gargoyle", 7, Rarity.UNCOMMON, mage.cards.c.CloisterGargoyle.class));
|
||||
cards.add(new SetCardInfo("Compelled Duel", 178, Rarity.COMMON, mage.cards.c.CompelledDuel.class));
|
||||
|
@ -77,6 +78,7 @@ public final class AdventuresInTheForgottenRealms extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Dragon's Fire", 139, Rarity.COMMON, mage.cards.d.DragonsFire.class));
|
||||
cards.add(new SetCardInfo("Drider", 98, Rarity.UNCOMMON, mage.cards.d.Drider.class));
|
||||
cards.add(new SetCardInfo("Drizzt Do'Urden", 220, Rarity.RARE, mage.cards.d.DrizztDoUrden.class));
|
||||
cards.add(new SetCardInfo("Druid Class", 180, Rarity.UNCOMMON, mage.cards.d.DruidClass.class));
|
||||
cards.add(new SetCardInfo("Dueling Rapier", 140, Rarity.COMMON, mage.cards.d.DuelingRapier.class));
|
||||
cards.add(new SetCardInfo("Dungeon Crawler", 99, Rarity.UNCOMMON, mage.cards.d.DungeonCrawler.class));
|
||||
cards.add(new SetCardInfo("Dungeon Descent", 255, Rarity.RARE, mage.cards.d.DungeonDescent.class));
|
||||
|
@ -94,6 +96,7 @@ public final class AdventuresInTheForgottenRealms extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Fates' Reversal", 102, Rarity.COMMON, mage.cards.f.FatesReversal.class));
|
||||
cards.add(new SetCardInfo("Feign Death", 103, Rarity.COMMON, mage.cards.f.FeignDeath.class));
|
||||
cards.add(new SetCardInfo("Fifty Feet of Rope", 244, Rarity.UNCOMMON, mage.cards.f.FiftyFeetOfRope.class));
|
||||
cards.add(new SetCardInfo("Fighter Class", 222, Rarity.RARE, mage.cards.f.FighterClass.class));
|
||||
cards.add(new SetCardInfo("Find the Path", 183, Rarity.COMMON, mage.cards.f.FindThePath.class));
|
||||
cards.add(new SetCardInfo("Flumph", 15, Rarity.RARE, mage.cards.f.Flumph.class));
|
||||
cards.add(new SetCardInfo("Fly", 59, Rarity.UNCOMMON, mage.cards.f.Fly.class));
|
||||
|
@ -241,6 +244,7 @@ public final class AdventuresInTheForgottenRealms extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("White Dragon", 41, Rarity.UNCOMMON, mage.cards.w.WhiteDragon.class));
|
||||
cards.add(new SetCardInfo("Wight", 127, Rarity.RARE, mage.cards.w.Wight.class));
|
||||
cards.add(new SetCardInfo("Wild Shape", 212, Rarity.UNCOMMON, mage.cards.w.WildShape.class));
|
||||
cards.add(new SetCardInfo("Wizard Class", 81, Rarity.UNCOMMON, mage.cards.w.WizardClass.class));
|
||||
cards.add(new SetCardInfo("Xorn", 167, Rarity.RARE, mage.cards.x.Xorn.class));
|
||||
cards.add(new SetCardInfo("You Come to a River", 83, Rarity.COMMON, mage.cards.y.YouComeToARiver.class));
|
||||
cards.add(new SetCardInfo("You Come to the Gnoll Camp", 168, Rarity.COMMON, mage.cards.y.YouComeToTheGnollCamp.class));
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package org.mage.test.cards.enchantments;
|
||||
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class ClassTest extends CardTestPlayerBase {
|
||||
|
||||
private static final String wizard = "Wizard Class";
|
||||
private static final String merfolk = "Merfolk of the Pearl Trident";
|
||||
private static final String druid = "Druid Class";
|
||||
private static final String forest = "Forest";
|
||||
private static final String wastes = "Wastes";
|
||||
|
||||
private void assertClassLevel(String cardName, int level) {
|
||||
Permanent permanent = getPermanent(cardName);
|
||||
Assert.assertEquals(
|
||||
cardName + " should be level " + level +
|
||||
" but was level " + permanent.getClassLevel(),
|
||||
level, permanent.getClassLevel()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_WizardClass__FirstLevel() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, wizard);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertClassLevel(wizard, 1);
|
||||
assertHandCount(playerA, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_WizardClass__SecondLevel() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, wizard);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U}");
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertClassLevel(wizard, 2);
|
||||
assertHandCount(playerA, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_WizardClass__ThirdLevel() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 8);
|
||||
addCard(Zone.BATTLEFIELD, playerA, wizard);
|
||||
addCard(Zone.BATTLEFIELD, playerA, merfolk);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U}");
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}{U}");
|
||||
|
||||
setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertClassLevel(wizard, 3);
|
||||
assertHandCount(playerA, 3);
|
||||
assertCounterCount(merfolk, CounterType.P1P1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DruidClass__FirstLevel() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, druid);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
assertClassLevel(druid, 1);
|
||||
assertHandCount(playerA, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DruidClass__SecondLevel() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, forest, 3);
|
||||
addCard(Zone.BATTLEFIELD, playerA, druid);
|
||||
addCard(Zone.HAND, playerA, forest, 2);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{G}");
|
||||
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, forest);
|
||||
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, forest);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertClassLevel(druid, 2);
|
||||
assertHandCount(playerA, 0);
|
||||
assertPermanentCount(playerA, forest, 5);
|
||||
assertLife(playerA, 20 + 1 + 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DruidClass__ThirdLevel() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, forest, 6);
|
||||
addCard(Zone.BATTLEFIELD, playerA, druid);
|
||||
addCard(Zone.HAND, playerA, forest);
|
||||
addCard(Zone.HAND, playerA, wastes);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{G}");
|
||||
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, forest);
|
||||
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, wastes);
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{4}{G}");
|
||||
addTarget(playerA, wastes);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertClassLevel(druid, 3);
|
||||
assertHandCount(playerA, 0);
|
||||
assertPermanentCount(playerA, forest, 7);
|
||||
assertLife(playerA, 20 + 1 + 1);
|
||||
assertType(wastes, CardType.CREATURE, true);
|
||||
assertPowerToughness(playerA, wastes, 8, 8);
|
||||
assertAbility(playerA, wastes, HasteAbility.getInstance(), true);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.MageObjectReference;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
|
@ -64,6 +65,7 @@ public class AttacksCreatureYouControlTriggeredAbility extends TriggeredAbilityI
|
|||
if (setTargetPointer) {
|
||||
this.getEffects().setTargetPointer(new FixedTarget(event.getSourceId(), game));
|
||||
}
|
||||
this.getEffects().setValue("attackerRef", new MageObjectReference(sourcePermanent, game));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package mage.abilities.common;
|
||||
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class BecomesClassLevelTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private final int level;
|
||||
|
||||
public BecomesClassLevelTriggeredAbility(Effect effect, int level) {
|
||||
super(Zone.BATTLEFIELD, effect);
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
private BecomesClassLevelTriggeredAbility(final BecomesClassLevelTriggeredAbility ability) {
|
||||
super(ability);
|
||||
this.level = ability.level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.GAINS_CLASS_LEVEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return event.getAmount() == level && event.getSourceId().equals(getSourceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BecomesClassLevelTriggeredAbility copy() {
|
||||
return new BecomesClassLevelTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "When this Class becomes level " + level + ", " + super.getRule();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package mage.abilities.effects.common.continuous;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class GainClassAbilitySourceEffect extends ContinuousEffectImpl implements SourceEffect {
|
||||
|
||||
private final Ability ability;
|
||||
private final int level;
|
||||
|
||||
public GainClassAbilitySourceEffect(Effect effect, int level) {
|
||||
this(new SimpleStaticAbility(effect), level);
|
||||
}
|
||||
|
||||
public GainClassAbilitySourceEffect(Ability ability, int level) {
|
||||
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
|
||||
staticText = ability.getRule();
|
||||
this.ability = ability;
|
||||
this.level = level;
|
||||
this.ability.setRuleVisible(false);
|
||||
generateGainAbilityDependencies(ability, null);
|
||||
}
|
||||
|
||||
private GainClassAbilitySourceEffect(final GainClassAbilitySourceEffect effect) {
|
||||
super(effect);
|
||||
this.level = effect.level;
|
||||
this.ability = effect.ability;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
if (permanent == null || permanent.getClassLevel() < level) {
|
||||
return false;
|
||||
}
|
||||
permanent.addAbility(ability, source.getSourceId(), game);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GainClassAbilitySourceEffect copy() {
|
||||
return new GainClassAbilitySourceEffect(this);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package mage.abilities.effects.common.cost;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbility;
|
||||
import mage.constants.CostModificationType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
|
@ -8,22 +9,28 @@ import mage.game.Game;
|
|||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Styxo
|
||||
*/
|
||||
public class AbilitiesCostReductionControllerEffect extends CostModificationEffectImpl {
|
||||
|
||||
private final Class activatedAbility;
|
||||
private final Class<? extends ActivatedAbility> activatedAbility;
|
||||
private final int amount;
|
||||
|
||||
public AbilitiesCostReductionControllerEffect(Class activatedAbility, String activatedAbilityName) {
|
||||
public AbilitiesCostReductionControllerEffect(Class<? extends ActivatedAbility> activatedAbility, String activatedAbilityName) {
|
||||
this(activatedAbility, activatedAbilityName, 1);
|
||||
}
|
||||
|
||||
public AbilitiesCostReductionControllerEffect(Class<? extends ActivatedAbility> activatedAbility, String activatedAbilityName, int amount) {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
|
||||
this.activatedAbility = activatedAbility;
|
||||
staticText = activatedAbilityName + " costs you pay cost {1} less";
|
||||
staticText = activatedAbilityName + " costs you pay cost {" + amount + "} less";
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public AbilitiesCostReductionControllerEffect(AbilitiesCostReductionControllerEffect effect) {
|
||||
super(effect);
|
||||
this.activatedAbility = effect.activatedAbility;
|
||||
this.amount = effect.amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -33,7 +40,7 @@ public class AbilitiesCostReductionControllerEffect extends CostModificationEffe
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||
CardUtil.reduceCost(abilityToModify, 1);
|
||||
CardUtil.reduceCost(abilityToModify, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package mage.abilities.hint.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public enum ClassLevelHint implements Hint {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public String getText(Game game, Ability ability) {
|
||||
Permanent permanent = ability.getSourcePermanentIfItStillExists(game);
|
||||
if (permanent == null) {
|
||||
return null;
|
||||
}
|
||||
return "Class level: " + permanent.getClassLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLevelHint copy() {
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.ActivatedAbilityImpl;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TimingRule;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class ClassLevelAbility extends ActivatedAbilityImpl {
|
||||
|
||||
private final int level;
|
||||
private final String manaString;
|
||||
|
||||
public ClassLevelAbility(int level, String manaString) {
|
||||
super(Zone.BATTLEFIELD, new SetClassLevelEffect(level), new ManaCostsImpl<>(manaString));
|
||||
this.level = level;
|
||||
this.manaString = manaString;
|
||||
setTiming(TimingRule.SORCERY);
|
||||
}
|
||||
|
||||
private ClassLevelAbility(final ClassLevelAbility ability) {
|
||||
super(ability);
|
||||
this.level = ability.level;
|
||||
this.manaString = ability.manaString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLevelAbility copy() {
|
||||
return new ClassLevelAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return manaString + ": Level " + level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivationStatus canActivate(UUID playerId, Game game) {
|
||||
Permanent permanent = getSourcePermanentIfItStillExists(game);
|
||||
if (permanent != null && permanent.getClassLevel() == level - 1) {
|
||||
return super.canActivate(playerId, game);
|
||||
}
|
||||
return ActivationStatus.getFalse();
|
||||
}
|
||||
}
|
||||
|
||||
class SetClassLevelEffect extends OneShotEffect {
|
||||
|
||||
private final int level;
|
||||
|
||||
SetClassLevelEffect(int level) {
|
||||
super(Outcome.Benefit);
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
private SetClassLevelEffect(final SetClassLevelEffect effect) {
|
||||
super(effect);
|
||||
this.level = effect.level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SetClassLevelEffect copy() {
|
||||
return new SetClassLevelEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
if (permanent == null || !permanent.setClassLevel(level)) {
|
||||
return false;
|
||||
}
|
||||
game.fireEvent(GameEvent.getEvent(
|
||||
GameEvent.EventType.GAINS_CLASS_LEVEL, source.getSourceId(),
|
||||
source, source.getControllerId(), level
|
||||
));
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.hint.common.ClassLevelHint;
|
||||
import mage.constants.Zone;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public class ClassReminderAbility extends StaticAbility {
|
||||
|
||||
public ClassReminderAbility() {
|
||||
super(Zone.ALL, null);
|
||||
this.addHint(ClassLevelHint.instance);
|
||||
}
|
||||
|
||||
private ClassReminderAbility(final ClassReminderAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassReminderAbility copy() {
|
||||
return new ClassReminderAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "<i>(Gain the next level as a sorcery to add its ability.)</i>";
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ public enum SubType {
|
|||
// 205.3h Enchantments have their own unique set of subtypes; these subtypes are called enchantment types.
|
||||
AURA("Aura", SubTypeSet.EnchantmentType),
|
||||
CARTOUCHE("Cartouche", SubTypeSet.EnchantmentType),
|
||||
CLASS("Class", SubTypeSet.EnchantmentType),
|
||||
CURSE("Curse", SubTypeSet.EnchantmentType),
|
||||
RUNE("Rune", SubTypeSet.EnchantmentType),
|
||||
SAGA("Saga", SubTypeSet.EnchantmentType),
|
||||
|
|
|
@ -340,6 +340,7 @@ public class GameEvent implements Serializable {
|
|||
*/
|
||||
BECOMES_EXERTED,
|
||||
BECOMES_RENOWNED,
|
||||
GAINS_CLASS_LEVEL,
|
||||
/* BECOMES_MONARCH
|
||||
targetId playerId of the player that becomes the monarch
|
||||
sourceId id of the source object that created that effect, if no effect exist it's null
|
||||
|
|
|
@ -73,6 +73,10 @@ public interface Permanent extends Card, Controllable {
|
|||
|
||||
void setRenowned(boolean value);
|
||||
|
||||
int getClassLevel();
|
||||
|
||||
boolean setClassLevel(int classLevel);
|
||||
|
||||
void setCardNumber(String cid);
|
||||
|
||||
void setExpansionSetCode(String expansionSetCode);
|
||||
|
|
|
@ -72,6 +72,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
protected boolean renowned;
|
||||
protected boolean manifested = false;
|
||||
protected boolean morphed = false;
|
||||
protected int classLevel = 1;
|
||||
protected UUID originalControllerId;
|
||||
protected UUID controllerId;
|
||||
protected UUID beforeResetControllerId;
|
||||
|
@ -163,6 +164,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
this.transformed = permanent.transformed;
|
||||
this.monstrous = permanent.monstrous;
|
||||
this.renowned = permanent.renowned;
|
||||
this.classLevel = permanent.classLevel;
|
||||
this.pairedPermanent = permanent.pairedPermanent;
|
||||
this.bandedCards.addAll(permanent.bandedCards);
|
||||
this.timesLoyaltyUsed = permanent.timesLoyaltyUsed;
|
||||
|
@ -1519,6 +1521,20 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
|
|||
this.renowned = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getClassLevel() {
|
||||
return classLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setClassLevel(int classLevel) {
|
||||
if (this.classLevel == classLevel - 1) {
|
||||
this.classLevel = classLevel;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPairedCard(MageObjectReference pairedCard) {
|
||||
this.pairedPermanent = pairedCard;
|
||||
|
|
Loading…
Reference in a new issue