mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 03:38:01 +00:00
A slow-down to AI vs AI games (not finished yet),
Closing the window during AI vs AI match leads to a draw
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -14163,6 +14163,7 @@ src/main/java/forge/control/ChatArea.java -text
|
|||||||
src/main/java/forge/control/ControlBazaarUI.java -text
|
src/main/java/forge/control/ControlBazaarUI.java -text
|
||||||
src/main/java/forge/control/FControl.java -text
|
src/main/java/forge/control/FControl.java -text
|
||||||
src/main/java/forge/control/FControlGameEventHandler.java -text
|
src/main/java/forge/control/FControlGameEventHandler.java -text
|
||||||
|
src/main/java/forge/control/FControlGamePlayback.java -text
|
||||||
src/main/java/forge/control/InputQueue.java svneol=native#text/plain
|
src/main/java/forge/control/InputQueue.java svneol=native#text/plain
|
||||||
src/main/java/forge/control/KeyboardShortcuts.java -text
|
src/main/java/forge/control/KeyboardShortcuts.java -text
|
||||||
src/main/java/forge/control/Lobby.java -text
|
src/main/java/forge/control/Lobby.java -text
|
||||||
@@ -14234,6 +14235,7 @@ src/main/java/forge/game/event/GameEventDuelFinished.java -text
|
|||||||
src/main/java/forge/game/event/GameEventDuelOutcome.java -text
|
src/main/java/forge/game/event/GameEventDuelOutcome.java -text
|
||||||
src/main/java/forge/game/event/GameEventFlipCoin.java -text
|
src/main/java/forge/game/event/GameEventFlipCoin.java -text
|
||||||
src/main/java/forge/game/event/GameEventGameRestarted.java -text
|
src/main/java/forge/game/event/GameEventGameRestarted.java -text
|
||||||
|
src/main/java/forge/game/event/GameEventGameStarted.java -text
|
||||||
src/main/java/forge/game/event/GameEventLandPlayed.java -text
|
src/main/java/forge/game/event/GameEventLandPlayed.java -text
|
||||||
src/main/java/forge/game/event/GameEventLifeLoss.java -text
|
src/main/java/forge/game/event/GameEventLifeLoss.java -text
|
||||||
src/main/java/forge/game/event/GameEventManaBurn.java -text
|
src/main/java/forge/game/event/GameEventManaBurn.java -text
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import java.awt.event.WindowAdapter;
|
|||||||
import java.awt.event.WindowEvent;
|
import java.awt.event.WindowEvent;
|
||||||
import java.awt.event.WindowListener;
|
import java.awt.event.WindowListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JLayeredPane;
|
import javax.swing.JLayeredPane;
|
||||||
@@ -60,6 +61,7 @@ import forge.gui.match.controllers.CStack;
|
|||||||
import forge.gui.match.nonsingleton.VField;
|
import forge.gui.match.nonsingleton.VField;
|
||||||
import forge.gui.match.views.VAntes;
|
import forge.gui.match.views.VAntes;
|
||||||
import forge.gui.toolbox.CardFaceSymbols;
|
import forge.gui.toolbox.CardFaceSymbols;
|
||||||
|
import forge.gui.toolbox.FOverlay;
|
||||||
import forge.gui.toolbox.FSkin;
|
import forge.gui.toolbox.FSkin;
|
||||||
import forge.net.NetServer;
|
import forge.net.NetServer;
|
||||||
import forge.properties.NewConstants;
|
import forge.properties.NewConstants;
|
||||||
@@ -127,7 +129,7 @@ public enum FControl {
|
|||||||
Singletons.getView().getFrame().setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
Singletons.getView().getFrame().setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||||
|
|
||||||
if (!FControl.this.game.isGameOver())
|
if (!FControl.this.game.isGameOver())
|
||||||
CDock.SINGLETON_INSTANCE.concede();
|
stopGame();
|
||||||
else {
|
else {
|
||||||
Singletons.getControl().changeState(FControl.Screens.HOME_SCREEN);
|
Singletons.getControl().changeState(FControl.Screens.HOME_SCREEN);
|
||||||
SOverlayUtils.hideOverlay();
|
SOverlayUtils.hideOverlay();
|
||||||
@@ -351,6 +353,28 @@ public enum FControl {
|
|||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void stopGame() {
|
||||||
|
List<Player> pp = new ArrayList<Player>();
|
||||||
|
for(Player p : game.getPlayers()) {
|
||||||
|
if ( p.getOriginalLobbyPlayer() == getLobby().getGuiPlayer() )
|
||||||
|
pp.add(p);
|
||||||
|
}
|
||||||
|
boolean hasHuman = !pp.isEmpty();
|
||||||
|
|
||||||
|
if ( pp.isEmpty() )
|
||||||
|
pp.addAll(game.getPlayers()); // no human? then all players surrender!
|
||||||
|
|
||||||
|
for(Player p: pp)
|
||||||
|
p.concede();
|
||||||
|
|
||||||
|
boolean humanHasPriority = game.getPhaseHandler().getPriorityPlayer().getLobbyPlayer() == getLobby().getGuiPlayer();
|
||||||
|
|
||||||
|
if ( hasHuman && humanHasPriority )
|
||||||
|
game.getAction().checkGameOverCondition();
|
||||||
|
else
|
||||||
|
game.isGameOver(); // this is synchronized method - it's used to make Game-0 thread see changes made here
|
||||||
|
}
|
||||||
|
|
||||||
private InputQueue inputQueue;
|
private InputQueue inputQueue;
|
||||||
public InputQueue getInputQueue() {
|
public InputQueue getInputQueue() {
|
||||||
return inputQueue;
|
return inputQueue;
|
||||||
@@ -358,6 +382,7 @@ public enum FControl {
|
|||||||
|
|
||||||
|
|
||||||
private final FControlGameEventHandler fcVisitor = new FControlGameEventHandler(this);
|
private final FControlGameEventHandler fcVisitor = new FControlGameEventHandler(this);
|
||||||
|
private final FControlGamePlayback playbackControl = new FControlGamePlayback(this);
|
||||||
public void attachToGame(Game game0) {
|
public void attachToGame(Game game0) {
|
||||||
// TODO: Detach from other game we might be looking at
|
// TODO: Detach from other game we might be looking at
|
||||||
|
|
||||||
@@ -382,14 +407,20 @@ public enum FControl {
|
|||||||
|
|
||||||
CMessage.SINGLETON_INSTANCE.getInputControl().setGame(game);
|
CMessage.SINGLETON_INSTANCE.getInputControl().setGame(game);
|
||||||
|
|
||||||
// models shall notify controllers of changes
|
|
||||||
|
|
||||||
|
|
||||||
// some observers were set in CMatchUI.initMatch
|
|
||||||
|
|
||||||
// Listen to DuelOutcome event to show ViewWinLose
|
// Listen to DuelOutcome event to show ViewWinLose
|
||||||
game.subscribeToEvents(fcVisitor);
|
game.subscribeToEvents(fcVisitor);
|
||||||
|
|
||||||
|
// Add playback controls to match if needed
|
||||||
|
boolean hasHuman = false;
|
||||||
|
for(Player p : game.getPlayers()) {
|
||||||
|
if ( p.getController().getLobbyPlayer() == getLobby().getGuiPlayer() )
|
||||||
|
hasHuman = true;
|
||||||
|
}
|
||||||
|
if (!hasHuman) {
|
||||||
|
game.subscribeToEvents(playbackControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
VAntes.SINGLETON_INSTANCE.setModel(game.getRegisteredPlayers());
|
VAntes.SINGLETON_INSTANCE.setModel(game.getRegisteredPlayers());
|
||||||
|
|
||||||
|
|||||||
@@ -26,16 +26,15 @@ import forge.gui.match.nonsingleton.VHand;
|
|||||||
import forge.gui.match.nonsingleton.VField.PhaseLabel;
|
import forge.gui.match.nonsingleton.VField.PhaseLabel;
|
||||||
|
|
||||||
public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||||
public final FControl fc;
|
private final FControl fc;
|
||||||
public FControlGameEventHandler(FControl fc ) {
|
public FControlGameEventHandler(FControl fc ) {
|
||||||
this.fc = fc;
|
this.fc = fc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final AtomicBoolean phaseUpdPlanned = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void receiveGameEvent(final GameEvent ev) { ev.visit(this); }
|
public void receiveGameEvent(final GameEvent ev) { ev.visit(this); }
|
||||||
|
|
||||||
|
private final AtomicBoolean phaseUpdPlanned = new AtomicBoolean(false);
|
||||||
@Override
|
@Override
|
||||||
public Void visit(final GameEventTurnPhase ev) {
|
public Void visit(final GameEventTurnPhase ev) {
|
||||||
if ( phaseUpdPlanned.getAndSet(true) ) return null;
|
if ( phaseUpdPlanned.getAndSet(true) ) return null;
|
||||||
@@ -50,12 +49,10 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
|||||||
PhaseLabel lbl = matchUi.getFieldViewFor(p).getLabelFor(ph);
|
PhaseLabel lbl = matchUi.getFieldViewFor(p).getLabelFor(ph);
|
||||||
|
|
||||||
matchUi.resetAllPhaseButtons();
|
matchUi.resetAllPhaseButtons();
|
||||||
if (lbl != null) {
|
if (lbl != null) lbl.setActive(true);
|
||||||
lbl.setActive(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} });
|
} });
|
||||||
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +96,4 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
|||||||
} });
|
} });
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
100
src/main/java/forge/control/FControlGamePlayback.java
Normal file
100
src/main/java/forge/control/FControlGamePlayback.java
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package forge.control;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
|
import forge.game.event.GameEvent;
|
||||||
|
import forge.game.event.GameEventBlockerAssigned;
|
||||||
|
import forge.game.event.GameEventGameStarted;
|
||||||
|
import forge.game.event.GameEventLandPlayed;
|
||||||
|
import forge.game.event.GameEventSpellResolved;
|
||||||
|
import forge.game.event.GameEventTurnPhase;
|
||||||
|
import forge.game.event.IGameEventVisitor;
|
||||||
|
import forge.game.player.Player;
|
||||||
|
import forge.gui.match.CMatchUI;
|
||||||
|
|
||||||
|
public class FControlGamePlayback extends IGameEventVisitor.Base<Void> {
|
||||||
|
private final FControl fc;
|
||||||
|
public FControlGamePlayback(FControl fc ) {
|
||||||
|
this.fc = fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void receiveGameEvent(final GameEvent ev) { ev.visit(this); }
|
||||||
|
|
||||||
|
private int phasesDelay = 400;
|
||||||
|
private int combatDelay = 400;
|
||||||
|
private int resolveDelay = 600;
|
||||||
|
|
||||||
|
private void pauseForEvent(int delay) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(delay);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// TODO Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visit(GameEventBlockerAssigned event) {
|
||||||
|
pauseForEvent(combatDelay);
|
||||||
|
return super.visit(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventTurnPhase)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Void visit(GameEventTurnPhase ev) {
|
||||||
|
boolean isUiToStop = CMatchUI.SINGLETON_INSTANCE.stopAtPhase(ev.playerTurn, ev.phase);
|
||||||
|
|
||||||
|
switch(ev.phase) {
|
||||||
|
case COMBAT_END:
|
||||||
|
case COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY:
|
||||||
|
case COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY:
|
||||||
|
if( fc.getObservedGame().getPhaseHandler().inCombat() )
|
||||||
|
pauseForEvent(combatDelay);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if( isUiToStop )
|
||||||
|
pauseForEvent(phasesDelay);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (non-Javadoc)
|
||||||
|
* @see forge.game.event.IGameEventVisitor.Base#visit(forge.game.event.GameEventGameStarted)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Void visit(GameEventGameStarted event) {
|
||||||
|
boolean hasHuman = false;
|
||||||
|
for(Player p : event.players) {
|
||||||
|
if ( p.getController().getLobbyPlayer() == fc.getLobby().getGuiPlayer() )
|
||||||
|
hasHuman = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// show input here to adjust speed if no human playing
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visit(GameEventLandPlayed event) {
|
||||||
|
pauseForEvent(resolveDelay);
|
||||||
|
return super.visit(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void visit(GameEventSpellResolved event) {
|
||||||
|
pauseForEvent(resolveDelay);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -491,7 +491,10 @@ public class Game {
|
|||||||
public Player getNextPlayerAfter(final Player playerTurn) {
|
public Player getNextPlayerAfter(final Player playerTurn) {
|
||||||
int iPlayer = roIngamePlayers.indexOf(playerTurn);
|
int iPlayer = roIngamePlayers.indexOf(playerTurn);
|
||||||
|
|
||||||
if (-1 == iPlayer && !roIngamePlayers.isEmpty()) { // if playerTurn has just lost
|
if (roIngamePlayers.isEmpty())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (-1 == iPlayer) { // if playerTurn has just lost
|
||||||
int iAlive;
|
int iAlive;
|
||||||
iPlayer = allPlayers.indexOf(playerTurn);
|
iPlayer = allPlayers.indexOf(playerTurn);
|
||||||
do {
|
do {
|
||||||
@@ -507,7 +510,6 @@ public class Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return roIngamePlayers.get(iPlayer);
|
return roIngamePlayers.get(iPlayer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPosition(Player player, Player startingPlayer) {
|
public int getPosition(Player player, Player startingPlayer) {
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ import forge.game.event.GameEventCardDestroyed;
|
|||||||
import forge.game.event.GameEventCardRegenerated;
|
import forge.game.event.GameEventCardRegenerated;
|
||||||
import forge.game.event.GameEventCardSacrificed;
|
import forge.game.event.GameEventCardSacrificed;
|
||||||
import forge.game.event.GameEventDuelFinished;
|
import forge.game.event.GameEventDuelFinished;
|
||||||
|
import forge.game.event.GameEventFlipCoin;
|
||||||
|
import forge.game.event.GameEventGameStarted;
|
||||||
import forge.game.player.GameLossReason;
|
import forge.game.player.GameLossReason;
|
||||||
import forge.game.player.HumanPlay;
|
import forge.game.player.HumanPlay;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
@@ -72,6 +74,7 @@ import forge.game.zone.Zone;
|
|||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
import forge.gui.GuiChoose;
|
import forge.gui.GuiChoose;
|
||||||
import forge.gui.GuiDialog;
|
import forge.gui.GuiDialog;
|
||||||
|
import forge.util.Aggregates;
|
||||||
import forge.util.maps.CollectionSuppliers;
|
import forge.util.maps.CollectionSuppliers;
|
||||||
import forge.util.maps.HashMapOfLists;
|
import forge.util.maps.HashMapOfLists;
|
||||||
import forge.util.maps.MapOfLists;
|
import forge.util.maps.MapOfLists;
|
||||||
@@ -786,75 +789,6 @@ public class GameAction {
|
|||||||
game.getStack().add(activate);
|
game.getStack().add(activate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* checkEndGameSate.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @return a boolean.
|
|
||||||
*/
|
|
||||||
private final GameEndReason checkEndGameState(final Game game) {
|
|
||||||
|
|
||||||
GameEndReason reason = null;
|
|
||||||
// award loses as SBE
|
|
||||||
List<Player> losers = null;
|
|
||||||
for (Player p : game.getPlayers()) {
|
|
||||||
if (p.checkLoseCondition()) { // this will set appropriate outcomes
|
|
||||||
// Run triggers
|
|
||||||
if (losers == null) {
|
|
||||||
losers = new ArrayList<Player>(3);
|
|
||||||
}
|
|
||||||
losers.add(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has anyone won by spelleffect?
|
|
||||||
for (Player p : game.getPlayers()) {
|
|
||||||
if (!p.hasWon()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// then the rest have lost!
|
|
||||||
reason = GameEndReason.WinsGameSpellEffect;
|
|
||||||
for (Player pl : game.getPlayers()) {
|
|
||||||
if (pl.equals(p)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pl.loseConditionMet(GameLossReason.OpponentWon, p.getOutcome().altWinSourceName)) {
|
|
||||||
reason = null; // they cannot lose!
|
|
||||||
} else {
|
|
||||||
if (losers == null) {
|
|
||||||
losers = new ArrayList<Player>(3);
|
|
||||||
}
|
|
||||||
losers.add(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// need a separate loop here, otherwise ConcurrentModificationException is raised
|
|
||||||
if (losers != null) {
|
|
||||||
for (Player p : losers) {
|
|
||||||
game.onPlayerLost(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// still unclear why this has not caught me conceding
|
|
||||||
if (reason == null && Iterables.size(Iterables.filter(game.getPlayers(), Player.Predicates.NOT_LOST)) == 1)
|
|
||||||
{
|
|
||||||
reason = GameEndReason.AllOpponentsLost;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ai's cannot finish their game without human yet - so terminate a game if human has left.
|
|
||||||
/*
|
|
||||||
if (reason == null && !Iterables.any(game.getPlayers(), Predicates.and(Player.Predicates.NOT_LOST, Player.Predicates.isType(PlayerType.HUMAN)))) {
|
|
||||||
reason = GameEndReason.AllHumansLost;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
public final void checkStaticAbilities() {
|
public final void checkStaticAbilities() {
|
||||||
FThreads.assertExecutedByEdt(false);
|
FThreads.assertExecutedByEdt(false);
|
||||||
@@ -1082,18 +1016,81 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
} // for q=0;q<2
|
} // for q=0;q<2
|
||||||
|
|
||||||
GameEndReason endGame = this.checkEndGameState(game);
|
checkGameOverCondition();
|
||||||
if (endGame != null) {
|
|
||||||
// Clear Simultaneous triggers at the end of the game
|
|
||||||
game.setGameOver(endGame);
|
|
||||||
game.getStack().clearSimultaneousStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!refreeze) {
|
if (!refreeze) {
|
||||||
game.getStack().unfreezeStack();
|
game.getStack().unfreezeStack();
|
||||||
}
|
}
|
||||||
} // checkStateEffects()
|
} // checkStateEffects()
|
||||||
|
|
||||||
|
public void checkGameOverCondition() {
|
||||||
|
GameEndReason reason = this.eliminateLosingPlayers();
|
||||||
|
|
||||||
|
// still unclear why this has not caught me conceding
|
||||||
|
if (reason == null )
|
||||||
|
{
|
||||||
|
int cntNotLost = Iterables.size(Iterables.filter(game.getPlayers(), Player.Predicates.NOT_LOST));
|
||||||
|
if( cntNotLost == 1 )
|
||||||
|
reason = GameEndReason.AllOpponentsLost;
|
||||||
|
else if ( cntNotLost == 0 )
|
||||||
|
reason = GameEndReason.Draw;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear Simultaneous triggers at the end of the game
|
||||||
|
game.setGameOver(reason);
|
||||||
|
game.getStack().clearSimultaneousStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
private GameEndReason eliminateLosingPlayers() {
|
||||||
|
// award loses as SBE
|
||||||
|
List<Player> losers = null;
|
||||||
|
for (Player p : game.getPlayers()) {
|
||||||
|
if (p.checkLoseCondition()) { // this will set appropriate outcomes
|
||||||
|
// Run triggers
|
||||||
|
if (losers == null) {
|
||||||
|
losers = new ArrayList<Player>(3);
|
||||||
|
}
|
||||||
|
losers.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GameEndReason reason = null;
|
||||||
|
// Has anyone won by spelleffect?
|
||||||
|
for (Player p : game.getPlayers()) {
|
||||||
|
if (!p.hasWon()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// then the rest have lost!
|
||||||
|
reason = GameEndReason.WinsGameSpellEffect;
|
||||||
|
for (Player pl : game.getPlayers()) {
|
||||||
|
if (pl.equals(p)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pl.loseConditionMet(GameLossReason.OpponentWon, p.getOutcome().altWinSourceName)) {
|
||||||
|
reason = null; // they cannot lose!
|
||||||
|
} else {
|
||||||
|
if (losers == null) {
|
||||||
|
losers = new ArrayList<Player>(3);
|
||||||
|
}
|
||||||
|
losers.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// need a separate loop here, otherwise ConcurrentModificationException is raised
|
||||||
|
if (losers != null) {
|
||||||
|
for (Player p : losers) {
|
||||||
|
game.onPlayerLost(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* destroyPlaneswalkers.
|
* destroyPlaneswalkers.
|
||||||
@@ -1475,28 +1472,50 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startGame(final Player firstPlayer) {
|
private Player determineFirstTurnPlayer(final GameOutcome lastGameOutcome) {
|
||||||
Player first = firstPlayer;
|
// 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());
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if ( game.isGameOver() ) break; // conceded during "play or draw"
|
if ( game.isGameOver() ) break; // conceded during "play or draw"
|
||||||
|
|
||||||
// Draw <handsize> cards
|
// FControl should determine now if there are any human players.
|
||||||
for (final Player p1 : game.getPlayers()) {
|
// Where there are none, it should bring up speed controls
|
||||||
p1.drawCards(p1.getMaxHandSize());
|
game.fireEvent(new GameEventGameStarted(first, game.getPlayers()));
|
||||||
}
|
|
||||||
|
|
||||||
game.setAge(GameAge.Mulligan);
|
game.setAge(GameAge.Mulligan);
|
||||||
|
for (final Player p1 : game.getPlayers())
|
||||||
|
p1.drawCards(p1.getMaxHandSize());
|
||||||
|
|
||||||
performMulligans(first, game.getType() == GameType.Commander);
|
performMulligans(first, game.getType() == GameType.Commander);
|
||||||
|
|
||||||
if ( game.isGameOver() ) break; // conceded during "mulligan" prompt
|
if ( game.isGameOver() ) break; // conceded during "mulligan" prompt
|
||||||
|
|
||||||
// should I restore everyting exiled by Karn here, or before Mulligans is fine?
|
|
||||||
|
|
||||||
game.setAge(GameAge.Play);
|
game.setAge(GameAge.Play);
|
||||||
|
|
||||||
// THIS CODE WILL WORK WITH PHASE = NULL {
|
// THIS CODE WILL WORK WITH PHASE = NULL {
|
||||||
if(game.getType() == GameType.Planechase)
|
if(game.getType() == GameType.Planechase)
|
||||||
firstPlayer.initPlane();
|
first.initPlane();
|
||||||
|
|
||||||
handleLeylinesAndChancellors();
|
handleLeylinesAndChancellors();
|
||||||
checkStateEffects();
|
checkStateEffects();
|
||||||
@@ -1511,7 +1530,7 @@ public class GameAction {
|
|||||||
first = game.getPhaseHandler().getPlayerTurn(); // needed only for restart
|
first = game.getPhaseHandler().getPlayerTurn(); // needed only for restart
|
||||||
} while( game.getAge() == GameAge.RestartedByKarn );
|
} while( game.getAge() == GameAge.RestartedByKarn );
|
||||||
|
|
||||||
// will pull UI
|
// will pull UI dialog, when the UI is listening
|
||||||
game.fireEvent(new GameEventDuelFinished());
|
game.fireEvent(new GameEventDuelFinished());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1588,6 +1607,7 @@ public class GameAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
public void invoke(final Runnable proc) {
|
||||||
if( FThreads.isGameThread() ) {
|
if( FThreads.isGameThread() ) {
|
||||||
proc.run();
|
proc.run();
|
||||||
|
|||||||
@@ -100,8 +100,7 @@ public class Match {
|
|||||||
currentGame.getAction().invoke(new Runnable() {
|
currentGame.getAction().invoke(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final Player firstPlayer = determineFirstTurnPlayer(getLastGameOutcome(), currentGame);
|
currentGame.getAction().startGame();
|
||||||
currentGame.getAction().startGame(firstPlayer);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -205,26 +204,4 @@ public class Match {
|
|||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Player determineFirstTurnPlayer(final GameOutcome lastGameOutcome, final Game game) {
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/main/java/forge/game/event/GameEventGameStarted.java
Normal file
26
src/main/java/forge/game/event/GameEventGameStarted.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package forge.game.event;
|
||||||
|
|
||||||
|
import forge.game.player.Player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Write javadoc for this type.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class GameEventGameStarted extends GameEvent {
|
||||||
|
|
||||||
|
public final Player firstTurn;
|
||||||
|
public final Iterable<Player> players;
|
||||||
|
|
||||||
|
public GameEventGameStarted(Player firstTurn, Iterable<Player> players) {
|
||||||
|
super();
|
||||||
|
this.firstTurn = firstTurn;
|
||||||
|
this.players = players;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T visit(IGameEventVisitor<T> visitor) {
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ public interface IGameEventVisitor<T> {
|
|||||||
T visit(GameEventDuelFinished event);
|
T visit(GameEventDuelFinished event);
|
||||||
T visit(GameEventDuelOutcome event);
|
T visit(GameEventDuelOutcome event);
|
||||||
T visit(GameEventFlipCoin event);
|
T visit(GameEventFlipCoin event);
|
||||||
|
T visit(GameEventGameStarted event);
|
||||||
T visit(GameEventGameRestarted event);
|
T visit(GameEventGameRestarted event);
|
||||||
T visit(GameEventLandPlayed event);
|
T visit(GameEventLandPlayed event);
|
||||||
T visit(GameEventLifeLoss event);
|
T visit(GameEventLifeLoss event);
|
||||||
@@ -53,6 +54,7 @@ public interface IGameEventVisitor<T> {
|
|||||||
public T visit(GameEventDuelFinished event) { return null; }
|
public T visit(GameEventDuelFinished event) { return null; }
|
||||||
public T visit(GameEventDuelOutcome event) { return null; }
|
public T visit(GameEventDuelOutcome event) { return null; }
|
||||||
public T visit(GameEventFlipCoin event) { return null; }
|
public T visit(GameEventFlipCoin event) { return null; }
|
||||||
|
public T visit(GameEventGameStarted event) { return null; }
|
||||||
public T visit(GameEventGameRestarted event) { return null; }
|
public T visit(GameEventGameRestarted event) { return null; }
|
||||||
public T visit(GameEventLandPlayed event) { return null; }
|
public T visit(GameEventLandPlayed event) { return null; }
|
||||||
public T visit(GameEventLifeLoss event) { return null; }
|
public T visit(GameEventLifeLoss event) { return null; }
|
||||||
@@ -69,7 +71,5 @@ public interface IGameEventVisitor<T> {
|
|||||||
public T visit(GameEventPlayerDamaged event) { return null; }
|
public T visit(GameEventPlayerDamaged event) { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -714,14 +714,14 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
|
|||||||
System.out.print(" >>\n");
|
System.out.print(" >>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( game.isGameOver() ) return; // conceded?
|
|
||||||
|
|
||||||
// actingPlayer is the player who may act
|
// actingPlayer is the player who may act
|
||||||
// the firstAction is the player who gained Priority First in this segment
|
// the firstAction is the player who gained Priority First in this segment
|
||||||
// of Priority
|
// of Priority
|
||||||
|
|
||||||
Player nextPlayer = game.getNextPlayerAfter(this.getPriorityPlayer());
|
Player nextPlayer = game.getNextPlayerAfter(this.getPriorityPlayer());
|
||||||
|
|
||||||
|
if ( game.isGameOver() || nextPlayer == null ) return; // conceded?
|
||||||
|
|
||||||
// System.out.println(String.format("%s %s: %s passes priority to %s", playerTurn, phase, actingPlayer, nextPlayer));
|
// System.out.println(String.format("%s %s: %s passes priority to %s", playerTurn, phase, actingPlayer, nextPlayer));
|
||||||
if (this.getFirstPriority().equals(nextPlayer)) {
|
if (this.getFirstPriority().equals(nextPlayer)) {
|
||||||
if (game.getStack().isEmpty()) {
|
if (game.getStack().isEmpty()) {
|
||||||
|
|||||||
@@ -2066,7 +2066,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
* Concede.
|
* Concede.
|
||||||
*/
|
*/
|
||||||
public final void concede() { // No cantLose checks - just lose
|
public final void concede() { // No cantLose checks - just lose
|
||||||
FThreads.assertExecutedByEdt(false);
|
|
||||||
setOutcome(PlayerOutcome.concede());
|
setOutcome(PlayerOutcome.concede());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2705,6 +2704,10 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return getController().getLobbyPlayer();
|
return getController().getLobbyPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final LobbyPlayer getOriginalLobbyPlayer() {
|
||||||
|
return controllerCreator.getLobbyPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
public final boolean isMindSlaved() {
|
public final boolean isMindSlaved() {
|
||||||
return controller.getLobbyPlayer() != controllerCreator.getLobbyPlayer();
|
return controller.getLobbyPlayer() != controllerCreator.getLobbyPlayer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,8 +102,8 @@ public class GuiDialog {
|
|||||||
// Play the Flip A Coin sound
|
// Play the Flip A Coin sound
|
||||||
caller.getGame().fireEvent(new GameEventFlipCoin());
|
caller.getGame().fireEvent(new GameEventFlipCoin());
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(null, source.getName() + " - " + caller + winMsg, source.getName(),
|
message(source.getName() + " - " + caller + winMsg, source.getName());
|
||||||
JOptionPane.PLAIN_MESSAGE);
|
|
||||||
return winFlip;
|
return winFlip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -198,8 +198,10 @@ public class ViewWinLose {
|
|||||||
nHumansInGame++;
|
nHumansInGame++;
|
||||||
}
|
}
|
||||||
LobbyPlayer winner = match.getLastGameOutcome().getWinner();
|
LobbyPlayer winner = match.getLastGameOutcome().getWinner();
|
||||||
String title = nHumansInGame == 1 ? "You " + (winner == guiPlayer ? "won!" : "lost!") : winner.getName() + " Won!";
|
if ( winner == null )
|
||||||
return title;
|
return "It's a draw!";
|
||||||
|
|
||||||
|
return nHumansInGame == 1 ? "You " + (winner == guiPlayer ? "won!" : "lost!") : winner.getName() + " Won!";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return {@link forge.gui.toolbox.FButton} */
|
/** @return {@link forge.gui.toolbox.FButton} */
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import forge.FThreads;
|
|||||||
import forge.Singletons;
|
import forge.Singletons;
|
||||||
import forge.CardPredicates.Presets;
|
import forge.CardPredicates.Presets;
|
||||||
import forge.Command;
|
import forge.Command;
|
||||||
|
import forge.control.FControl;
|
||||||
import forge.deck.Deck;
|
import forge.deck.Deck;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.phase.CombatUtil;
|
import forge.game.phase.CombatUtil;
|
||||||
@@ -81,22 +82,7 @@ public enum CDock implements ICDoc {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Player p = findAffectedPlayer();
|
Singletons.getControl().stopGame();
|
||||||
if( p == null ) return;
|
|
||||||
if( p.isMindSlaved() ) {
|
|
||||||
GuiDialog.message("You cannot make concede a player you temporarily control");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
game.getAction().invoke(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
p.concede();
|
|
||||||
p.getGame().getAction().checkStateEffects();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
game = null; // no second entry possible;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user