GameLog: quest listener is registered in MatchClass, Mulligan logs from events

PhaseHandler - half-measures to handle close window during turn-based actions (like declare combatants)
getAllPossibleAbilites method moved from game to card
removed 5 calls to isHuman/Computer
This commit is contained in:
Maxmtg
2013-06-04 20:25:25 +00:00
parent 045a75f071
commit 03816aa47a
15 changed files with 102 additions and 124 deletions

View File

@@ -63,6 +63,7 @@ import forge.card.trigger.Trigger;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.card.trigger.ZCTrigger; import forge.card.trigger.ZCTrigger;
import forge.game.Game; import forge.game.Game;
import forge.game.GameActionUtil;
import forge.game.GlobalRuleChange; import forge.game.GlobalRuleChange;
import forge.game.event.GameEventCardDamaged; import forge.game.event.GameEventCardDamaged;
import forge.game.event.GameEventCardDamaged.DamageType; import forge.game.event.GameEventCardDamaged.DamageType;
@@ -72,6 +73,7 @@ import forge.game.event.GameEventCounterRemoved;
import forge.game.event.GameEventCardTapped; import forge.game.event.GameEventCardTapped;
import forge.game.phase.Combat; import forge.game.phase.Combat;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.item.CardDb; import forge.item.CardDb;
import forge.util.Expressions; import forge.util.Expressions;
@@ -8287,4 +8289,41 @@ public class Card extends GameEntity implements Comparable<Card> {
throw new IllegalStateException("Card " + toString() + " has no means to determine the game it belongs to!"); throw new IllegalStateException("Card " + toString() + " has no means to determine the game it belongs to!");
} }
/**
* TODO: Write javadoc for this method.
* @param card
* @param game TODO
* @param player
* @return
*/
public List<SpellAbility> getAllPossibleAbilites(Player player) {
// this can only be called by the Human
final Zone zone = player.getGame().getZoneOf(this);
final List<SpellAbility> abilities = new ArrayList<SpellAbility>();
for (SpellAbility sa : getSpellAbilities()) {
//add alternative costs as additional spell abilities
abilities.add(sa);
abilities.addAll(GameActionUtil.getAlternativeCosts(sa));
}
for (int iSa = 0; iSa < abilities.size();) {
SpellAbility sa = abilities.get(iSa);
sa.setActivatingPlayer(player);
if (!sa.canPlay())
abilities.remove(iSa);
else
iSa++;
}
if (isLand() && player.canPlayLand(this)) {
if (zone.is(ZoneType.Hand) || (!zone.is(ZoneType.Battlefield) && hasStartOfKeyword("May be played"))) {
Ability.PLAY_LAND_SURROGATE.setSourceCard(this);
abilities.add(Ability.PLAY_LAND_SURROGATE);
}
}
return abilities;
}
} // end Card class } // end Card class

View File

@@ -14,6 +14,7 @@ import forge.game.event.GameEventBlockersDeclared;
import forge.game.event.GameEventCardDamaged; import forge.game.event.GameEventCardDamaged;
import forge.game.event.GameEventCardDamaged.DamageType; import forge.game.event.GameEventCardDamaged.DamageType;
import forge.game.event.GameEventLandPlayed; import forge.game.event.GameEventLandPlayed;
import forge.game.event.GameEventMulligan;
import forge.game.event.GameEventPlayerDamaged; import forge.game.event.GameEventPlayerDamaged;
import forge.game.event.GameEventPlayerPoisoned; import forge.game.event.GameEventPlayerPoisoned;
import forge.game.event.GameEventSpellAbilityCast; import forge.game.event.GameEventSpellAbilityCast;
@@ -27,6 +28,7 @@ import forge.game.event.GameEventPlayerControl;
import forge.game.player.LobbyPlayer; import forge.game.player.LobbyPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerStatistics; import forge.game.player.PlayerStatistics;
import forge.game.zone.ZoneType;
import forge.util.Lang; import forge.util.Lang;
import forge.util.maps.MapOfLists; import forge.util.maps.MapOfLists;
@@ -232,6 +234,12 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
return new GameLogEntry(GameLogEntryType.COMBAT, sb.toString()); return new GameLogEntry(GameLogEntryType.COMBAT, sb.toString());
} }
@Override
public GameLogEntry visit(GameEventMulligan ev) {
String message = String.format( "%s has mulliganed down to %d cards.", ev.player, ev.player.getZone(ZoneType.Hand).size());
return new GameLogEntry(GameLogEntryType.MULLIGAN, message);
}
@Subscribe @Subscribe
public void recieve(GameEvent ev) { public void recieve(GameEvent ev) {

View File

@@ -38,6 +38,7 @@ import forge.game.ai.ComputerUtilMana;
import forge.game.phase.Combat; import forge.game.phase.Combat;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.Aggregates; import forge.util.Aggregates;
import forge.util.MyRandom; import forge.util.MyRandom;
@@ -1372,4 +1373,13 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
} // end changeHiddenOriginResolveAI } // end changeHiddenOriginResolveAI
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#confirmAction(forge.game.player.Player, forge.card.spellability.SpellAbility, forge.game.player.PlayerActionConfirmMode, java.lang.String)
*/
@Override
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
// AI was never asked
return true;
}
} }

View File

@@ -21,7 +21,6 @@ import forge.card.spellability.SpellAbilityStackInstance;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
import forge.game.Game; import forge.game.Game;
import forge.game.ai.ComputerUtilCard;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.Zone; import forge.game.zone.Zone;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
@@ -428,10 +427,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (tgt != null && tgtC.isInPlay() && !tgtC.canBeTargetedBy(sa)) { if (tgt != null && tgtC.isInPlay() && !tgtC.canBeTargetedBy(sa)) {
continue; continue;
} }
final String prompt = "Do you want to move " + tgtC + " from " + origin + " to " + destination + "?";
if (player.isHuman() && optional && !GuiDialog.confirm(hostCard, prompt)) { final String prompt = String.format("Do you want to move %s from %s to %s?", tgtC, origin, destination);
if (optional && false == player.getController().confirmAction(sa, null, prompt) )
continue; continue;
}
final Zone originZone = game.getZoneOf(tgtC); final Zone originZone = game.getZoneOf(tgtC);
// if Target isn't in the expected Zone, continue // if Target isn't in the expected Zone, continue
@@ -472,16 +472,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
list = CardLists.getValidCards(list, sa.getParam("AttachedTo"), tgtC.getController(), tgtC); list = CardLists.getValidCards(list, sa.getParam("AttachedTo"), tgtC.getController(), tgtC);
} }
if (!list.isEmpty()) { if (!list.isEmpty()) {
Card attachedTo = null; Card attachedTo = player.getController().chooseSingleCardForEffect(list, sa, tgtC + " - Select a card to attach to.");
if (player.isHuman()) {
if (list.size() > 1) {
attachedTo = GuiChoose.one(tgtC + " - Select a card to attach to.", list);
} else {
attachedTo = list.get(0);
}
} else { // AI player
attachedTo = ComputerUtilCard.getBestAI(list);
}
if (tgtC.isAura()) { if (tgtC.isAura()) {
if (tgtC.isEnchanting()) { if (tgtC.isEnchanting()) {
// If this Card is already Enchanting something, need // If this Card is already Enchanting something, need

View File

@@ -18,7 +18,6 @@
package forge.card.cardfactory; package forge.card.cardfactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import forge.Card; import forge.Card;
@@ -31,14 +30,10 @@ import forge.card.mana.ManaCost;
import forge.card.spellability.Spell; import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.Game; import forge.game.Game;
import forge.game.ai.AiController;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.player.PlayerControllerAi;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose; import forge.gui.GuiChoose;
import forge.gui.input.InputPayManaExecuteCommands; import forge.gui.input.InputPayManaExecuteCommands;
import forge.gui.input.InputSelectCards;
import forge.gui.input.InputSelectCardsFromList;
/** /**
* <p> * <p>
@@ -52,39 +47,24 @@ public class CardFactorySorceries {
private static final void balanceLands(Game game, Spell card) { private static final void balanceLands(Game game, Spell card) {
List<List<Card>> lands = new ArrayList<List<Card>>(); int minLands = Integer.MAX_VALUE;
for (Player p : game.getPlayers()) { for (Player p : game.getPlayers()) {
int pL = p.getLandsInPlay().size();
lands.add(p.getLandsInPlay()); if( pL < minLands )
minLands = pL;
} }
int min = Integer.MAX_VALUE;
for (List<Card> l : lands) {
int s = l.size();
min = Math.min(min, s);
}
Iterator<List<Card>> ll = lands.iterator();
for (Player p : game.getPlayers()) { for (Player p : game.getPlayers()) {
List<Card> l = ll.next(); List<Card> l = p.getLandsInPlay();
int sac = l.size() - min; int sac = l.size() - minLands;
if (sac == 0) { if (sac == 0) {
continue; continue;
} }
if (p.isComputer()) {
CardLists.shuffle(l);
for (int i = 0; i < sac; i++) {
game.getAction().sacrifice(l.get(i), card);
}
} else {
final List<Card> list = CardLists.getType(p.getCardsIn(ZoneType.Battlefield), "Land");
InputSelectCards inp = new InputSelectCardsFromList(sac, sac, list); List<Card> toSac = p.getController().choosePermanentsToSacrifice(card, sac, sac, l, "Select %d more land(s) to sacrifice");
inp.setMessage("Select %d more land(s) to sacrifice"); for( Card crd : toSac )
Singletons.getControl().getInputQueue().setInputAndWait(inp); p.getGame().getAction().sacrifice(crd, card);
for( Card crd : inp.getSelected() )
p.getGame().getAction().sacrifice(crd, card);
}
} }
} }
@@ -100,26 +80,16 @@ public class CardFactorySorceries {
if (sac == 0) { if (sac == 0) {
continue; continue;
} }
if (p.isHuman()) {
InputSelectCards sc = new InputSelectCardsFromList(sac, sac, hand); List<Card> toDiscard = p.getController().chooseCardsToDiscardFrom(p, spell, hand, sac, sac); // "Select %d more card(s) to discard"
sc.setMessage("Select %d more card(s) to discard"); for (Card c : toDiscard)
Singletons.getControl().getInputQueue().setInputAndWait(sc); p.discard(c, spell);
for( Card c : sc.getSelected())
p.discard(c, spell);
} else {
final AiController ai = ((PlayerControllerAi)p.getController()).getAi();
final List<Card> toDiscard = ai.getCardsToDiscard(sac, (String[])null, spell);
for (int i = 0; i < toDiscard.size(); i++) {
p.discard(toDiscard.get(i), spell);
}
}
} }
} }
private static final void balanceCreatures(Game game, Spell card) { private static final void balanceCreatures(Game game, Spell card) {
List<List<Card>> creats = new ArrayList<List<Card>>(); List<List<Card>> creats = new ArrayList<List<Card>>();
for (Player p : game.getPlayers()) { for (Player p : game.getPlayers()) {
creats.add(p.getCreaturesInPlay()); creats.add(p.getCreaturesInPlay());
} }
int min = Integer.MAX_VALUE; int min = Integer.MAX_VALUE;
@@ -135,22 +105,10 @@ public class CardFactorySorceries {
if (sac == 0) { if (sac == 0) {
continue; continue;
} }
if (p.isComputer()) { List<Card> toSac = p.getController().choosePermanentsToSacrifice(card, sac, sac, c, "Select %d more creature(s) to sacrifice");
CardLists.sortByPowerAsc(c);
CardLists.sortByCmcDesc(c);
Collections.reverse(c);
for (int i = 0; i < sac; i++) {
p.getGame().getAction().sacrifice(c.get(i), card);
}
} else {
final List<Card> list = CardLists.getType(p.getCardsIn(ZoneType.Battlefield), "Creature");
InputSelectCards inp = new InputSelectCardsFromList(sac, sac, list);
inp.setMessage("Select %d more creature(s) to sacrifice");
Singletons.getControl().getInputQueue().setInputAndWait(inp);
for( Card crd : inp.getSelected() )
p.getGame().getAction().sacrifice(crd, card);
} for( Card crd : toSac )
p.getGame().getAction().sacrifice(crd, card);
} }
} }

View File

@@ -10,7 +10,6 @@ import forge.Card;
import forge.FThreads; import forge.FThreads;
import forge.game.event.GameEvent; import forge.game.event.GameEvent;
import forge.game.event.GameEventAnteCardsSelected; import forge.game.event.GameEventAnteCardsSelected;
import forge.game.event.GameEventAttackersDeclared;
import forge.game.event.GameEventGameFinished; import forge.game.event.GameEventGameFinished;
import forge.game.event.GameEventGameOutcome; import forge.game.event.GameEventGameOutcome;
import forge.game.event.GameEventPlayerControl; import forge.game.event.GameEventPlayerControl;

View File

@@ -30,11 +30,8 @@ import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.ColorChanger; import forge.ColorChanger;
import forge.GameLog; import forge.GameLog;
import forge.Singletons;
import forge.StaticEffects; import forge.StaticEffects;
import forge.card.replacement.ReplacementHandler; import forge.card.replacement.ReplacementHandler;
import forge.card.spellability.Ability;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance; import forge.card.spellability.SpellAbilityStackInstance;
import forge.card.trigger.TriggerHandler; import forge.card.trigger.TriggerHandler;
import forge.card.trigger.TriggerType; import forge.card.trigger.TriggerType;
@@ -122,9 +119,6 @@ public class Game {
endOfTurn = new EndOfTurn(this); endOfTurn = new EndOfTurn(this);
endOfCombat = new EndOfCombat(this); endOfCombat = new EndOfCombat(this);
if ( match0.getGameType() == GameType.Quest)
events.register(Singletons.getModel().getQuest()); // this one listens to player's mulligans ATM
subscribeToEvents(gameLog.getEventVisitor()); subscribeToEvents(gameLog.getEventVisitor());
} }
@@ -549,42 +543,6 @@ public class Game {
return type; return type;
} }
/**
* TODO: Write javadoc for this method.
* @param card
* @param player
* @return
*/
public List<SpellAbility> getAbilitesOfCard(Card c, Player player) {
// this can only be called by the Human
final Zone zone = this.getZoneOf(c);
final List<SpellAbility> abilities = new ArrayList<SpellAbility>();
for (SpellAbility sa : c.getSpellAbilities()) {
//add alternative costs as additional spell abilities
abilities.add(sa);
abilities.addAll(GameActionUtil.getAlternativeCosts(sa));
}
for (int iSa = 0; iSa < abilities.size();) {
SpellAbility sa = abilities.get(iSa);
sa.setActivatingPlayer(player);
if (!sa.canPlay())
abilities.remove(iSa);
else
iSa++;
}
if (c.isLand() && player.canPlayLand(c)) {
if (zone.is(ZoneType.Hand) || (!zone.is(ZoneType.Battlefield) && c.hasStartOfKeyword("May be played"))) {
Ability.PLAY_LAND_SURROGATE.setSourceCard(c);
abilities.add(Ability.PLAY_LAND_SURROGATE);
}
}
return abilities;
}
/** /**
* @return the activePlane * @return the activePlane
*/ */

View File

@@ -83,6 +83,9 @@ public class Match {
currentGame = new Game(players, gameType, this); currentGame = new Game(players, gameType, this);
if ( getGameType() == GameType.Quest)
currentGame.subscribeToEvents(Singletons.getModel().getQuest()); // this one listens to player's mulligans ATM
Singletons.getControl().attachToGame(currentGame); Singletons.getControl().attachToGame(currentGame);
final boolean canRandomFoil = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL) && gameType == GameType.Constructed; final boolean canRandomFoil = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL) && gameType == GameType.Constructed;

View File

@@ -695,6 +695,9 @@ public class AiController {
} }
return choice; return choice;
case ChangeZone: // called when permanent ETB 'AttachedTo' something
return ComputerUtilCard.getBestAI(options);
default: throw new InvalidParameterException("AI chooseSingleCard does not know how to choose card for " + api); default: throw new InvalidParameterException("AI chooseSingleCard does not know how to choose card for " + api);
} }
} }

View File

@@ -466,6 +466,9 @@ public class PhaseHandler implements java.io.Serializable {
Player whoDeclares = playerDeclaresAttackers == null || playerDeclaresAttackers.hasLost() ? playerTurn : playerDeclaresAttackers; Player whoDeclares = playerDeclaresAttackers == null || playerDeclaresAttackers.hasLost() ? playerTurn : playerDeclaresAttackers;
whoDeclares.getController().declareAttackers(playerTurn); whoDeclares.getController().declareAttackers(playerTurn);
if ( game.isGameOver() ) // they just like to close window at any moment
return;
game.getCombat().removeAbsentCombatants(); game.getCombat().removeAbsentCombatants();
CombatUtil.checkAttackOrBlockAlone(game.getCombat()); CombatUtil.checkAttackOrBlockAlone(game.getCombat());
@@ -519,6 +522,9 @@ public class PhaseHandler implements java.io.Serializable {
Player whoDeclaresBlockers = playerDeclaresBlockers == null || playerDeclaresBlockers.hasLost() ? p : playerDeclaresBlockers; Player whoDeclaresBlockers = playerDeclaresBlockers == null || playerDeclaresBlockers.hasLost() ? p : playerDeclaresBlockers;
if ( combat.isPlayerAttacked(p) ) if ( combat.isPlayerAttacked(p) )
whoDeclaresBlockers.getController().declareBlockers(p); whoDeclaresBlockers.getController().declareBlockers(p);
if ( game.isGameOver() ) // they just like to close window at any moment
return;
} while(p != playerTurn); } while(p != playerTurn);
combat.removeAbsentCombatants(); combat.removeAbsentCombatants();

View File

@@ -39,7 +39,6 @@ import forge.Constant.Preferences;
import forge.CounterType; import forge.CounterType;
import forge.FThreads; import forge.FThreads;
import forge.GameEntity; import forge.GameEntity;
import forge.GameLogEntryType;
import forge.Singletons; import forge.Singletons;
import forge.card.MagicColor; import forge.card.MagicColor;
import forge.card.ability.AbilityFactory; import forge.card.ability.AbilityFactory;
@@ -3137,7 +3136,6 @@ public class Player extends GameEntity implements Comparable<Player> {
public void onMulliganned() { public void onMulliganned() {
game.fireEvent(new GameEventMulligan(this)); // quest listener may interfere here game.fireEvent(new GameEventMulligan(this)); // quest listener may interfere here
final int newHand = getCardsIn(ZoneType.Hand).size(); final int newHand = getCardsIn(ZoneType.Hand).size();
game.getGameLog().add(GameLogEntryType.MULLIGAN, this + " has mulliganed down to " + newHand + " cards.");
stats.notifyHasMulliganed(); stats.notifyHasMulliganed();
stats.notifyOpeningHandSize(newHand); stats.notifyOpeningHandSize(newHand);
} }

View File

@@ -200,6 +200,9 @@ public class PlayerControllerAi extends PlayerController {
@Override @Override
public List<Card> chooseCardsToDiscardFrom(Player p, SpellAbility sa, List<Card> validCards, int min, int max) { public List<Card> chooseCardsToDiscardFrom(Player p, SpellAbility sa, List<Card> validCards, int min, int max) {
if ( p == player )
return brains.getCardsToDiscard(min, max, validCards, sa);
boolean isTargetFriendly = !p.isOpponentOf(player); boolean isTargetFriendly = !p.isOpponentOf(player);
return isTargetFriendly return isTargetFriendly

View File

@@ -263,8 +263,10 @@ public class PlayerControllerHuman extends PlayerController {
// Human is supposed to read the message and understand from it what to choose // Human is supposed to read the message and understand from it what to choose
if ( isOptional ) if ( isOptional )
return GuiChoose.oneOrNone(title, options); return GuiChoose.oneOrNone(title, options);
else else if ( options.size() > 2 )
return GuiChoose.one(title, options); return GuiChoose.one(title, options);
else
return options.get(0);
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@@ -64,7 +64,7 @@ public class InputPassPriority extends InputSyncronizedBase {
@Override @Override
protected void onCardSelected(Card card, boolean isRmb) { protected void onCardSelected(Card card, boolean isRmb) {
final SpellAbility ab = player.getController().getAbilityToPlay(player.getGame().getAbilitesOfCard(card, player)); final SpellAbility ab = player.getController().getAbilityToPlay(card.getAllPossibleAbilites(player));
if ( null != ab) { if ( null != ab) {
chosenSa = ab; chosenSa = ab;
stop(); stop();

View File

@@ -154,7 +154,7 @@ public class CField implements ICDoc {
// TODO: "can play" check needed! // TODO: "can play" check needed!
// should I check for who owns these cards? Are there any abilities to be played from opponent's graveyard? // should I check for who owns these cards? Are there any abilities to be played from opponent's graveyard?
final SpellAbility ab = player.getController().getAbilityToPlay(game.getAbilitesOfCard(c, player)); final SpellAbility ab = player.getController().getAbilityToPlay(c.getAllPossibleAbilites(player));
if ( null != ab) { if ( null != ab) {
game.getAction().invoke(new Runnable(){ @Override public void run(){ game.getAction().invoke(new Runnable(){ @Override public void run(){
HumanPlay.playSpellAbility(player, ab); HumanPlay.playSpellAbility(player, ab);