mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 03:08:02 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user