mirror of
https://github.com/correl/mage.git
synced 2024-11-25 03:00:11 +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";
|
this.blockName = "Command Zone";
|
||||||
|
|
||||||
cards.add(new SetCardInfo("Ainok Survivalist", 156, Rarity.COMMON, mage.cards.a.AinokSurvivalist.class));
|
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("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("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));
|
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);
|
void setGameStopped(boolean gameStopped);
|
||||||
|
|
||||||
boolean isGameStopped();
|
boolean isGameStopped();
|
||||||
|
|
||||||
|
boolean isTurnOrderReversed();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3770,6 +3770,11 @@ public abstract class GameImpl implements Game {
|
||||||
return gameStopped;
|
return gameStopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTurnOrderReversed() {
|
||||||
|
return state.getReverseTurnOrder();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
Player activePayer = this.getPlayer(this.getActivePlayerId());
|
Player activePayer = this.getPlayer(this.getActivePlayerId());
|
||||||
|
|
|
@ -108,6 +108,7 @@ public class GameState implements Serializable, Copyable<GameState> {
|
||||||
private boolean manaBurn = false;
|
private boolean manaBurn = false;
|
||||||
private boolean hasDayNight = false;
|
private boolean hasDayNight = false;
|
||||||
private boolean isDaytime = true;
|
private boolean isDaytime = true;
|
||||||
|
private boolean reverseTurnOrder = false;
|
||||||
|
|
||||||
private int applyEffectsCounter; // Upcounting number of each applyEffects execution
|
private int applyEffectsCounter; // Upcounting number of each applyEffects execution
|
||||||
|
|
||||||
|
@ -1449,4 +1450,16 @@ public class GameState implements Serializable, Copyable<GameState> {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return CardUtil.getTurnInfo(this);
|
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;
|
Player player;
|
||||||
while (true) {
|
while (true) {
|
||||||
player = game.getPlayer(super.getNext());
|
if(game.isTurnOrderReversed()){
|
||||||
|
player = game.getPlayer(super.getPrevious());
|
||||||
|
} else{
|
||||||
|
player = game.getPlayer(super.getNext());
|
||||||
|
}
|
||||||
if (player.isInGame()) {
|
if (player.isInGame()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue