mirror of
https://github.com/correl/mage.git
synced 2024-11-28 19:19:55 +00:00
Implement Aeon Engine (#8804)
Co-authored-by: teskogi <tojile7269@yeafam.com>
This commit is contained in:
parent
015cdf3136
commit
7ec94425df
7 changed files with 238 additions and 3 deletions
65
Mage.Sets/src/mage/cards/a/AeonEngine.java
Normal file
65
Mage.Sets/src/mage/cards/a/AeonEngine.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTappedAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.ExileSourceCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.GameState;
|
||||
import mage.players.PlayerList;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author azra1l <algee2005@gmail.com>
|
||||
*/
|
||||
public final class AeonEngine extends CardImpl {
|
||||
public AeonEngine(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}");
|
||||
|
||||
// Aeon Engine enters the battlefield tapped.
|
||||
this.addAbility(new EntersBattlefieldTappedAbility());
|
||||
|
||||
// {T}, Exile Aeon Engine: Reverse the gameâs turn order. (For example, if play had proceeded clockwise around the table, it now goes counterclockwise.)
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AeonEngineEffect(), new TapSourceCost());
|
||||
ability.addCost(new ExileSourceCost());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
public AeonEngine(final AeonEngine card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AeonEngine copy() {
|
||||
return new AeonEngine(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AeonEngineEffect extends OneShotEffect {
|
||||
public AeonEngineEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "Reverse the game turn order.";
|
||||
}
|
||||
|
||||
public AeonEngineEffect(final AeonEngineEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
public AeonEngineEffect copy() {
|
||||
return new AeonEngineEffect(this);
|
||||
}
|
||||
|
||||
public boolean apply(Game game, Ability source) {
|
||||
game.getState().setReverseTurnOrder(true);
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ public final class Commander2019Edition extends ExpansionSet {
|
|||
this.blockName = "Command Zone";
|
||||
|
||||
cards.add(new SetCardInfo("Ainok Survivalist", 156, Rarity.COMMON, mage.cards.a.AinokSurvivalist.class));
|
||||
cards.add(new SetCardInfo("Aeon Engine", 52, Rarity.RARE, mage.cards.a.AeonEngine.class));
|
||||
cards.add(new SetCardInfo("Akoum Refuge", 226, Rarity.UNCOMMON, mage.cards.a.AkoumRefuge.class));
|
||||
cards.add(new SetCardInfo("Alchemist's Greeting", 133, Rarity.COMMON, mage.cards.a.AlchemistsGreeting.class));
|
||||
cards.add(new SetCardInfo("Angel of Sanctions", 61, Rarity.MYTHIC, mage.cards.a.AngelOfSanctions.class));
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
package org.mage.test.cards.single.c19;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import mage.constants.MultiplayerAttackOption;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.FreeForAll;
|
||||
import mage.game.Game;
|
||||
import mage.game.GameException;
|
||||
import mage.game.mulligan.MulliganType;
|
||||
import mage.players.Player;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author azra1l <algee2005@gmail.com>
|
||||
*/
|
||||
public class AeonEngineTest extends CardTestMultiPlayerBase {
|
||||
@Override
|
||||
protected Game createNewGameAndPlayers() throws GameException {
|
||||
Game game = new FreeForAll(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 20);
|
||||
// Player order: A -> D -> C -> B
|
||||
playerA = createPlayer(game, playerA, "PlayerA");
|
||||
playerB = createPlayer(game, playerB, "PlayerB");
|
||||
playerC = createPlayer(game, playerC, "PlayerC");
|
||||
playerD = createPlayer(game, playerD, "PlayerD");
|
||||
return game;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnterTappedNormalTurnOrder() {
|
||||
// Aeon Engine - Artefact - {5}
|
||||
// Aeon Engine enters the battlefield tapped.
|
||||
// {T}, Exile Aeon Engine: Reverse the gameâs turn order. (For example, if play had proceeded clockwise around the table, it now goes counterclockwise.)
|
||||
addCard(Zone.HAND, playerA, "Aeon Engine", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
|
||||
addCard(Zone.HAND, playerD, "Agonizing Syphon", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerD, "Swamp", 4);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aeon Engine");
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Agonizing Syphon", playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
//check if aeon engine is tapped
|
||||
assertTapped("Aeon Engine", true);
|
||||
|
||||
//check if turn was passed to correct player - should be D
|
||||
assertActivePlayer(playerD);
|
||||
assertLife(playerA, 17);
|
||||
assertLife(playerD, 23);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExileCostReversedTurnOrder() throws GameException, FileNotFoundException {
|
||||
// Aeon Engine - Artefact - {5}
|
||||
// Aeon Engine enters the battlefield tapped.
|
||||
// {T}, Exile Aeon Engine: Reverse the gameâs turn order. (For example, if play had proceeded clockwise around the table, it now goes counterclockwise.)
|
||||
addCard(Zone.HAND, playerB, "Agonizing Syphon", 3);
|
||||
addCard(Zone.HAND, playerA, "Aeon Engine", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
|
||||
addCard(Zone.HAND, playerB, "Agonizing Syphon", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 5);
|
||||
addCard(Zone.HAND, playerC, "Agonizing Syphon", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerC, "Swamp", 5);
|
||||
addCard(Zone.HAND, playerD, "Agonizing Syphon", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerD, "Swamp", 5);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aeon Engine");
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Agonizing Syphon", playerA);
|
||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Agonizing Syphon", playerA);
|
||||
castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Agonizing Syphon", playerA);
|
||||
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Exile {this}:");
|
||||
castSpell(6, PhaseStep.PRECOMBAT_MAIN, playerB, "Agonizing Syphon", playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(6, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
//check if aeon engine has been exiled
|
||||
assertExileCount(playerA, "Aeon Engine", 1);
|
||||
|
||||
//check if turn was passed to correct player each turn - should be B
|
||||
assertActivePlayer(playerB);
|
||||
assertLife(playerA, 8);
|
||||
assertLife(playerB, 26);
|
||||
assertLife(playerC, 23);
|
||||
assertLife(playerD, 23);
|
||||
assertGraveyardCount(playerB, "Agonizing Syphon", 2);
|
||||
assertGraveyardCount(playerC, "Agonizing Syphon", 1);
|
||||
assertGraveyardCount(playerD, "Agonizing Syphon", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExileCostReversedTurnOrderDouble() throws GameException, FileNotFoundException {
|
||||
// Aeon Engine - Artefact - {5}
|
||||
// Aeon Engine enters the battlefield tapped.
|
||||
// {T}, Exile Aeon Engine: Reverse the gameâs turn order. (For example, if play had proceeded clockwise around the table, it now goes counterclockwise.)
|
||||
addCard(Zone.HAND, playerA, "Agonizing Syphon", 3);
|
||||
addCard(Zone.HAND, playerA, "Aeon Engine", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
|
||||
addCard(Zone.HAND, playerB, "Agonizing Syphon", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 5);
|
||||
addCard(Zone.HAND, playerC, "Agonizing Syphon", 3);
|
||||
addCard(Zone.BATTLEFIELD, playerC, "Swamp", 5);
|
||||
addCard(Zone.HAND, playerD, "Agonizing Syphon", 3);
|
||||
addCard(Zone.HAND, playerD, "Aeon Engine", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerD, "Swamp", 5);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aeon Engine");
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Aeon Engine");
|
||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Agonizing Syphon", playerA);
|
||||
castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Agonizing Syphon", playerA);
|
||||
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Exile {this}:");
|
||||
castSpell(6, PhaseStep.PRECOMBAT_MAIN, playerB, "Agonizing Syphon", playerA);
|
||||
castSpell(7, PhaseStep.PRECOMBAT_MAIN, playerC, "Agonizing Syphon", playerA);
|
||||
activateAbility(8, PhaseStep.PRECOMBAT_MAIN, playerD, "{T}, Exile {this}:");
|
||||
castSpell(9, PhaseStep.PRECOMBAT_MAIN, playerC, "Agonizing Syphon", playerA);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(9, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
//check if aeon engine's have been exiled
|
||||
assertExileCount(playerA, "Aeon Engine", 1);
|
||||
assertExileCount(playerD, "Aeon Engine", 1);
|
||||
|
||||
//check if turn was passed to correct player each turn - should be C
|
||||
assertActivePlayer(playerC);
|
||||
assertLife(playerA, 5);
|
||||
assertLife(playerB, 26);
|
||||
assertLife(playerC, 29);
|
||||
assertLife(playerD, 20);
|
||||
assertGraveyardCount(playerB, "Agonizing Syphon", 2);
|
||||
assertGraveyardCount(playerC, "Agonizing Syphon", 3);
|
||||
}
|
||||
}
|
|
@ -647,4 +647,6 @@ public interface Game extends MageItem, Serializable, Copyable<Game> {
|
|||
void setGameStopped(boolean gameStopped);
|
||||
|
||||
boolean isGameStopped();
|
||||
|
||||
boolean isTurnOrderReversed();
|
||||
}
|
||||
|
|
|
@ -3769,6 +3769,11 @@ public abstract class GameImpl implements Game {
|
|||
public boolean isGameStopped() {
|
||||
return gameStopped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTurnOrderReversed() {
|
||||
return state.getReverseTurnOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
|
@ -108,6 +108,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
private boolean manaBurn = false;
|
||||
private boolean hasDayNight = false;
|
||||
private boolean isDaytime = true;
|
||||
private boolean reverseTurnOrder = false;
|
||||
|
||||
private int applyEffectsCounter; // Upcounting number of each applyEffects execution
|
||||
|
||||
|
@ -1444,9 +1445,21 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
boolean isDaytime() {
|
||||
return isDaytime;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return CardUtil.getTurnInfo(this);
|
||||
}
|
||||
|
||||
public boolean setReverseTurnOrder(boolean reverse){
|
||||
if(this.reverseTurnOrder&&reverse){
|
||||
this.reverseTurnOrder = false;
|
||||
} else {
|
||||
this.reverseTurnOrder = reverse;
|
||||
}
|
||||
return this.reverseTurnOrder;
|
||||
}
|
||||
public boolean getReverseTurnOrder(){
|
||||
return this.reverseTurnOrder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,11 @@ public class PlayerList extends CircularList<UUID> {
|
|||
}
|
||||
Player player;
|
||||
while (true) {
|
||||
player = game.getPlayer(super.getNext());
|
||||
if(game.isTurnOrderReversed()){
|
||||
player = game.getPlayer(super.getPrevious());
|
||||
} else{
|
||||
player = game.getPlayer(super.getNext());
|
||||
}
|
||||
if (player.isInGame()) {
|
||||
break;
|
||||
}
|
||||
|
@ -89,4 +93,4 @@ public class PlayerList extends CircularList<UUID> {
|
|||
return new PlayerList(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue