[40K] Implement Kharn the Betrayer (#10885)

* implements Kharn the Betrayer

* added flavor text

* fixed test

* fixed issue with triggered ability not working

* removed diacritic

* added effect text to test

* fixed several issues as per PR comments

* fixed access

* updated Kharn to use PreventionEffect

* added unit test for control loss via other sources
This commit is contained in:
Vivian Greenslade 2023-09-09 01:27:11 -02:30 committed by GitHub
parent 113650e48b
commit 26012ee135
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 206 additions and 0 deletions

View file

@ -0,0 +1,134 @@
package mage.cards.k;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.PreventionEffectImpl;
import mage.abilities.effects.common.DrawCardTargetEffect;
import mage.abilities.effects.common.combat.AttacksIfAbleSourceEffect;
import mage.abilities.effects.common.combat.BlocksIfAbleSourceEffect;
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.target.common.TargetOpponent;
import mage.target.targetpointer.FixedTarget;
/**
* @author Xanderhall
*/
public class KharnTheBetrayer extends CardImpl {
public KharnTheBetrayer(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
this.addSuperType(SuperType.LEGENDARY);
this.addSubType(SubType.ASTARTES, SubType.BERSERKER);
this.power = new MageInt(5);
this.toughness = new MageInt(1);
// {this} attacks or blocks each combat if able
Ability ability = new SimpleStaticAbility(new AttacksIfAbleSourceEffect(Duration.WhileOnBattlefield).setText("{this} attacks"));
ability.addEffect(new BlocksIfAbleSourceEffect(Duration.WhileOnBattlefield).setText("blocks each combat if able").concatBy("or"));
this.addAbility(ability.withFlavorWord("Berzerker"));
// When you lose control of {this}, draw two cards
this.addAbility(new KharnTheBetrayerTriggeredAbility().withFlavorWord("Sigil of Corruption"));
// If damage would be dealt to {this}, prevent that damage and an opponent of your choice gains control of it.
this.addAbility(new SimpleStaticAbility(new KharnTheBetrayerPreventionEffect()).withFlavorWord("The Betrayer"));
}
protected KharnTheBetrayer(final KharnTheBetrayer card) {
super(card);
}
@Override
public KharnTheBetrayer copy() {
return new KharnTheBetrayer(this);
}
}
class KharnTheBetrayerTriggeredAbility extends TriggeredAbilityImpl {
KharnTheBetrayerTriggeredAbility () {
super(Zone.BATTLEFIELD, new DrawCardTargetEffect(2));
this.setTriggerPhrase("When you lose control of {this}, ");
}
private KharnTheBetrayerTriggeredAbility(KharnTheBetrayerTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.LOST_CONTROL;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(sourceId)) {
this.getEffects().setTargetPointer(new FixedTarget(event.getPlayerId()));
return true;
}
return false;
}
@Override
public KharnTheBetrayerTriggeredAbility copy() {
return new KharnTheBetrayerTriggeredAbility(this);
}
}
class KharnTheBetrayerPreventionEffect extends PreventionEffectImpl {
KharnTheBetrayerPreventionEffect() {
super(Duration.WhileOnBattlefield);
}
private KharnTheBetrayerPreventionEffect(final KharnTheBetrayerPreventionEffect effect) {
super(effect);
}
@Override
public KharnTheBetrayerPreventionEffect copy() {
return new KharnTheBetrayerPreventionEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Player player = game.getPlayer(source.getControllerId());
if (player == null || preventDamageAction(event, source, game).getPreventedDamage() == 0) {
return false;
}
TargetOpponent target = new TargetOpponent();
if (!player.choose(outcome, target, source, game)) {
return false;
}
ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, true, target.getFirstTarget());
effect.setTargetPointer(new FixedTarget(source.getSourceId(), game));
ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(effect, false, "an opponent of your choice gains control of it.");
game.fireReflexiveTriggeredAbility(ability, source);
return true;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return super.applies(event, source, game) && event.getTargetId().equals(source.getSourceId());
}
}

View file

@ -150,6 +150,7 @@ public final class Warhammer40000 extends ExpansionSet {
cards.add(new SetCardInfo("Inspiring Call", 217, Rarity.UNCOMMON, mage.cards.i.InspiringCall.class));
cards.add(new SetCardInfo("Island", 307, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Keeper of Secrets", 78, Rarity.RARE, mage.cards.k.KeeperOfSecrets.class));
cards.add(new SetCardInfo("Kharn the Betrayer", 79, Rarity.RARE, mage.cards.k.KharnTheBetrayer.class));
cards.add(new SetCardInfo("Kill! Maim! Burn!", 128, Rarity.RARE, mage.cards.k.KillMaimBurn.class));
cards.add(new SetCardInfo("Knight Paladin", 160, Rarity.RARE, mage.cards.k.KnightPaladin.class));
cards.add(new SetCardInfo("Knight Rampager", 80, Rarity.RARE, mage.cards.k.KnightRampager.class));

View file

@ -0,0 +1,71 @@
package org.mage.test.cards.single._40k;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestCommander4Players;
import mage.constants.PhaseStep;
import mage.constants.Zone;
public class KharnTheBetrayerTest extends CardTestCommander4Players {
private static final String KHARN = "Kharn the Betrayer";
private static final String BOLT = "Lightning Bolt";
private static final String OFFERING = "Harmless Offering";
/**
*
Berzerker Khârn the Betrayer attacks or blocks each combat if able.
Sigil of Corruption When you lose control of Khârn the Betrayer, draw two cards.
The Betrayer If damage would be dealt to Khârn the Betrayer, prevent that damage and an opponent of your choice gains control of it.
*/
@Test
public void testEffect() {
addCard(Zone.BATTLEFIELD, playerA, KHARN, 1);
addCard(Zone.LIBRARY, playerA, "Mountain", 5);
addCard(Zone.LIBRARY, playerB, "Mountain", 5);
addCard(Zone.BATTLEFIELD, playerC, "Mountain", 2);
addCard(Zone.HAND, playerC, BOLT, 2);
// Player C pings Kharn, which triggers his effect.
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerC, BOLT, KHARN);
// Player A chooses Player B to gain control, draws 2 cards when losing control.
setChoice(playerA, "PlayerB");
// // Player C pings Kharn, triggering effect again.
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerC, BOLT, KHARN);
// // Player B chooses player A, draws 2 cards when losing control.
setChoice(playerB, "PlayerC");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
setStrictChooseMode(true);
execute();
// Account for draw for turn
assertHandCount(playerA, 1 + 2);
assertHandCount(playerB, 2);
assertHandCount(playerC, 0);
assertPermanentCount(playerC, KHARN, 1);
}
@Test
public void testLostControl() {
addCard(Zone.BATTLEFIELD, playerA, KHARN, 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
addCard(Zone.HAND, playerA, OFFERING);
addCard(Zone.LIBRARY, playerA, "Mountain", 5);
// Player A gives Kharn to player B, should trigger Kharn's effect
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, OFFERING);
addTarget(playerA, playerB);
addTarget(playerA, KHARN);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
// Account for draw for turn
assertPermanentCount(playerB, KHARN, 1);
assertHandCount(playerA, 1 + 2);
}
}