Help implementing a card
Moderators: North, BetaSteward, noxx, jeffwadsworth, JayDi, TheElk801, LevelX, CCGHQ Admins
Re: Help implementing a card
by jeffwadsworth » 03 Jan 2014, 20:40
@cbt33
Also, consider these changes to your apply section (the formating is messed up, but it is readable. I just took your code and "tried" to modify it in the code block:
Also, consider these changes to your apply section (the formating is messed up, but it is readable. I just took your code and "tried" to modify it in the code block:
- Code: Select all
@Override
public boolean apply(Game game, Ability source) {
Player you = game.getPlayer(source.getControllerId());
if (you != null) {
FilterCard filter = new FilterCard();
Player damagedPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source));
if (damagedPlayer != null) {
filter.add(new OwnerIdPredicate(damagedPlayer.getId()));
Target target = new TargetCardInGraveyard(filter);
if (you.chooseTarget(Outcome.Neutral, target, source, game) {
UUID exileId = CardUtil.getCardExileZoneId(game, source);
game.getPermanent(target.getFirstTarget()).moveToExile(exileId, "Zombie Canibal", source.getSourceId(), game);
return true;
}
}
}
return false;
}
- jeffwadsworth
- Super Tester Elite
- Posts: 1171
- Joined: 20 Oct 2010, 04:47
- Location: USA
- Has thanked: 287 times
- Been thanked: 69 times
Re: Help implementing a card
by jeffwadsworth » 14 Feb 2014, 21:00
Code for Canker Abomination. Put here for convenience.
- Code: Select all
package mage.sets.eventide;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.counters.CounterType;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetOpponent;
/**
*
* @author jeffwadsworth
*
*/
public class CankerAbomination extends CardImpl<CankerAbomination> {
public CankerAbomination(UUID ownerId) {
super(ownerId, 115, "Canker Abomination", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{B/G}{B/G}");
this.expansionSetCode = "EVE";
this.subtype.add("Treefolk");
this.subtype.add("Horror");
this.color.setGreen(true);
this.color.setBlack(true);
this.power = new MageInt(6);
this.toughness = new MageInt(6);
// As Canker Abomination enters the battlefield, choose an opponent. Canker Abomination enters the battlefield with a -1/-1 counter on it for each creature that player controls.
Ability ability = new AsEntersBattlefieldAbility(new CankerAbominationEffect());
Target target = new TargetOpponent(true);
target.setNotTarget(true);
ability.addTarget(target);
this.addAbility(ability);
}
public CankerAbomination(final CankerAbomination card) {
super(card);
}
@Override
public CankerAbomination copy() {
return new CankerAbomination(this);
}
}
class CankerAbominationEffect extends OneShotEffect<CankerAbominationEffect> {
public CankerAbominationEffect() {
super(Outcome.Neutral);
this.staticText = "choose an opponent. {this} enters the battlefield with a -1/-1 counter on it for each creature that player controls";
}
public CankerAbominationEffect(final CankerAbominationEffect effect) {
super(effect);
}
@Override
public CankerAbominationEffect copy() {
return new CankerAbominationEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Permanent CankerAbomination = game.getPermanent(source.getSourceId());
if (player != null && CankerAbomination != null) {
Player chosenPlayer = game.getPlayer(source.getFirstTarget());
if (chosenPlayer != null) {
game.informPlayers(CankerAbomination.getName() + ": " + player.getName() + " has chosen " + chosenPlayer.getName());
int amount = game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), chosenPlayer.getId(), game).size();
CankerAbomination.addCounters(CounterType.M1M1.createInstance(amount), game);
return true;
}
}
return false;
}
}
- jeffwadsworth
- Super Tester Elite
- Posts: 1171
- Joined: 20 Oct 2010, 04:47
- Location: USA
- Has thanked: 287 times
- Been thanked: 69 times
Re: Help implementing a card
by jeffwadsworth » 16 May 2014, 19:52
Having an issue with Unstoppable Ash.
The "Predicates.or" within the ChampionAbility class does not seem to work for this, yet it looks okay. The Champion ability will not allow any targets to be chosen. Permanents in play that I control include both the Treefolk and Warrior subtypes, namely Bosk Banneret and Cat Warriors.
Code:
The "Predicates.or" within the ChampionAbility class does not seem to work for this, yet it looks okay. The Champion ability will not allow any targets to be chosen. Permanents in play that I control include both the Treefolk and Warrior subtypes, namely Bosk Banneret and Cat Warriors.
Code:
- Code: Select all
package mage.sets.morningtide;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BecomesBlockedAllTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continious.BoostTargetEffect;
import mage.abilities.keyword.ChampionAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author jeffwadsworth
*
*/
public class UnstoppableAsh extends CardImpl<UnstoppableAsh> {
final static private FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature you control");
static {
filter.add(new ControllerPredicate(TargetController.YOU));
}
public UnstoppableAsh(UUID ownerId) {
super(ownerId, 137, "Unstoppable Ash", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{G}");
this.expansionSetCode = "MOR";
this.subtype.add("Treefolk");
this.subtype.add("Warrior");
this.color.setGreen(true);
this.power = new MageInt(5);
this.toughness = new MageInt(5);
// Trample
this.addAbility(TrampleAbility.getInstance());
// Champion a Treefolk or Warrior
this.addAbility(new ChampionAbility(this, "Treefolk, Warrior"));
// Whenever a creature you control becomes blocked, it gets +0/+5 until end of turn.
this.addAbility(new BecomesBlockedAllTriggeredAbility(new UnstoppableAshEffect(), false, filter, true));
}
public UnstoppableAsh(final UnstoppableAsh card) {
super(card);
}
@Override
public UnstoppableAsh copy() {
return new UnstoppableAsh(this);
}
}
class UnstoppableAshEffect extends OneShotEffect<UnstoppableAshEffect> {
UnstoppableAshEffect() {
super(Outcome.Neutral);
staticText = "it gets +0/+5 until end of turn";
}
UnstoppableAshEffect(final UnstoppableAshEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent creature = game.getPermanent(this.getTargetPointer().getFirst(game, source));
if (creature != null) {
game.addEffect(new BoostTargetEffect(0, 5, Duration.EndOfTurn), source);
return true;
}
return false;
}
@Override
public UnstoppableAshEffect copy() {
return new UnstoppableAshEffect(this);
}
}
- Code: Select all
package mage.abilities.keyword;
import java.util.ArrayList;
import java.util.UUID;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.StaticAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.costs.CostImpl;
import mage.abilities.effects.common.ReturnFromExileForSourceEffect;
import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect;
import mage.cards.Card;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.common.TargetControlledCreaturePermanent;
/*
* @author LevelX2
*
*
* 702.70. Champion
*
* 702.70a Champion represents two triggered abilities. "Champion an [object]" means
* "When this permanent enters the battlefield, sacrifice it unless you exile another
* [object] you control" and "When this permanent leaves the battlefield, return the
* exiled card to the battlefield under its owner's control."
*
* 702.70b The two abilities represented by champion are linked. See rule 607, "Linked Abilities." #
*
* 702.70c A permanent is "championed" by another permanent if the latter exiles
* the former as the direct result of a champion ability. #
*
*/
public class ChampionAbility extends StaticAbility<ChampionAbility> {
protected String[] subtypes;
protected String objectDescription;
public ChampionAbility(Card card, String subtype) {
this(card, new String[]{subtype});
}
public ChampionAbility(Card card, String[] subtypes) {
super(Zone.BATTLEFIELD, null);
this.subtypes = subtypes;
StringBuilder sb = new StringBuilder("another ");
ArrayList<Predicate<MageObject>> subtypesPredicates = new ArrayList<>();
int i = 0;
for (String subtype : this.subtypes) {
subtypesPredicates.add(new SubtypePredicate(subtype));
if (i == 0) {
sb.append(subtype);
} else {
sb.append(" or ").append(subtype);
}
i++;
}
this.objectDescription = sb.toString();
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(objectDescription);
filter.add(Predicates.or(subtypesPredicates));
filter.add(new AnotherPredicate());
// When this permanent enters the battlefield, sacrifice it unless you exile another [object] you control.
Ability ability1 = new EntersBattlefieldTriggeredAbility(
new SacrificeSourceUnlessPaysEffect(new ChampionExileCost(filter, new StringBuilder(card.getName()).append(" championed creatures").toString())),false);
ability1.setRuleVisible(false);
card.addAbility(ability1);
// When this permanent leaves the battlefield, return the exiled card to the battlefield under its owner's control.
Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false);
ability2.setRuleVisible(false);
card.addAbility(ability2);
}
public ChampionAbility(final ChampionAbility ability) {
super(ability);
this.subtypes = ability.subtypes;
this.objectDescription = ability.objectDescription;
}
@Override
public ChampionAbility copy() {
return new ChampionAbility(this);
}
@Override
public String getRule() {
StringBuilder sb = new StringBuilder("Champion ").append(objectDescription);
sb.append("<i>(When this enters the battlefield, sacrifice it unless you exile another ");
sb.append(objectDescription);
sb.append(" you control. When this leaves the battlefield, that card returns to the battlefield.)</i>");
return sb.toString();
}
}
class ChampionExileCost extends CostImpl<ChampionExileCost> {
private String exileZone = null;
public ChampionExileCost(FilterControlledCreaturePermanent filter, String exileZone) {
this.addTarget(new TargetControlledCreaturePermanent(1,1,filter, true));
this.text = "exile " + filter.getMessage() + " you control";
this.exileZone = exileZone;
}
public ChampionExileCost(ChampionExileCost cost) {
super(cost);
this.exileZone = cost.exileZone;
}
@Override
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana) {
if (targets.choose(Outcome.Exile, controllerId, sourceId, game)) {
for (UUID targetId: targets.get(0).getTargets()) {
Permanent permanent = game.getPermanent(targetId);
if (permanent == null) {
return false;
}
paid |= permanent.moveToExile(sourceId, exileZone, sourceId, game);
}
}
return paid;
}
@Override
public boolean canPay(UUID sourceId, UUID controllerId, Game game) {
return targets.canChoose(controllerId, game);
}
@Override
public ChampionExileCost copy() {
return new ChampionExileCost(this);
}
}
- jeffwadsworth
- Super Tester Elite
- Posts: 1171
- Joined: 20 Oct 2010, 04:47
- Location: USA
- Has thanked: 287 times
- Been thanked: 69 times
Re: Help implementing a card
by LevelX » 17 May 2014, 04:49
You use the wrong signature to create the champion ability.
Instead of
this.addAbility(new ChampionAbility(this, "Treefolk, Warrior"));
use
this.addAbility(new ChampionAbility(this, new String[]{"Treefolk", "Warrior"}));
Regards
Instead of
this.addAbility(new ChampionAbility(this, "Treefolk, Warrior"));
use
this.addAbility(new ChampionAbility(this, new String[]{"Treefolk", "Warrior"}));
Regards
-
LevelX - DEVELOPER
- Posts: 1677
- Joined: 08 Dec 2011, 15:08
- Has thanked: 174 times
- Been thanked: 374 times
Re: Help implementing a card
by jeffwadsworth » 22 May 2014, 20:57
Here is the code for Godhead of Awe. It does not work, yet the code looks okay.
The casting cost of 0 is just for testing purposes.
The casting cost of 0 is just for testing purposes.
- Code: Select all
package mage.sets.shadowmoor;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.continious.SetPowerToughnessAllEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.permanent.AnotherPredicate;
/**
*
* @author jeffwadsworth
*
*/
public class GodheadOfAwe extends CardImpl<GodheadOfAwe> {
private static final FilterPermanent filter = new FilterPermanent("Other creatures");
static {
filter.add(new CardTypePredicate(CardType.CREATURE));
filter.add(new AnotherPredicate());
}
public GodheadOfAwe(UUID ownerId) {
super(ownerId, 142, "Godhead of Awe", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{0}");
this.expansionSetCode = "SHM";
this.subtype.add("Spirit");
this.subtype.add("Avatar");
this.color.setBlue(true);
this.color.setWhite(true);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Other creatures are 1/1.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SetPowerToughnessAllEffect(1, 1, Duration.WhileOnBattlefield, filter, true)));
}
public GodheadOfAwe(final GodheadOfAwe card) {
super(card);
}
@Override
public GodheadOfAwe copy() {
return new GodheadOfAwe(this);
}
}
- jeffwadsworth
- Super Tester Elite
- Posts: 1171
- Joined: 20 Oct 2010, 04:47
- Location: USA
- Has thanked: 287 times
- Been thanked: 69 times
Re: Help implementing a card
by LevelX » 23 May 2014, 06:31
I fixed a bug and submitted the changes:jeffwadsworth wrote:Here is the code for Godhead of Awe. It does not work, yet the code looks okay.
The casting cost of 0 is just for testing purposes.
- Code: Select all
package mage.sets.shadowmoor;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.continious.SetPowerToughnessAllEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.filter.predicate.permanent.AnotherPredicate;
/**
*
* @author jeffwadsworth
*
*/
public class GodheadOfAwe extends CardImpl<GodheadOfAwe> {
private static final FilterPermanent filter = new FilterPermanent("Other creatures");
static {
filter.add(new CardTypePredicate(CardType.CREATURE));
filter.add(new AnotherPredicate());
}
public GodheadOfAwe(UUID ownerId) {
super(ownerId, 142, "Godhead of Awe", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{0}");
this.expansionSetCode = "SHM";
this.subtype.add("Spirit");
this.subtype.add("Avatar");
this.color.setBlue(true);
this.color.setWhite(true);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Other creatures are 1/1.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SetPowerToughnessAllEffect(1, 1, Duration.WhileOnBattlefield, filter, true)));
}
public GodheadOfAwe(final GodheadOfAwe card) {
super(card);
}
@Override
public GodheadOfAwe copy() {
return new GodheadOfAwe(this);
}
}
SetPowerToughnessAllEffect - Fixed a bug that the effect did not work correctly for static abilities.
Was this your problem (the effect of Godhead didn't work for creatures that entered the battlefield after Godhead)or what exactly did not work?
-
LevelX - DEVELOPER
- Posts: 1677
- Joined: 08 Dec 2011, 15:08
- Has thanked: 174 times
- Been thanked: 374 times
Re: Help implementing a card
by jeffwadsworth » 23 May 2014, 13:45
Your fix worked great. The problem before the fix: When GOA entered the battlefield, the P/T were not changed. Subsequent cards entering the battlefield were not changed either.
- jeffwadsworth
- Super Tester Elite
- Posts: 1171
- Joined: 20 Oct 2010, 04:47
- Location: USA
- Has thanked: 287 times
- Been thanked: 69 times
Re: Help implementing a card
by jeffwadsworth » 30 May 2014, 20:15
Here is some testing code for Chorus of the Conclave. It is not an implementation...just a test of the simple trigger. The "cast_spell" trigger does not work for me in the latest (May 30th) build. I have seen issues like this crop up on my installation and just want to verify that it isn't something else. Does this trigger work for anyone?
- Code: Select all
package mage.sets.commander;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.keyword.ForestwalkAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.GameEvent;
/**
*
* @author jeffwadsworth
*
*/
public class ChorusOfTheConclave extends CardImpl<ChorusOfTheConclave> {
public ChorusOfTheConclave(UUID ownerId) {
super(ownerId, 189, "Chorus of the Conclave", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{1}");
this.expansionSetCode = "CMD";
this.supertype.add("Legendary");
this.subtype.add("Dryad");
this.color.setGreen(true);
this.color.setWhite(true);
this.power = new MageInt(3);
this.toughness = new MageInt(8);
// Forestwalk
this.addAbility(new ForestwalkAbility());
// As an additional cost to cast creature spells, you may pay any amount of mana. If you do, that creature enters the battlefield with that many additional +1/+1 counters on it.
this.addAbility(new ChorusOfTheConclaveTriggeredAbility());
}
public ChorusOfTheConclave(final ChorusOfTheConclave card) {
super(card);
}
@Override
public ChorusOfTheConclave copy() {
return new ChorusOfTheConclave(this);
}
}
class ChorusOfTheConclaveTriggeredAbility extends TriggeredAbilityImpl<ChorusOfTheConclaveTriggeredAbility> {
ChorusOfTheConclaveTriggeredAbility() {
//super(Zone.BATTLEFIELD, new EmptyEffect(""), false);
super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(4)), true);
}
ChorusOfTheConclaveTriggeredAbility(final ChorusOfTheConclaveTriggeredAbility ability) {
super(ability);
}
@Override
public ChorusOfTheConclaveTriggeredAbility copy() {
return new ChorusOfTheConclaveTriggeredAbility(this);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.CAST_SPELL) {
System.out.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
return true;
}
return false;
}
@Override
public String getRule() {
return "As an additional cost to cast creature spells, you may pay any amount of mana. If you do, that creature enters the battlefield with that many additional +1/+1 counters on it.";
}
}
- jeffwadsworth
- Super Tester Elite
- Posts: 1171
- Joined: 20 Oct 2010, 04:47
- Location: USA
- Has thanked: 287 times
- Been thanked: 69 times
Re: Help implementing a card
by LevelX » 30 May 2014, 20:43
It does not trigger because the event CAST_SPELL is only used for replacement effects. So only game.replaceEvent() is called and you have to create a replacement effect to react to it.jeffwadsworth wrote:Here is some testing code for Chorus of the Conclave. It is not an implementation...just a test of the simple trigger. The "cast_spell" trigger does not work for me in the latest (May 30th) build. I have seen issues like this crop up on my installation and just want to verify that it isn't something else. Does this trigger work for anyone?
[/code]
Chorus of the Conclave's ability isn't a triggered ability.
Chorus of the Conclave creates an additional cost that must be added to the spell ability by a replacement effect.
-
LevelX - DEVELOPER
- Posts: 1677
- Joined: 08 Dec 2011, 15:08
- Has thanked: 174 times
- Been thanked: 374 times
Re: Help implementing a card
by jeffwadsworth » 30 May 2014, 21:12
Ahh. I noticed Molten Disaster which uses the same event check within a TriggeredAbility and thought it would work.
- Code: Select all
package mage.sets.modernmasters;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.condition.common.KickedCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continious.GainAbilitySourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.KickerAbility;
import mage.abilities.keyword.SplitSecondAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author LevelX2
*/
public class MoltenDisaster extends CardImpl<MoltenDisaster> {
public MoltenDisaster(UUID ownerId) {
super(ownerId, 123, "Molten Disaster", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{X}{R}{R}");
this.expansionSetCode = "MMA";
this.color.setRed(true);
// If Molten Disaster was kicked, it has split second.
Ability ability = new ConditionalTriggeredAbility(new MoltenDisasterTriggeredAbility(), KickedCondition.getInstance(), "");
ability.setRuleAtTheTop(true);
this.addAbility(ability);
// Kicker {R}
this.addAbility(new KickerAbility("{R}"));
// Molten Disaster deals X damage to each creature without flying and each player.
this.getSpellAbility().addEffect(new MoltenDisasterEffect());
}
public MoltenDisaster(final MoltenDisaster card) {
super(card);
}
@Override
public MoltenDisaster copy() {
return new MoltenDisaster(this);
}
}
class MoltenDisasterTriggeredAbility extends TriggeredAbilityImpl<MoltenDisasterTriggeredAbility> {
public MoltenDisasterTriggeredAbility() {
super(Zone.HAND, new GainAbilitySourceEffect(new SplitSecondAbility(), Duration.WhileOnStack), false);
}
public MoltenDisasterTriggeredAbility(final MoltenDisasterTriggeredAbility ability) {
super(ability);
}
@Override
public MoltenDisasterTriggeredAbility copy() {
return new MoltenDisasterTriggeredAbility(this);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getType().equals(GameEvent.EventType.CAST_SPELL) && event.getSourceId().equals(this.getSourceId())) {
return true;
}
return false;
}
@Override
public String getRule() {
return "If Molten Disaster was kicked, it has split second <i>(As long as this spell is on the stack, players can't cast spells or activate abilities that aren't mana abilities.)</i>";
}
}
class MoltenDisasterEffect extends OneShotEffect<MoltenDisasterEffect> {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
static {
filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class)));
}
public MoltenDisasterEffect() {
super(Outcome.Damage);
staticText = "{this} deals X damage to each creature without flying and each player";
}
public MoltenDisasterEffect(final MoltenDisasterEffect effect) {
super(effect);
}
@Override
public MoltenDisasterEffect copy() {
return new MoltenDisasterEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
int amount = source.getManaCostsToPay().getX();
for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) {
permanent.damage(amount, source.getId(), game, true, false);
}
for (UUID playerId: game.getPlayer(source.getControllerId()).getInRange()) {
Player player = game.getPlayer(playerId);
if (player != null) {
player.damage(amount, source.getSourceId(), game, false, true);
}
}
return true;
}
}
- jeffwadsworth
- Super Tester Elite
- Posts: 1171
- Joined: 20 Oct 2010, 04:47
- Location: USA
- Has thanked: 287 times
- Been thanked: 69 times
Re: Help implementing a card
by LevelX » 30 May 2014, 22:18
You selected a bad template in this case.jeffwadsworth wrote:Ahh. I noticed Molten Disaster which uses the same event check within a TriggeredAbility and thought it would work.
I guess Molten Disaster add split second ability won't work as intended.
I add it to my list of cards that have to be fixed.
-
LevelX - DEVELOPER
- Posts: 1677
- Joined: 08 Dec 2011, 15:08
- Has thanked: 174 times
- Been thanked: 374 times
Re: Help implementing a card
by Lysk » 04 Apr 2015, 19:53
Hello everyone,
I am completely new to Java and probably should have started with a less difficult card implementation, but since this one is one of my favourite commanders I thought maybe give it a shot.
So I tried implementing Scion of the Ur-Dragon, hamstering code examples from Entomb and Sarkhan's Triumph. The one thing I cannot get working right away is the correct ability implementation, so it copies the searched card once it's been put into the graveyard.
Since I (still) cannot post my code because it would make my post look "too spamy for a new user" (thanks, forum engine) here's the pastebin link: pastebin[dot]com/xEQTg2EE
Could someone fix it or tell me how to fix it please?
I am completely new to Java and probably should have started with a less difficult card implementation, but since this one is one of my favourite commanders I thought maybe give it a shot.
So I tried implementing Scion of the Ur-Dragon, hamstering code examples from Entomb and Sarkhan's Triumph. The one thing I cannot get working right away is the correct ability implementation, so it copies the searched card once it's been put into the graveyard.
Since I (still) cannot post my code because it would make my post look "too spamy for a new user" (thanks, forum engine) here's the pastebin link: pastebin[dot]com/xEQTg2EE
Could someone fix it or tell me how to fix it please?
- Code: Select all
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.timespiral;
import java.util.List;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.effects.SearchEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInLibrary;
/**
*
* @author Lysk
*/
public class ScionOfTheUrDragon extends CardImpl{
public ScionOfTheUrDragon(UUID ownerId)
{
super(ownerId, 246, "Scion of the Ur-Dragon", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{W}{U}{B}{R}{G}");
this.expansionSetCode = "TSP";
this.supertype.add("Legendary");
this.subtype.add("Dragon");
this.subtype.add("Avatar");
// WUBRG
this.color.setRed(true);
this.color.setBlue(true);
this.color.setGreen(true);
this.color.setBlack(true);
this.color.setWhite(true);
// 4/4
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// TODO: The actual ability
}
public ScionOfTheUrDragon(final ScionOfTheUrDragon card) {
super(card);
}
@Override
public ScionOfTheUrDragon copy() {
return new ScionOfTheUrDragon(this);
}
}
class ScionOfTheUrDragonEffect extends SearchEffect {
private static final FilterCreatureCard filter = new FilterCreatureCard("Dragon creature card");
static {
filter.add(new SubtypePredicate("Dragon"));
}
public ScionOfTheUrDragonEffect() {
super(new TargetCardInLibrary(new FilterCard()), Outcome.Neutral);
staticText = "Search your library for a Dragon permanent card and put it into your graveyard. If you do, Scion of the Ur-Dragon becomes a copy of that card until end of turn. Then shuffle your library.";
}
public ScionOfTheUrDragonEffect(final ScionOfTheUrDragonEffect effect) {
super(effect);
}
@Override
public ScionOfTheUrDragonEffect copy() {
return new ScionOfTheUrDragonEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
boolean result = false;
// Search your library for a Dragon permanent card
TargetCardInLibrary targetDragonPermanent = new TargetCardInLibrary(filter);
if (controller.searchLibrary(targetDragonPermanent, game)) {
if (targetDragonPermanent.getTargets().size() > 0) {
for (UUID cardId: (List<UUID>)targetDragonPermanent.getTargets()) {
Card card = controller.getLibrary().remove(cardId, game);
if (card != null) {
// and put it into your graveyard
controller.moveCardToGraveyardWithInfo(card, source.getSourceId(), game, Zone.LIBRARY);
result = true;
}
}
}
}
if(result) {
// TODO: Scion of the Ur-Dragon becomes a copy of that card until end of turn
}
controller.shuffleLibrary(game);
return result;
}
}
Re: Help implementing a card
by LevelX » 04 Apr 2015, 21:25
HihoLysk wrote:Hello everyone,
Maybe you're right.Lysk wrote:I am completely new to Java and probably should have started with a less difficult card implementation, but since this one is one of my favourite commanders I thought maybe give it a shot.
You can look at the code of Dimir Doppelganger or Lazav, Dimir Mastermind that should be good examples. Easy to find if you use the Card Tracker where you can filter e.g. only implemented cards (use % as wildcard for rule search).Lysk wrote:So I tried implementing Scion of the Ur-Dragon, hamstering code examples from Entomb and Sarkhan's Triumph. The one thing I cannot get working right away is the correct ability implementation, so it copies the searched card once it's been put into the graveyard.
But copy effects are always a bit hard and for sure there are still some quirks or better said some details in existings codes that don't work correctly in such effects.
You can starting with post number 5. So it shouldn't be a problem now. I also added your code to your post.Lysk wrote:Since I (still) cannot post my code because it would make my post look "too spamy for a new user" (thanks, forum engine) here's the pastebin link: pastebin[dot]com/xEQTg2EE
Could someone fix it or tell me how to fix it please?
And use the Perl script gen-card.pl to generate the card frame, it's helpful believe me.
E.g. you can remove the color setting from your code it's done automatically now.
If you have more questions don't hesitate to post.
-
LevelX - DEVELOPER
- Posts: 1677
- Joined: 08 Dec 2011, 15:08
- Has thanked: 174 times
- Been thanked: 374 times
Re: Help implementing a card
by TGower » 19 May 2015, 03:16
Sorry for the newbie question, but once I have finished the coding of a card how do I get it to show up in the deck editor/game for testing? Card is Rootwater Diver Creature - Merfolk with Tap, sac: return target artifact from your graveyard to your hand. Code is
- Code: Select all
...
/**
*
* @author Trevor
*/
public class RootwaterDiver extends CardImpl {
public RootwaterDiver(UUID ownerId) {
super(ownerId, 81, "Rootwater Diver", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{U}");
this.expansionSetCode = "TMP";
this.subtype.add("Merfolk");
this.color.setBlue(true);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
this.addAbility(new ColorlessManaAbility());
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new TapSourceCost());
ability.addCost(new SacrificeSourceCost());
ability.addTarget(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard")));
this.addAbility(ability);
}
public RootwaterDiver(final RootwaterDiver card) {
super(card);
}
@Override
public RootwaterDiver copy() {
return new RootwaterDiver(this);
}
}
- TGower
- Posts: 3
- Joined: 18 May 2015, 19:04
- Has thanked: 0 time
- Been thanked: 0 time
Re: Help implementing a card
by LevelX » 19 May 2015, 08:04
1) Use always the perl script gen-card.pl to create the java class frame. It's much easier than doing it by manually and helps to prevent some simple mistakes. See here to get some info about how to instrall and use it: https://github.com/magefree/mage/wiki/Developer-Getting-Started#perlTGower wrote:Sorry for the newbie question, but once I have finished the coding of a card how do I get it to show up in the deck editor/game for testing? Card is Rootwater Diver Creature - Merfolk with Tap, sac: return target artifact from your graveyard to your hand. Code isPackage and imports removed do to spam protection on the forum. File name is a java file named Rootwater Diver inside mage-sets-tempest under Mage Sets->Source Packages. I Committed the file, cleaned Mage root, Mage Client, and Mage Server before doing a Build with Dependencies and running the server and client. Not sure what I'm missing. Thank you.
- Code: Select all
...
/**
*
* @author Trevor
*/
public class RootwaterDiver extends CardImpl {
public RootwaterDiver(UUID ownerId) {
super(ownerId, 81, "Rootwater Diver", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{U}");
this.expansionSetCode = "TMP";
this.subtype.add("Merfolk");
this.color.setBlue(true);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
this.addAbility(new ColorlessManaAbility());
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandTargetEffect(), new TapSourceCost());
ability.addCost(new SacrificeSourceCost());
ability.addTarget(new TargetCardInYourGraveyard(new FilterArtifactCard("artifact card from your graveyard")));
this.addAbility(ability);
}
public RootwaterDiver(final RootwaterDiver card) {
super(card);
}
@Override
public RootwaterDiver copy() {
return new RootwaterDiver(this);
}
}
2) You don't you see your newly implemented card because the card is not added to the client H2 db. The deck editor works with the H2 client db to show the included cards.
After adding new cards in the IDE you have to check the "force database update" check box in the connect dialog window of the clinet to get the client db updated. Alternatively you can delete all files in the db folder of the client before connecting leading also to a db copy of the server db.
As the xmage server program is started, all missing card entries for the card db are generated from the card classes. For all already existing card records nothing happens this way.
That means if you change the implementation of a card that has influence of the db record, you have to delete the db files of the server before starting the xmage server, so the changes to the card class will be reflected to the db file of the server.
-
LevelX - DEVELOPER
- Posts: 1677
- Joined: 08 Dec 2011, 15:08
- Has thanked: 174 times
- Been thanked: 374 times
Who is online
Users browsing this forum: No registered users and 4 guests