[KHM] Implemented Toralf, God of Fury / Toralf's Hammer

This commit is contained in:
Evan Kranzler 2021-01-13 19:32:56 -05:00
parent e18e3c79d4
commit c0efada19a
4 changed files with 253 additions and 44 deletions

View file

@ -0,0 +1,189 @@
package mage.cards.t;
import mage.MageInt;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.AttachedToMatchesFilterCondition;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.common.UnattachCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.common.TapTargetEffect;
import mage.abilities.effects.common.continuous.BoostEquippedEffect;
import mage.abilities.effects.common.continuous.GainAbilityWithAttachmentEffect;
import mage.abilities.keyword.EquipAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardSetInfo;
import mage.cards.ModalDoubleFacesCard;
import mage.constants.*;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreaturePlayerOrPlaneswalker;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.MageObjectReferencePredicate;
import mage.game.Game;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetAnyTarget;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class ToralfGodOfFury extends ModalDoubleFacesCard {
private static final Condition condition
= new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_LEGENDARY);
public ToralfGodOfFury(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo,
new CardType[]{CardType.CREATURE}, new SubType[]{SubType.GOD}, "{2}{R}{R}",
"", new CardType[]{CardType.ARTIFACT}, new SubType[]{SubType.EQUIPMENT}, "{1}{R}"
);
// 1.
// Toralf, God of Fury
// Legendary Creature - God
this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY);
this.getLeftHalfCard().setPT(new MageInt(5), new MageInt(4));
// Trample
this.getLeftHalfCard().addAbility(TrampleAbility.getInstance());
// Whenever a creature or planeswalker an opponent controls is dealt excess noncombat damage, Toralf, God of Fury deals damage equal to the excess to any target other than that permanent.
this.getLeftHalfCard().addAbility(new ToralfGodOfFuryTriggeredAbility());
// 2.
// Toralf's Hammer
// Legendary Artifact - Equipment
this.getRightHalfCard().addSuperType(SuperType.LEGENDARY);
// Equipped creature has "{1}{R}, {T}, Unattach Toralf's Hammer: It deals 3 damage to any target. Return Toralf's Hammer to its owner's hand."
this.getRightHalfCard().addAbility(new SimpleStaticAbility(new GainAbilityWithAttachmentEffect(
"equipped creature has \"{tap}, Unattach {this}: It deals 3 damage to any target. Return {this} to its owner's hand.\"",
new TapTargetEffect(), new TargetAnyTarget(), new UnattachCost(), new ManaCostsImpl<>("{1]{R}"), new TapSourceCost()
)));
// Equipped creature get +3/+0 as long as its legendary.
this.getRightHalfCard().addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
new BoostEquippedEffect(3, 0), condition,
"equipped creature get +3/+0 as long as it's legendary"
)));
// Equip {1}{R}
this.getRightHalfCard().addAbility(new EquipAbility(Outcome.BoostCreature, new ManaCostsImpl<>("{1}{R}")));
}
private ToralfGodOfFury(final ToralfGodOfFury card) {
super(card);
}
@Override
public ToralfGodOfFury copy() {
return new ToralfGodOfFury(this);
}
}
class ToralfGodOfFuryTriggeredAbility extends TriggeredAbilityImpl {
ToralfGodOfFuryTriggeredAbility() {
super(Zone.BATTLEFIELD, null);
}
private ToralfGodOfFuryTriggeredAbility(final ToralfGodOfFuryTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
switch (event.getType()) {
case DAMAGED_CREATURE:
case DAMAGED_PLANESWALKER:
return true;
}
return false;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
DamagedEvent dEvent = (DamagedEvent) event;
if (dEvent.getExcess() < 1
|| dEvent.isCombatDamage()
|| !game.getOpponents(getControllerId()).contains(game.getControllerId(event.getTargetId()))) {
return false;
}
this.getEffects().clear();
this.getTargets().clear();
this.addEffect(new DamageTargetEffect(dEvent.getExcess()));
FilterCreaturePlayerOrPlaneswalker filter = new FilterCreaturePlayerOrPlaneswalker();
filter.getPermanentFilter().add(Predicates.not(new MageObjectReferencePredicate(new MageObjectReference(event.getTargetId(), game))));
this.addTarget(new TargetAnyTarget(filter));
return true;
}
@Override
public ToralfGodOfFuryTriggeredAbility copy() {
return new ToralfGodOfFuryTriggeredAbility(this);
}
@Override
public String getRule() {
return "Whenever a creature or planeswalker an opponent controls is dealt excess noncombat damage, " +
"{this} deals damage equal to the excess to any target other than that permanent.";
}
}
class ToralfsHammerEffect extends OneShotEffect {
ToralfsHammerEffect() {
super(Outcome.Benefit);
}
private ToralfsHammerEffect(final ToralfsHammerEffect effect) {
super(effect);
}
@Override
public ToralfsHammerEffect copy() {
return new ToralfsHammerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Object object = getValue("attachedPermanent");
Player player = game.getPlayer(source.getControllerId());
if (!(object instanceof Permanent) || player == null) {
return false;
}
Permanent permanent = (Permanent) object;
Permanent targetedPermanent = game.getPermanent(source.getFirstTarget());
if (targetedPermanent == null) {
Player targetedPlayer = game.getPlayer(source.getFirstTarget());
if (targetedPlayer != null) {
targetedPlayer.damage(3, permanent.getId(), source, game);
}
} else {
targetedPermanent.damage(3, permanent.getId(), source, game);
}
player.moveCards(permanent, Zone.HAND, source, game);
return true;
}
@Override
public String getText(Mode mode) {
String name = "Toralf's Hammer";
Object object = getValue("attachedPermanent");
if (object instanceof Permanent) {
name = ((Permanent) object).getName();
}
return "It deals 3 damage to any target. Return " + name + " to its owner's hand.";
}
}

View file

@ -169,6 +169,7 @@ public final class Kaldheim extends ExpansionSet {
cards.add(new SetCardInfo("The Trickster-God's Heist", 232, Rarity.UNCOMMON, mage.cards.t.TheTricksterGodsHeist.class));
cards.add(new SetCardInfo("The World Tree", 275, Rarity.RARE, mage.cards.t.TheWorldTree.class));
cards.add(new SetCardInfo("Thornmantle Striker", 387, Rarity.UNCOMMON, mage.cards.t.ThornmantleStriker.class));
cards.add(new SetCardInfo("Toralf, God of Fury", 154, Rarity.MYTHIC, mage.cards.t.ToralfGodOfFury.class));
cards.add(new SetCardInfo("Toski, Bearer of Secrets", 197, Rarity.RARE, mage.cards.t.ToskiBearerOfSecrets.class));
cards.add(new SetCardInfo("Tyvar Kell", 198, Rarity.MYTHIC, mage.cards.t.TyvarKell.class));
cards.add(new SetCardInfo("Undersea Invader", 78, Rarity.COMMON, mage.cards.u.UnderseaInvader.class));

View file

@ -1,20 +1,19 @@
package mage.game.events;
import java.util.UUID;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public abstract class DamagedEvent extends GameEvent {
protected boolean combat;
protected int excess;
public DamagedEvent(EventType type, UUID targetId, UUID attackerId, UUID playerId, int amount, boolean combat) {
super(type, targetId, null, playerId, amount, false);
this.combat = combat;
this.excess = 0;
this.setSourceId(attackerId);
}
@ -26,4 +25,11 @@ public abstract class DamagedEvent extends GameEvent {
return flag;
}
public void setExcess(int excess) {
this.excess = Math.max(excess, 0);
}
public int getExcess() {
return excess;
}
}

View file

@ -853,7 +853,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
/**
* @param damageAmount
* @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases)
* @param attackerId id of the permanent or player who make damage (source.getSourceId() in most cases)
* @param source
* @param game
* @param preventable
@ -975,53 +975,64 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
protected int damagePlaneswalker(int damage, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
GameEvent event = new DamagePlaneswalkerEvent(objectId, attackerId, controllerId, damage, preventable, combat);
event.setAppliedEffects(appliedEffects);
if (!game.replaceEvent(event)) {
int actualDamage = checkProtectionAbilities(event, attackerId, source, game);
if (actualDamage > 0) {
int countersToRemove = actualDamage;
if (countersToRemove > getCounters(game).getCount(CounterType.LOYALTY)) {
countersToRemove = getCounters(game).getCount(CounterType.LOYALTY);
}
removeCounters(CounterType.LOYALTY.getName(), countersToRemove, source, game);
DamagedEvent damagedEvent = new DamagedPlaneswalkerEvent(objectId, attackerId, controllerId, actualDamage, combat);
game.fireEvent(damagedEvent);
game.getState().addSimultaneousDamage(damagedEvent, game);
return actualDamage;
}
if (game.replaceEvent(event)) {
return 0;
}
return 0;
int actualDamage = checkProtectionAbilities(event, attackerId, source, game);
if (actualDamage <= 0) {
return 0;
}
int countersToRemove = Math.min(actualDamage, getCounters(game).getCount(CounterType.LOYALTY));
removeCounters(CounterType.LOYALTY.getName(), countersToRemove, source, game);
DamagedEvent damagedEvent = new DamagedPlaneswalkerEvent(objectId, attackerId, controllerId, actualDamage, combat);
damagedEvent.setExcess(actualDamage - countersToRemove);
game.fireEvent(damagedEvent);
game.getState().addSimultaneousDamage(damagedEvent, game);
return actualDamage;
}
protected int damageCreature(int damage, UUID attackerId, Ability source, Game game, boolean preventable, boolean combat, boolean markDamage, List<UUID> appliedEffects) {
GameEvent event = new DamageCreatureEvent(this.getId(), attackerId, this.getControllerId(), damage, preventable, combat);
event.setAppliedEffects(appliedEffects);
if (!game.replaceEvent(event)) {
int actualDamage = checkProtectionAbilities(event, attackerId, source, game);
if (actualDamage > 0) {
MageObject attacker = game.getObject(attackerId);
if (attacker != null && (attacker.getAbilities().containsKey(InfectAbility.getInstance().getId())
|| attacker.getAbilities().containsKey(WitherAbility.getInstance().getId()))) {
if (markDamage) {
// mark damage only
markDamage(CounterType.M1M1.createInstance(actualDamage), attacker);
} else {
Ability damageSourceAbility = null;
if (attacker instanceof Permanent) {
damageSourceAbility = ((Permanent) attacker).getSpellAbility();
}
// deal damage immediately
addCounters(CounterType.M1M1.createInstance(actualDamage), damageSourceAbility, game);
}
} else {
this.damage = CardUtil.addWithOverflowCheck(this.damage, actualDamage);
}
DamagedEvent damagedEvent = new DamagedCreatureEvent(this.getId(), attackerId, this.getControllerId(), actualDamage, combat);
game.fireEvent(damagedEvent);
game.getState().addSimultaneousDamage(damagedEvent, game);
return actualDamage;
}
if (game.replaceEvent(event)) {
return 0;
}
return 0;
int actualDamage = checkProtectionAbilities(event, attackerId, source, game);
if (actualDamage <= 0) {
return 0;
}
MageObject attacker = game.getObject(attackerId);
int lethal = 0;
if (game.getState().getActivePowerInsteadOfToughnessForDamageLethalityFilters().stream().anyMatch(f -> f.match(this, game))) {
lethal = power.getValue();
} else {
lethal = toughness.getValue();
}
lethal = Math.max(lethal - this.damage, 0);
if (attacker.getAbilities().containsKey(DeathtouchAbility.getInstance().getId())) {
lethal = Math.min(1, lethal);
}
if (attacker != null && (attacker.getAbilities().containsKey(InfectAbility.getInstance().getId())
|| attacker.getAbilities().containsKey(WitherAbility.getInstance().getId()))) {
if (markDamage) {
// mark damage only
markDamage(CounterType.M1M1.createInstance(actualDamage), attacker);
} else {
Ability damageSourceAbility = null;
if (attacker instanceof Permanent) {
damageSourceAbility = ((Permanent) attacker).getSpellAbility();
}
// deal damage immediately
addCounters(CounterType.M1M1.createInstance(actualDamage), damageSourceAbility, game);
}
} else {
this.damage = CardUtil.addWithOverflowCheck(this.damage, actualDamage);
}
DamagedEvent damagedEvent = new DamagedCreatureEvent(this.getId(), attackerId, this.getControllerId(), actualDamage, combat);
damagedEvent.setExcess(actualDamage - lethal);
game.fireEvent(damagedEvent);
game.getState().addSimultaneousDamage(damagedEvent, game);
return actualDamage;
}
private int checkProtectionAbilities(GameEvent event, UUID attackerId, Ability source, Game game) {
@ -1211,6 +1222,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
/**
* Simple event without source
*
* @param eventType
* @param game
*/
@ -1220,6 +1232,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
/**
* Simple event without source
*
* @param eventType
* @param game
* @return