This commit is contained in:
grimreap124 2025-12-07 13:42:59 -06:00 committed by GitHub
commit 205e202cb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 174 additions and 1 deletions

View file

@ -0,0 +1,97 @@
package mage.cards.h;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.ActivateAsSorceryActivatedAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.*;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.CountersSourceCount;
import mage.abilities.dynamicvalue.common.GetXValue;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.mana.WhiteManaAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.common.FilterNonlandCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetCardInGraveyard;
import mage.util.CardUtil;
/**
*
* @author grimreap124
*/
public final class HourglassOfTheLost extends CardImpl {
public HourglassOfTheLost(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}");
// {T}: Add {W}. Put a time counter on Hourglass of the Lost.
Ability ability = new WhiteManaAbility();
ability.addEffect(new AddCountersSourceEffect(CounterType.TIME.createInstance()));
this.addAbility(ability);
// {T}, Remove X time counters from Hourglass of the Lost and exile it: Return each nonland permanent card with mana value X from your graveyard to the battlefield. Activate only as a sorcery.
Ability returnAbility = new ActivateAsSorceryActivatedAbility(new HourglassOfTheLostEffect(), null);
returnAbility.addCost(new TapSourceCost());
returnAbility.addCost(new RemoveVariableCountersSourceCost(CounterType.TIME));
returnAbility.addCost(new ExileSourceCost().setText("and exile it"));
this.addAbility(returnAbility);
}
private HourglassOfTheLost(final HourglassOfTheLost card) {
super(card);
}
@Override
public HourglassOfTheLost copy() {
return new HourglassOfTheLost(this);
}
}
class HourglassOfTheLostEffect extends OneShotEffect {
HourglassOfTheLostEffect() {
super(Outcome.Benefit);
this.staticText = "Return each nonland permanent card with mana value X from your graveyard to the battlefield.";
}
private HourglassOfTheLostEffect(final HourglassOfTheLostEffect effect) {
super(effect);
}
@Override
public HourglassOfTheLostEffect copy() {
return new HourglassOfTheLostEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
FilterCard filter = new FilterNonlandCard();
int xValue = CardUtil.getSourceCostsTag(game, source, "X", 0);
game.informPlayers(xValue + " Time counters removed");
filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue));
return controller.moveCards(controller.getGraveyard().getCards(filter, game), Zone.BATTLEFIELD, source, game);
}
game.informPlayers("controller is null");
return false;
}
}

View file

@ -157,6 +157,7 @@ public final class ModernHorizons3Commander extends ExpansionSet {
cards.add(new SetCardInfo("Hideous Taskmaster", 57, Rarity.RARE, mage.cards.h.HideousTaskmaster.class));
cards.add(new SetCardInfo("Horizon of Progress", 78, Rarity.RARE, mage.cards.h.HorizonOfProgress.class));
cards.add(new SetCardInfo("Hour of Promise", 232, Rarity.RARE, mage.cards.h.HourOfPromise.class));
cards.add(new SetCardInfo("Hourglass of the Lost", 40, Rarity.RARE, mage.cards.h.HourglassOfTheLost.class));
cards.add(new SetCardInfo("Hydra Broodmaster", 233, Rarity.RARE, mage.cards.h.HydraBroodmaster.class));
cards.add(new SetCardInfo("Hydroid Krasis", 266, Rarity.RARE, mage.cards.h.HydroidKrasis.class));
cards.add(new SetCardInfo("Idol of Oblivion", 297, Rarity.UNCOMMON, mage.cards.i.IdolOfOblivion.class));

View file

@ -0,0 +1,45 @@
package org.mage.test.cards.single.m3c;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class HourglassOfTheLostTest extends CardTestPlayerBase {
/**
* {@link mage.cards.h.HourglassOfTheLost HourglassOfTheLost} {2}{U}
*/
private static final String hourglass = "Hourglass of the Lost";
@Test
public void test_Simple() {
setStrictChooseMode(true);
skipInitShuffling();
addCard(Zone.BATTLEFIELD, playerA, hourglass);
addCard(Zone.GRAVEYARD, playerA, "Birds of Paradise"); // Mana value 1
addCard(Zone.GRAVEYARD, playerA, "+2 Mace"); // Mana value 2
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}");
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertCounterCount(playerA, hourglass, CounterType.TIME,1);
assertExileCount(playerA, 0);
assertGraveyardCount(playerA, 2);
setChoiceAmount(playerA, 1); // Remove 1 counter
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Remove X"); // Return all with mana value 1
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertExileCount(playerA, 1);
assertGraveyardCount(playerA, 1); // +2 Mace still in graveyard
assertPermanentCount(playerA, "Birds of Paradise", 1);
}
}

View file

@ -644,7 +644,12 @@ public abstract class AbilityImpl implements Ability {
if (!(variableCost instanceof VariableManaCost) && !((Cost) variableCost).isPaid()) {
int xValue = variableCost.announceXValue(this, game);
Cost fixedCost = variableCost.getFixedCostsFromAnnouncedValue(xValue);
addCost(fixedCost);
int index = getCosts().indexOf(variableCost);
if (index == -1) {
addCost(fixedCost);
} else {
addCost(fixedCost, index + 1);
}
// set the xcosts to paid
variableCost.setAmount(xValue, xValue, false);
((Cost) variableCost).setPaid();
@ -1076,6 +1081,31 @@ public abstract class AbilityImpl implements Ability {
}
}
public void addCost(Cost cost, int index) {
if (cost == null) {
return;
}
if (cost instanceof Costs) {
// as list of costs
Costs<Cost> list = (Costs<Cost>) cost;
for (Cost single : list) {
addCost(single, index);
}
} else {
// as single cost
if (cost instanceof ManaCost) {
manaCosts.add((ManaCost) cost);
manaCostsToPay.add((ManaCost) cost);
} else {
if (index > costs.size()) {
costs.add(cost);
} else {
costs.add(index, cost);
}
}
}
}
@Override
public void addManaCostsToPay(ManaCost manaCost) {
if (manaCost == null) {