[ACR] Implement Ratonhnhaketon

This commit is contained in:
theelk801 2025-06-03 11:00:09 -04:00
parent 0e36541cf8
commit 8f9f317c19
6 changed files with 255 additions and 127 deletions

View file

@ -1,10 +1,8 @@
package mage.cards.k;
import mage.MageInt;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.SourceHasntDealtDamageThisGameCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.keyword.FlyingAbility;
@ -15,14 +13,8 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.watchers.Watcher;
import mage.watchers.common.DealtDamageThisGameWatcher;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
@ -49,8 +41,9 @@ public final class KarakykGuardian extends CardImpl {
// This creature has hexproof if it hasn't dealt damage yet.
this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
new GainAbilitySourceEffect(HexproofAbility.getInstance()),
KarakykGuardianCondition.instance, "{this} has hexproof if it hasn't dealt damage yet"
)), new KarakykGuardianWatcher());
SourceHasntDealtDamageThisGameCondition.instance,
"{this} has hexproof if it hasn't dealt damage yet"
)).addHint(SourceHasntDealtDamageThisGameCondition.getHint()), new DealtDamageThisGameWatcher());
}
private KarakykGuardian(final KarakykGuardian card) {
@ -62,49 +55,3 @@ public final class KarakykGuardian extends CardImpl {
return new KarakykGuardian(this);
}
}
enum KarakykGuardianCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
KarakykGuardianWatcher watcher = game.getState().getWatcher(KarakykGuardianWatcher.class);
return permanent != null && !watcher.getDamagers().contains(new MageObjectReference(permanent, game));
}
@Override
public String toString() {
return "{this} hasn't dealt damage yet";
}
}
class KarakykGuardianWatcher extends Watcher {
private final Set<MageObjectReference> damagers = new HashSet<>();
public KarakykGuardianWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
switch (event.getType()) {
case DAMAGED_PERMANENT:
case DAMAGED_PLAYER:
break;
default:
return;
}
Permanent permanent = game.getPermanent(event.getSourceId());
if (permanent != null) {
damagers.add(new MageObjectReference(permanent, game));
}
}
public Set<MageObjectReference> getDamagers() {
return damagers;
}
}

View file

@ -1,13 +1,8 @@
package mage.cards.p;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageInt;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.Condition;
import mage.abilities.condition.common.SourceHasntDealtDamageThisGameCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.keyword.FlyingAbility;
@ -19,15 +14,11 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.watchers.Watcher;
import mage.watchers.common.DealtDamageThisGameWatcher;
import java.util.UUID;
/**
*
* @author TheElk801
*/
public final class PalladiaMorsTheRuiner extends CardImpl {
@ -51,13 +42,11 @@ public final class PalladiaMorsTheRuiner extends CardImpl {
this.addAbility(TrampleAbility.getInstance());
// Palladia-Mors, the Ruiner has hexproof if it hasn't dealt damage yet.
this.addAbility(new SimpleStaticAbility(
new ConditionalContinuousEffect(
new GainAbilitySourceEffect(HexproofAbility.getInstance()),
PalladiaMorsTheRuinerCondition.instance,
"{this} has hexproof if it hasn't dealt damage yet"
)
), new PalladiaMorsTheRuinerWatcher());
this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
new GainAbilitySourceEffect(HexproofAbility.getInstance()),
SourceHasntDealtDamageThisGameCondition.instance,
"{this} has hexproof if it hasn't dealt damage yet"
)).addHint(SourceHasntDealtDamageThisGameCondition.getHint()), new DealtDamageThisGameWatcher());
}
private PalladiaMorsTheRuiner(final PalladiaMorsTheRuiner card) {
@ -69,49 +58,3 @@ public final class PalladiaMorsTheRuiner extends CardImpl {
return new PalladiaMorsTheRuiner(this);
}
}
enum PalladiaMorsTheRuinerCondition implements Condition {
instance;
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
PalladiaMorsTheRuinerWatcher watcher = game.getState().getWatcher(PalladiaMorsTheRuinerWatcher.class);
return permanent != null && !watcher.getDamagers().contains(new MageObjectReference(permanent, game));
}
@Override
public String toString() {
return "{this} hasn't dealt damage yet";
}
}
class PalladiaMorsTheRuinerWatcher extends Watcher {
private final Set<MageObjectReference> damagers = new HashSet<>();
public PalladiaMorsTheRuinerWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
switch (event.getType()) {
case DAMAGED_PERMANENT:
case DAMAGED_PLAYER:
break;
default:
return;
}
Permanent permanent = game.getPermanent(event.getSourceId());
if (permanent != null) {
damagers.add(new MageObjectReference(permanent, game));
}
}
public Set<MageObjectReference> getDamagers() {
return damagers;
}
}

View file

@ -0,0 +1,152 @@
package mage.cards.r;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
import mage.abilities.condition.common.SourceHasntDealtDamageThisGameCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.decorator.ConditionalRestrictionEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.keyword.HexproofAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.AssassinMenaceToken;
import mage.game.permanent.token.Token;
import mage.players.Player;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import mage.watchers.common.DealtDamageThisGameWatcher;
import java.util.Optional;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class Ratonhnhaketon extends CardImpl {
public Ratonhnhaketon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}{B}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.ASSASSIN);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// As long as Ratonhnhaketon hasn't dealt damage yet, it has hexproof and can't be blocked.
Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect(
new GainAbilitySourceEffect(HexproofAbility.getInstance()),
SourceHasntDealtDamageThisGameCondition.instance,
"as long as {this} hasn't dealt damage yet, it has hexproof"
));
ability.addEffect(new ConditionalRestrictionEffect(
new CantBeBlockedSourceEffect(),
SourceHasntDealtDamageThisGameCondition.instance,
"and can't be blocked"
));
this.addAbility(ability.addHint(SourceHasntDealtDamageThisGameCondition.getHint()), new DealtDamageThisGameWatcher());
// Whenever Ratonhnhaketon deals combat damage to a player, create a 1/1 black Assassin creature token with menace. When you do, return target Equipment card from your graveyard to the battlefield, then attach it to that token.
this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new RatonhnhaketonTokenEffect()));
}
private Ratonhnhaketon(final Ratonhnhaketon card) {
super(card);
}
@Override
public Ratonhnhaketon copy() {
return new Ratonhnhaketon(this);
}
}
class RatonhnhaketonTokenEffect extends OneShotEffect {
private static final FilterCard filter = new FilterCard("Equipment card from your graveyard");
static {
filter.add(SubType.EQUIPMENT.getPredicate());
}
RatonhnhaketonTokenEffect() {
super(Outcome.Benefit);
staticText = "create a 1/1 black Assassin creature token with menace. When you do, return " +
"target Equipment card from your graveyard to the battlefield, then attach it to that token";
}
private RatonhnhaketonTokenEffect(final RatonhnhaketonTokenEffect effect) {
super(effect);
}
@Override
public RatonhnhaketonTokenEffect copy() {
return new RatonhnhaketonTokenEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Token token = new AssassinMenaceToken();
token.putOntoBattlefield(1, game, source);
for (UUID tokenId : token.getLastAddedTokenIds()) {
Permanent permanent = game.getPermanent(tokenId);
if (permanent == null) {
continue;
}
ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
new RatonhnhaketonReturnEffect(tokenId), false
);
ability.addTarget(new TargetCardInYourGraveyard(filter));
game.fireReflexiveTriggeredAbility(ability, source);
}
return true;
}
}
class RatonhnhaketonReturnEffect extends OneShotEffect {
private final UUID tokenId;
RatonhnhaketonReturnEffect(UUID tokenId) {
super(Outcome.Benefit);
staticText = "return target Equipment card from your graveyard to the battlefield, then attach it to that token";
this.tokenId = tokenId;
}
private RatonhnhaketonReturnEffect(final RatonhnhaketonReturnEffect effect) {
super(effect);
this.tokenId = effect.tokenId;
}
@Override
public RatonhnhaketonReturnEffect copy() {
return new RatonhnhaketonReturnEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Card card = game.getCard(getTargetPointer().getFirst(game, source));
if (player == null) {
return false;
}
player.moveCards(card, Zone.BATTLEFIELD, source, game);
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
if (permanent == null) {
return false;
}
Optional.ofNullable(tokenId)
.map(game::getPermanent)
.ifPresent(p -> p.addAttachment(permanent.getId(), source, game));
return true;
}
}

View file

@ -237,9 +237,9 @@ public final class AssassinsCreed extends ExpansionSet {
cards.add(new SetCardInfo("Poison-Blade Mentor", 288, Rarity.UNCOMMON, mage.cards.p.PoisonBladeMentor.class));
cards.add(new SetCardInfo("Propaganda", 195, Rarity.UNCOMMON, mage.cards.p.Propaganda.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Propaganda", 85, Rarity.UNCOMMON, mage.cards.p.Propaganda.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Ratonhnhake:ton", 150, Rarity.RARE, mage.cards.r.Ratonhnhaketon.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Ratonhnhake:ton", 244, Rarity.RARE, mage.cards.r.Ratonhnhaketon.class, NON_FULL_USE_VARIOUS));
//cards.add(new SetCardInfo("Ratonhnhake:ton", 62, Rarity.RARE, mage.cards.r.Ratonhnhakton.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Ratonhnhaketon", 150, Rarity.RARE, mage.cards.r.Ratonhnhaketon.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Ratonhnhaketon", 244, Rarity.RARE, mage.cards.r.Ratonhnhaketon.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Ratonhnhaketon", 62, Rarity.RARE, mage.cards.r.Ratonhnhaketon.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Raven Clan War-Axe", 297, Rarity.RARE, mage.cards.r.RavenClanWarAxe.class));
cards.add(new SetCardInfo("Reconnaissance", 179, Rarity.UNCOMMON, mage.cards.r.Reconnaissance.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Reconnaissance", 82, Rarity.UNCOMMON, mage.cards.r.Reconnaissance.class, NON_FULL_USE_VARIOUS));

View file

@ -0,0 +1,34 @@
package mage.abilities.condition.common;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.abilities.hint.ConditionHint;
import mage.abilities.hint.Hint;
import mage.game.Game;
import mage.watchers.common.DealtDamageThisGameWatcher;
/**
* requires DealtDamageThisGameWatcher
*
* @author TheElk801
*/
public enum SourceHasntDealtDamageThisGameCondition implements Condition {
instance;
private static final Hint hint = new ConditionHint(
instance, "This creature hasn't dealt damage yet this game"
);
public static Hint getHint() {
return hint;
}
@Override
public boolean apply(Game game, Ability source) {
return DealtDamageThisGameWatcher.checkCreature(game, source);
}
@Override
public String toString() {
return "{this} hasn't dealt damage yet";
}
}

View file

@ -0,0 +1,52 @@
package mage.watchers.common;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.watchers.Watcher;
import java.util.HashSet;
import java.util.Set;
/**
* @author TheElk801
*/
public class DealtDamageThisGameWatcher extends Watcher {
private final Set<MageObjectReference> damagers = new HashSet<>();
public DealtDamageThisGameWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
switch (event.getType()) {
case BEGINNING_PHASE_PRE:
// keep the stored values from getting too big, especially since it doesn't reset between games
damagers.removeIf(mor -> !mor.zoneCounterIsCurrent(game));
return;
case DAMAGED_PERMANENT:
case DAMAGED_PLAYER:
break;
default:
return;
}
Permanent permanent = game.getPermanent(event.getSourceId());
if (permanent != null) {
damagers.add(new MageObjectReference(permanent, game));
}
}
public static boolean checkCreature(Game game, Ability source) {
return game
.getState()
.getWatcher(DealtDamageThisGameWatcher.class)
.damagers
.stream()
.noneMatch(mor -> mor.refersTo(source.getSourcePermanentOrLKI(game), game));
}
}