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.ZCTrigger;
import forge.game.Game;
import forge.game.GameActionUtil;
import forge.game.GlobalRuleChange;
import forge.game.event.GameEventCardDamaged;
import forge.game.event.GameEventCardDamaged.DamageType;
@@ -72,6 +73,7 @@ import forge.game.event.GameEventCounterRemoved;
import forge.game.event.GameEventCardTapped;
import forge.game.phase.Combat;
import forge.game.player.Player;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
import forge.item.CardDb;
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!");
}
/**
* 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

View File

@@ -14,6 +14,7 @@ import forge.game.event.GameEventBlockersDeclared;
import forge.game.event.GameEventCardDamaged;
import forge.game.event.GameEventCardDamaged.DamageType;
import forge.game.event.GameEventLandPlayed;
import forge.game.event.GameEventMulligan;
import forge.game.event.GameEventPlayerDamaged;
import forge.game.event.GameEventPlayerPoisoned;
import forge.game.event.GameEventSpellAbilityCast;
@@ -27,6 +28,7 @@ import forge.game.event.GameEventPlayerControl;
import forge.game.player.LobbyPlayer;
import forge.game.player.Player;
import forge.game.player.PlayerStatistics;
import forge.game.zone.ZoneType;
import forge.util.Lang;
import forge.util.maps.MapOfLists;
@@ -232,6 +234,12 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
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
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.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import forge.util.MyRandom;
@@ -1372,4 +1373,13 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
} // 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.trigger.TriggerType;
import forge.game.Game;
import forge.game.ai.ComputerUtilCard;
import forge.game.player.Player;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
@@ -428,10 +427,11 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (tgt != null && tgtC.isInPlay() && !tgtC.canBeTargetedBy(sa)) {
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;
}
final Zone originZone = game.getZoneOf(tgtC);
// 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);
}
if (!list.isEmpty()) {
Card attachedTo = null;
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);
}
Card attachedTo = player.getController().chooseSingleCardForEffect(list, sa, tgtC + " - Select a card to attach to.");
if (tgtC.isAura()) {
if (tgtC.isEnchanting()) {
// If this Card is already Enchanting something, need

View File

@@ -18,7 +18,6 @@
package forge.card.cardfactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import forge.Card;
@@ -31,14 +30,10 @@ import forge.card.mana.ManaCost;
import forge.card.spellability.Spell;
import forge.card.spellability.SpellAbility;
import forge.game.Game;
import forge.game.ai.AiController;
import forge.game.player.Player;
import forge.game.player.PlayerControllerAi;
import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.gui.input.InputPayManaExecuteCommands;
import forge.gui.input.InputSelectCards;
import forge.gui.input.InputSelectCardsFromList;
/**
* <p>
@@ -52,41 +47,26 @@ public class CardFactorySorceries {
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()) {
lands.add(p.getLandsInPlay());
int pL = p.getLandsInPlay().size();
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()) {
List<Card> l = ll.next();
int sac = l.size() - min;
List<Card> l = p.getLandsInPlay();
int sac = l.size() - minLands;
if (sac == 0) {
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);
inp.setMessage("Select %d more land(s) to sacrifice");
Singletons.getControl().getInputQueue().setInputAndWait(inp);
for( Card crd : inp.getSelected() )
List<Card> toSac = p.getController().choosePermanentsToSacrifice(card, sac, sac, l, "Select %d more land(s) to sacrifice");
for( Card crd : toSac )
p.getGame().getAction().sacrifice(crd, card);
}
}
}
private static final void balanceHands(Game game, Spell spell) {
int min = Integer.MAX_VALUE;
@@ -100,26 +80,16 @@ public class CardFactorySorceries {
if (sac == 0) {
continue;
}
if (p.isHuman()) {
InputSelectCards sc = new InputSelectCardsFromList(sac, sac, hand);
sc.setMessage("Select %d more card(s) to discard");
Singletons.getControl().getInputQueue().setInputAndWait(sc);
for( Card c : sc.getSelected())
List<Card> toDiscard = p.getController().chooseCardsToDiscardFrom(p, spell, hand, sac, sac); // "Select %d more card(s) to discard"
for (Card c : toDiscard)
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) {
List<List<Card>> creats = new ArrayList<List<Card>>();
for (Player p : game.getPlayers()) {
creats.add(p.getCreaturesInPlay());
}
int min = Integer.MAX_VALUE;
@@ -135,22 +105,10 @@ public class CardFactorySorceries {
if (sac == 0) {
continue;
}
if (p.isComputer()) {
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);
List<Card> toSac = p.getController().choosePermanentsToSacrifice(card, sac, sac, c, "Select %d more creature(s) to sacrifice");
}
for( Card crd : toSac )
p.getGame().getAction().sacrifice(crd, card);
}
}

View File

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

View File

@@ -30,11 +30,8 @@ import forge.Card;
import forge.CardLists;
import forge.ColorChanger;
import forge.GameLog;
import forge.Singletons;
import forge.StaticEffects;
import forge.card.replacement.ReplacementHandler;
import forge.card.spellability.Ability;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.SpellAbilityStackInstance;
import forge.card.trigger.TriggerHandler;
import forge.card.trigger.TriggerType;
@@ -122,9 +119,6 @@ public class Game {
endOfTurn = new EndOfTurn(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());
}
@@ -549,42 +543,6 @@ public class Game {
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
*/

View File

@@ -83,6 +83,9 @@ public class Match {
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);
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;
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);
}
}

View File

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

View File

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

View File

@@ -200,6 +200,9 @@ public class PlayerControllerAi extends PlayerController {
@Override
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);
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
if ( isOptional )
return GuiChoose.oneOrNone(title, options);
else
else if ( options.size() > 2 )
return GuiChoose.one(title, options);
else
return options.get(0);
}
/* (non-Javadoc)

View File

@@ -64,7 +64,7 @@ public class InputPassPriority extends InputSyncronizedBase {
@Override
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) {
chosenSa = ab;
stop();

View File

@@ -154,7 +154,7 @@ public class CField implements ICDoc {
// TODO: "can play" check needed!
// 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) {
game.getAction().invoke(new Runnable(){ @Override public void run(){
HumanPlay.playSpellAbility(player, ab);