[LCC] Implement Topography Tracker (#11504)

* Refactor replacement effects on ExploreEvent by using a queue of events
This commit is contained in:
Grath 2023-12-04 00:46:21 -05:00 committed by GitHub
parent 15fcc22bb3
commit 5c83818cba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 289 additions and 37 deletions

View file

@ -10,6 +10,7 @@ import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.ExploreEvent;
import mage.game.events.ExploredEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
@ -70,41 +71,57 @@ public class ExploreSourceEffect extends OneShotEffect {
return false;
}
for (int i = 0; i < amount; ++i) {
GameEvent event = new GameEvent(GameEvent.EventType.EXPLORE, permanentId, source, permanent.getControllerId());
if (game.replaceEvent(event)) {
continue;
// Because of Topography Tracker and Twists and Turns, we need to be able to have a mix of Explores and Scry 1s
// The ExploreEvent generates with a queue of EventType.EXPLORE equal to amount, the replacement effects
// can then replace each EXPLORE with either two EXPLOREs or a SCRY and an EXPLORE.
ExploreEvent event = new ExploreEvent(permanent, source, amount);
if (game.replaceEvent(event)) {
return false;
}
for (GameEvent.EventType eventType : event.getEventQueue()) {
switch (eventType) {
case SCRY:
controller.scry(1, source, game);
break;
case EXPLORE:
doOneExplore(game, permanent, source, controller);
break;
default:
throw new IllegalArgumentException("Wrong code usage: unrecognized event type in explore event");
}
// 701.40a Certain abilities instruct a permanent to explore. To do so, that permanents controller reveals
// the top card of their library. If a land card is revealed this way, that player puts that card into their
// hand. Otherwise, that player puts a +1/+1 counter on the exploring permanent and may put the revealed
// card into their graveyard.
Card card = controller.getLibrary().getFromTop(game);
if (card != null) {
controller.revealCards("Explored card", new CardsImpl(card), game);
if (card.isLand(game)) {
controller.moveCards(card, Zone.HAND, source, game);
} else {
addCounter(game, permanent, source);
if (controller.chooseUse(Outcome.Neutral, "Put " + card.getLogName() + " in your graveyard?", source, game)) {
controller.moveCards(card, Zone.GRAVEYARD, source, game);
} else {
game.informPlayers(controller.getLogName() + " leaves " + card.getLogName() + " on top of their library.");
}
}
} else {
// If no card is revealed, most likely because that player's library is empty,
// the exploring creature receives a +1/+1 counter.
addCounter(game, permanent, source);
}
game.getState().processAction(game);
// 701.40b A permanent explores after the process described in rule 701.40a is complete, even if some or all of
// those actions were impossible.
game.fireEvent(new ExploredEvent(permanent, source, card));
}
return true;
}
private static void doOneExplore(Game game, Permanent permanent, Ability source, Player controller) {
// 701.40a Certain abilities instruct a permanent to explore. To do so, that permanents controller reveals
// the top card of their library. If a land card is revealed this way, that player puts that card into their
// hand. Otherwise, that player puts a +1/+1 counter on the exploring permanent and may put the revealed
// card into their graveyard.
Card card = controller.getLibrary().getFromTop(game);
if (card != null) {
controller.revealCards("Explored card", new CardsImpl(card), game);
if (card.isLand(game)) {
controller.moveCards(card, Zone.HAND, source, game);
} else {
addCounter(game, permanent, source);
if (controller.chooseUse(Outcome.Neutral, "Put " + card.getLogName() + " in your graveyard?", source, game)) {
controller.moveCards(card, Zone.GRAVEYARD, source, game);
} else {
game.informPlayers(controller.getLogName() + " leaves " + card.getLogName() + " on top of their library.");
}
}
} else {
// If no card is revealed, most likely because that player's library is empty,
// the exploring creature receives a +1/+1 counter.
addCounter(game, permanent, source);
}
game.getState().processAction(game);
// 701.40b A permanent explores after the process described in rule 701.40a is complete, even if some or all of
// those actions were impossible.
game.fireEvent(new ExploredEvent(permanent, source, card));
}
private static void addCounter(Game game, Permanent permanent, Ability source) {
if (game.getState().getZone(permanent.getId()) == Zone.BATTLEFIELD) { // needed in case LKI object is used
permanent.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game);

View file

@ -0,0 +1,51 @@
package mage.game.events;
import mage.abilities.Ability;
import mage.game.permanent.Permanent;
import java.util.ArrayDeque;
import java.util.Queue;
/**
* @author Grath
*/
public class ExploreEvent extends GameEvent {
private Queue<EventType> eventQueue = new ArrayDeque<>();
public ExploreEvent(Permanent permanent, Ability source, int amount) {
super(EventType.EXPLORE, permanent.getId(), source, permanent.getControllerId(), amount, false);
// Populate the queue with a number of EXPLOREs equal to the initial number of EXPLOREs.
for (int i = 0; i < amount; ++i) {
eventQueue.add(EventType.EXPLORE);
}
}
public void doubleExplores() {
// Process through the Queue, when we find an EXPLORE add another EXPLORE.
Queue<EventType> newQueue = new ArrayDeque<>();
for (EventType eventType : eventQueue) {
if (eventType == EventType.EXPLORE) {
newQueue.add(EventType.EXPLORE);
}
newQueue.add(eventType);
}
eventQueue = newQueue;
}
public void addScry() {
// Process through the Queue, when we find an EXPLORE add a SCRY before it.
Queue<EventType> newQueue = new ArrayDeque<>();
for (EventType eventType : eventQueue) {
if (eventType == EventType.EXPLORE) {
newQueue.add(EventType.SCRY);
}
newQueue.add(eventType);
}
eventQueue = newQueue;
}
public Queue<EventType> getEventQueue() {
return eventQueue;
}
}