handleLeylinesAndChancellors - scripted all actions performed from opening hand

now you may execute actions from opening hand in any order (rule 103.5)
This commit is contained in:
Maxmtg
2013-06-02 22:21:46 +00:00
parent 3fa617b3eb
commit 5b44514fd4
24 changed files with 171 additions and 114 deletions

View File

@@ -733,9 +733,6 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
break;
}
// card has to be on battlefield or in own hand
boolean canUseInputToSelectCard = origin.size() == 1 && ( origin.get(0) == ZoneType.Battlefield || origin.get(0) == ZoneType.Hand && player == decider);
Card c;
if (sa.hasParam("AtRandom")) {
c = Aggregates.random(fetchList);
@@ -743,6 +740,8 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
c = fetchList.get(0);
} else {
boolean mustChoose = sa.hasParam("Mandatory");
// card has to be on battlefield or in own hand
boolean canUseInputToSelectCard = origin.size() == 1 && ( origin.get(0) == ZoneType.Battlefield || origin.get(0) == ZoneType.Hand && player == decider);
if( canUseInputToSelectCard ) {
InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, fetchList);
inp.setCancelAllowed(!mustChoose);

View File

@@ -46,6 +46,8 @@ public class RevealEffect extends SpellAbilityEffect {
revealed.add(Aggregates.random(cardsInHand));
}
} else if (sa.hasParam("Defined")) {
revealed.addAll(AbilityUtils.getDefinedCards(sa.getSourceCard(), sa.getParam("Defined"), sa));
} else {
List<Card> valid = new ArrayList<Card>(cardsInHand);

View File

@@ -79,6 +79,7 @@ public class HumanPlaySpellAbility {
if (isFree || this.payment.isFullyPaid()) {
if (skipStack) {
game.getStack().unfreezeStack();
AbilityUtils.resolve(this.ability);
} else {
this.enusureAbilityHasDescription(this.ability);

View File

@@ -40,7 +40,6 @@ import forge.CounterType;
import forge.FThreads;
import forge.GameEntity;
import forge.GameLogEntryType;
import forge.Singletons;
import forge.card.CardType;
import forge.card.TriggerReplacementBase;
import forge.card.ability.AbilityFactory;
@@ -59,7 +58,6 @@ import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerType;
import forge.card.trigger.ZCTrigger;
import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCard;
import forge.game.event.GameEventCardDestroyed;
import forge.game.event.GameEventCardRegenerated;
import forge.game.event.GameEventCardSacrificed;
@@ -75,8 +73,6 @@ import forge.game.zone.PlayerZoneBattlefield;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.gui.GuiDialog;
import forge.gui.input.InputSelectCardsFromList;
import forge.util.Aggregates;
import forge.util.maps.CollectionSuppliers;
import forge.util.maps.HashMapOfLists;
@@ -1420,103 +1416,6 @@ public class GameAction {
}
}
private void handleLeylinesAndChancellors(final Player first) {
for (Player p : game.getPlayers()) {
final List<Card> openingHand = new ArrayList<Card>(p.getCardsIn(ZoneType.Hand));
for (final Card c : openingHand) {
// check c.isInZone(ZoneType.Hand) because Gemstone Caverns would exile a card
if (!c.isInZone(ZoneType.Hand)) {
continue;
}
if (p.isHuman()) {
for (String kw : c.getKeyword()) {
if (kw.startsWith("MayEffectFromOpeningHand")) {
final String effName = kw.split(":")[1];
final SpellAbility effect = AbilityFactory.getAbility(c.getSVar(effName), c);
effect.setActivatingPlayer(p);
if (GuiDialog.confirm(c, "Use " + c +"'s ability?")) {
// If we ever let the AI memorize cards in the players
// hand, this would be a place to do so.
HumanPlay.playSpellAbilityNoStack(p, effect);
}
}
}
if (c.getName().startsWith("Leyline of")) {
if (GuiDialog.confirm(c, "Use " + c + "'s ability?")) {
game.getAction().moveToPlay(c);
}
}
if (c.getName().equals("Gemstone Caverns") && !p.equals(first)
&& GuiDialog.confirm(c, "Use " + c + "'s ability?")) {
c.addCounter(CounterType.LUCK, 1, true);
game.getAction().moveToPlay(c);
List<Card> remainingHand = new ArrayList<Card>(p.getCardsIn(ZoneType.Hand));
InputSelectCardsFromList inp = new InputSelectCardsFromList(1, 1, remainingHand);
inp.setCancelAllowed(false);
inp.setMessage("Choose a card in you hand to exile");
Singletons.getControl().getInputQueue().setInputAndWait(inp);
Card exiled = inp.getSelected().get(0);
game.getAction().exile(exiled);
}
} else { // Computer Leylines & Chancellors
if (!c.getName().startsWith("Leyline of") && !c.getName().equals("Gemstone Caverns")) {
for (String kw : c.getKeyword()) {
if (kw.startsWith("MayEffectFromOpeningHand")) {
final String effName = kw.split(":")[1];
final SpellAbility effect = AbilityFactory.getAbility(c.getSVar(effName), c);
effect.setActivatingPlayer(p);
// Is there a better way for the AI to decide this?
if (effect.doTrigger(false, p)) {
GuiDialog.message("Computer reveals " + c.getName() + "(" + c.getUniqueNumber() + ").");
ComputerUtil.playNoStack(p, effect, game);
}
}
}
}
if (c.getName().startsWith("Leyline of")
&& !(c.getName().startsWith("Leyline of Singularity")
&& (Iterables.any(game.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Leyline of Singularity"))))) {
game.getAction().moveToPlay(c);
//ga.checkStateEffects();
}
if (c.getName().equals("Gemstone Caverns") && !p.equals(first)
&& !Iterables.any(game.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Gemstone Caverns"))) {
c.addCounter(CounterType.LUCK, 1, true);
game.getAction().moveToPlay(c);
List<Card> remainingHand = new ArrayList<Card>(p.getCardsIn(ZoneType.Hand));
Card exiled = ComputerUtilCard.getWorstAI(remainingHand);
game.getAction().exile(exiled);
}
}
}
}
}
private Player determineFirstTurnPlayer(final GameOutcome lastGameOutcome) {
// Only cut/coin toss if it's the first game of the match
Player goesFirst = null;
boolean isFirstGame = lastGameOutcome == null;
if (isFirstGame) {
game.fireEvent(new GameEventFlipCoin()); // Play the Flip Coin sound
goesFirst = Aggregates.random(game.getPlayers());
} else {
for(Player p : game.getPlayers()) {
if(!lastGameOutcome.isWinner(p.getLobbyPlayer())) {
goesFirst = p;
break;
}
}
}
boolean willPlay = goesFirst.getController().getWillPlayOnFirstTurn(isFirstGame);
goesFirst = willPlay ? goesFirst : goesFirst.getOpponent();
return goesFirst;
}
public void startGame() {
Player first = determineFirstTurnPlayer(game.getMatch().getLastGameOutcome());
@@ -1540,7 +1439,7 @@ public class GameAction {
if(game.getType() == GameType.Planechase)
first.initPlane();
handleLeylinesAndChancellors(first);
runOpeningHandActions(first);
checkStateEffects();
// Run Trigger beginning of the game
@@ -1557,6 +1456,28 @@ public class GameAction {
game.fireEvent(new GameEventGameFinished());
}
private Player determineFirstTurnPlayer(final GameOutcome lastGameOutcome) {
// Only cut/coin toss if it's the first game of the match
Player goesFirst = null;
boolean isFirstGame = lastGameOutcome == null;
if (isFirstGame) {
game.fireEvent(new GameEventFlipCoin()); // Play the Flip Coin sound
goesFirst = Aggregates.random(game.getPlayers());
} else {
for(Player p : game.getPlayers()) {
if(!lastGameOutcome.isWinner(p.getLobbyPlayer())) {
goesFirst = p;
break;
}
}
}
boolean willPlay = goesFirst.getController().getWillPlayOnFirstTurn(isFirstGame);
goesFirst = willPlay ? goesFirst : goesFirst.getOpponent();
return goesFirst;
}
private void performMulligans(final Player firstPlayer, final boolean isCommander) {
List<Player> whoCanMulligan = Lists.newArrayList(game.getPlayers());
int offset = whoCanMulligan.indexOf(firstPlayer);
@@ -1630,6 +1551,45 @@ public class GameAction {
}
}
private void runOpeningHandActions(final Player first) {
Player takesAction = first;
do {
List<SpellAbility> usableFromOpeningHand = new ArrayList<SpellAbility>();
// Select what can be activated from a given hand
for (final Card c : takesAction.getCardsIn(ZoneType.Hand)) {
for (String kw : c.getKeyword()) {
if (kw.startsWith("MayEffectFromOpeningHand")) {
String[] split = kw.split(":");
final String effName = split[1];
if ( split.length > 2 && split[2].equalsIgnoreCase("!PlayFirst") && first == takesAction)
continue;
final SpellAbility effect = AbilityFactory.getAbility(c.getSVar(effName), c);
effect.setActivatingPlayer(takesAction);
usableFromOpeningHand.add(effect);
}
}
}
// Players are supposed to return the effects in an order they want those to be resolved (Rule 103.5)
usableFromOpeningHand = takesAction.getController().chooseSaToActivateFromOpeningHand(usableFromOpeningHand);
for(final SpellAbility sa : usableFromOpeningHand ) {
if (!takesAction.getZone(ZoneType.Hand).contains(sa.getSourceCard()))
continue;
if (takesAction.isHuman())
HumanPlay.playSpellAbilityNoStack(takesAction, sa);
else
ComputerUtil.playNoStack(takesAction, sa, game);
}
takesAction = game.getNextPlayerAfter(takesAction);
} while( takesAction != first );
// state effects are checked only when someone gets priority
}
// Invokes given runnable in Game thread pool - used to start game and perform actions from UI (when game-0 waits for input)
public void invoke(final Runnable proc) {
if( FThreads.isGameThread() ) {

View File

@@ -904,5 +904,45 @@ public class AiController {
}
return toExile;
}
public List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand) {
// AI would play everything. But limits to one copy of (Leyline of Singularity) and (Gemstone Caverns)
List<SpellAbility> result = new ArrayList<SpellAbility>();
for(SpellAbility sa : usableFromOpeningHand) {
// Is there a better way for the AI to decide this?
if (sa.doTrigger(false, player)) {
result.add(sa);
}
}
boolean hasLeyline1 = false;
SpellAbility saGemstones = null;
for(int i = 0; i < result.size(); i++) {
SpellAbility sa = result.get(i);
String srcName = sa.getSourceCard().getName();
if("Gemstone Caverns".equals(srcName)) {
if(saGemstones == null)
saGemstones = sa;
else
result.remove(i--);
} else if ("Leyline of Singularity".equals(srcName)) {
if(!hasLeyline1)
hasLeyline1 = true;
else
result.remove(i--);
}
}
// Play them last
if( saGemstones != null ) {
result.remove(saGemstones);
result.add(saGemstones);
}
return result;
}
}

View File

@@ -49,9 +49,7 @@ import forge.game.Game;
import forge.game.GlobalRuleChange;
import forge.game.player.Player;
import forge.game.player.PlayerController.ManaPaymentPurpose;
import forge.game.zone.PlayerZone;
import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.gui.GuiDialog;
import forge.gui.framework.EDocID;
import forge.gui.framework.SDisplayUtil;

View File

@@ -7,5 +7,6 @@ package forge.game.player;
public enum PlayerActionConfirmMode {
Random,
BraidOfFire,
FromOpeningHand;
}

View File

@@ -129,6 +129,7 @@ public abstract class PlayerController {
public abstract List<Card> chooseCardsToDelve(int colorLessAmount, List<Card> grave);
public abstract List<Card> chooseCardsToRevealFromHand(int min, int max, List<Card> valid);
public abstract List<Card> chooseCardsToDiscardUnlessType(int min, List<Card> hand, String param, SpellAbility sa);
public abstract List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand);
public abstract Mana chooseManaFromPool(List<Mana> manaChoices);
public abstract String chooseSomeType(String kindOfType, String aiLogic, List<String> validTypes, List<String> invalidTypes);

View File

@@ -325,4 +325,10 @@ public class PlayerControllerAi extends PlayerController {
}
return false;
}
@Override
public List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand) {
// AI would play everything. But limits to one copy of (Leyline of Singularity) and (Gemstone Caverns)
return brains.chooseSaToActivateFromOpeningHand(usableFromOpeningHand);
}
}

View File

@@ -539,4 +539,27 @@ public class PlayerControllerHuman extends PlayerController {
public boolean payManaOptional(Card c, Cost attackCost, String prompt, ManaPaymentPurpose purpose) {
return HumanPlay.payCostDuringAbilityResolve(player, c, attackCost, null);
}
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseSaToActivateFromOpeningHand(java.util.List)
*/
@Override
public List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand) {
List<Card> srcCards = new ArrayList<Card>();
for(SpellAbility sa : usableFromOpeningHand) {
srcCards.add(sa.getSourceCard());
}
List<Card> chosen = GuiChoose.order("Choose cards to activate from opening hand", "Activate first", -1, srcCards, null, null);
List<SpellAbility> result = new ArrayList<SpellAbility>();
for(Card c : chosen) {
for(SpellAbility sa : usableFromOpeningHand) {
if ( sa.getSourceCard() == c ) {
result.add(sa);
break;
}
}
}
return result;
}
}