Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ICE] Implement Kjeldoran Guard #11184

Merged
merged 5 commits into from
Sep 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions Mage.Sets/src/mage/cards/k/KjeldoranGuard.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package mage.cards.k;

import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.condition.common.DefendingPlayerControlsNoSourceCondition;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.decorator.ConditionalActivatedAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.SacrificeSourceEffect;
import mage.abilities.effects.common.continuous.BoostTargetEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterLandPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;

import java.util.UUID;

/**
* @author Alex-Vasile, Susucr
*/
public final class KjeldoranGuard extends CardImpl {

private static final FilterLandPermanent snowLandFiler = new FilterLandPermanent("a snow land");
Susucre marked this conversation as resolved.
Show resolved Hide resolved

static { snowLandFiler.add(SuperType.SNOW.getPredicate()); }

public KjeldoranGuard(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");

this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.SOLDIER);
this.power = new MageInt(1);
this.toughness = new MageInt(1);

// {T}: Target creature gets +1/+1 until end of turn. When that creature leaves the battlefield this turn, sacrifice Kjeldoran Guard. Activate only during combat and only if defending player controls no snow lands.
Ability ability = new ConditionalActivatedAbility(
Zone.BATTLEFIELD,
new KjeldoranGuardEffect(),
new TapSourceCost(),
new DefendingPlayerControlsNoSourceCondition(snowLandFiler),
"activate only during combat and only if defending player controls no snow lands"
);
ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability);
}

private KjeldoranGuard(final KjeldoranGuard card) { super(card); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style


@Override
public Card copy() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Covariant return

return new KjeldoranGuard(this);
}
}

class KjeldoranGuardEffect extends OneShotEffect {

KjeldoranGuardEffect() {
super(Outcome.Neutral);
staticText = "Target creature gets +1/+1 until end of turn." +
"When that creature leaves the battlefield this turn, sacrifice {this}." +
"Activate only during combat and only if defending player controls no snow lands.";
}

@Override
public boolean apply(Game game, Ability source) {
UUID targetId = source.getFirstTarget();
if (game.getPermanent(targetId) == null) {
return false;
}

// Target creature gets +1/+1 until end of turn.
BoostTargetEffect buffEffect = new BoostTargetEffect(1, 1, Duration.EndOfTurn);
buffEffect.setTargetPointer(new FixedTarget(targetId, game));
game.addEffect(buffEffect, source);

// When that creature leaves the battlefield this turn, sacrifice Kjeldoran Guard.
game.addDelayedTriggeredAbility(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please test- if you flicker Kjeldoran Guard, shouldn't have to sacrifice it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh right that should be using MOR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually that was already correct. SacrificeSourceEffect does sacrifice source.getSourceObjectIfItStillExists(game), so the Kjeldoran Guard with a different zcc will not get sacrificed (there still will be a trigger, although it does nothing).

I did replace the UUID of the target to a MOR, but that should not really matter with

  • target being still valid means that it has not leave on resolve
  • the DelayedTrigger being set with triggerOnce=true.

Copy link
Contributor Author

@Susucre Susucre Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nevermind, there is indeed an issue if you blink the Guard in response to tapping it.
Will rework with a MOR based SacrificeTargetEffect

new KjeldoranGuardDelayedTriggeredAbility(targetId),
source
);

return true;
}

private KjeldoranGuardEffect(KjeldoranGuardEffect effect) { super(effect); }

@Override
public KjeldoranGuardEffect copy() { return new KjeldoranGuardEffect(this); }
}

class KjeldoranGuardDelayedTriggeredAbility extends DelayedTriggeredAbility {

private final UUID creatureId;

KjeldoranGuardDelayedTriggeredAbility(UUID creatureId) {
super(new SacrificeSourceEffect(), Duration.EndOfTurn, true);
this.creatureId = creatureId;
}

KjeldoranGuardDelayedTriggeredAbility(KjeldoranGuardDelayedTriggeredAbility ability) {
super(ability);
this.creatureId = ability.creatureId;
}

@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
}

@Override
public boolean checkTrigger(GameEvent event, Game game) {
return event.getTargetId().equals(creatureId);
}

@Override
public KjeldoranGuardDelayedTriggeredAbility copy() {
return new KjeldoranGuardDelayedTriggeredAbility(this);
}

@Override
public String getRule() { return "sacrifice {this}"; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, this doesn't feel quite right but not sure why, can you check the delayed trigger displays with correct text on the stack?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we are displaying the trigger when those delayed trigger happens, is that was you feel is not right?

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will capitalize

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah ideally the trigger phrase should be part of it (but for sure lots of these have wrong text all over the codebase). So not required to fix, just a nice to have.

Also it doesn't get automatically capitalized which is a separate bug.

Copy link
Contributor Author

@Susucre Susucre Sep 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can replace it with "When that creature leaves the battlefield, sacrifice Kjeldoran Guard"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be "When that creature leaves the battlefield this turn, sacrifice {this}."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for a discussion point, got a comment from theelk801 last month that reflexive triggers can have their trigger left out. #10866 (comment)
So is that the line? Delayed should have trigger text but not reflexive ones? Or should that be a standalone dev discussion outside of that poor Ice Age common implementation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question - my initial reaction is I disagree but I should double check how it works.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more important to have clarity on delayed, the reflexive at least occur in close proximity to their source

}
1 change: 1 addition & 0 deletions Mage.Sets/src/mage/sets/IceAge.java
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ private IceAge() {
cards.add(new SetCardInfo("Kjeldoran Dead", 137, Rarity.COMMON, mage.cards.k.KjeldoranDead.class));
cards.add(new SetCardInfo("Kjeldoran Elite Guard", 34, Rarity.UNCOMMON, mage.cards.k.KjeldoranEliteGuard.class));
cards.add(new SetCardInfo("Kjeldoran Frostbeast", 296, Rarity.UNCOMMON, mage.cards.k.KjeldoranFrostbeast.class));
cards.add(new SetCardInfo("Kjeldoran Guard", 259, Rarity.COMMON, mage.cards.k.KjeldoranGuard.class));
cards.add(new SetCardInfo("Kjeldoran Knight", 36, Rarity.RARE, mage.cards.k.KjeldoranKnight.class));
cards.add(new SetCardInfo("Kjeldoran Phalanx", 37, Rarity.RARE, mage.cards.k.KjeldoranPhalanx.class));
cards.add(new SetCardInfo("Kjeldoran Royal Guard", 38, Rarity.RARE, mage.cards.k.KjeldoranRoyalGuard.class));
Expand Down