Public Discard Pile Support
- Added support for public discard piles on a per-format basis. - Altered the "removed" pile to be public info. - Altered handling of the end-game-at-reconcile option to not actually require the reconcile. - Altered the PC formats to make discard piles public and to end the game after regroup actions
This commit is contained in:
@@ -258,7 +258,7 @@ var GempLotrGameUI = Class.extend({
|
||||
this.layoutUI(false);
|
||||
},
|
||||
|
||||
initializeGameUI: function () {
|
||||
initializeGameUI: function (discardPublic) {
|
||||
this.advPathGroup = new AdvPathCardGroup($("#main"));
|
||||
|
||||
var that = this;
|
||||
@@ -330,16 +330,18 @@ var GempLotrGameUI = Class.extend({
|
||||
|
||||
//TODO: This is where we will need to add support for public discard piles
|
||||
if (!this.spectatorMode) {
|
||||
$("#discard" + this.getPlayerIndex(this.bottomPlayerId)).addClass("clickable").click(
|
||||
(function (index) {
|
||||
return function () {
|
||||
var dialog = that.discardPileDialogs[index];
|
||||
var group = that.discardPileGroups[index];
|
||||
openSizeDialog(dialog);
|
||||
that.dialogResize(dialog, group);
|
||||
group.layoutCards();
|
||||
};
|
||||
})(that.bottomPlayerId));
|
||||
if(!discardPublic) {
|
||||
$("#discard" + this.getPlayerIndex(this.bottomPlayerId)).addClass("clickable").click(
|
||||
(function (index) {
|
||||
return function () {
|
||||
var dialog = that.discardPileDialogs[index];
|
||||
var group = that.discardPileGroups[index];
|
||||
openSizeDialog(dialog);
|
||||
that.dialogResize(dialog, group);
|
||||
group.layoutCards();
|
||||
};
|
||||
})(that.bottomPlayerId));
|
||||
}
|
||||
$("#adventureDeck" + this.getPlayerIndex(this.bottomPlayerId)).addClass("clickable").click(
|
||||
(function (index) {
|
||||
return function () {
|
||||
@@ -363,6 +365,19 @@ var GempLotrGameUI = Class.extend({
|
||||
group.layoutCards();
|
||||
};
|
||||
})(i));
|
||||
|
||||
if(discardPublic) {
|
||||
$("#discard" + i).addClass("clickable").click(
|
||||
(function (index) {
|
||||
return function () {
|
||||
var dialog = that.discardPileDialogs[that.allPlayerIds[index]];
|
||||
var group = that.discardPileGroups[that.allPlayerIds[index]];
|
||||
openSizeDialog(dialog);
|
||||
that.dialogResize(dialog, group);
|
||||
group.layoutCards();
|
||||
};
|
||||
})(i));
|
||||
}
|
||||
}
|
||||
|
||||
this.alertBox = $("<div class='ui-widget-content'></div>");
|
||||
@@ -1359,6 +1374,7 @@ var GempLotrGameUI = Class.extend({
|
||||
participant: function (element) {
|
||||
var participantId = element.getAttribute("participantId");
|
||||
this.allPlayerIds = element.getAttribute("allParticipantIds").split(",");
|
||||
var discardPublic = element.getAttribute("discardPublic") === 'true';
|
||||
|
||||
this.bottomPlayerId = participantId;
|
||||
|
||||
@@ -1371,79 +1387,100 @@ var GempLotrGameUI = Class.extend({
|
||||
} else {
|
||||
this.spectatorMode = false;
|
||||
|
||||
var discardPileDialog = $("<div></div>").dialog({
|
||||
autoOpen: false,
|
||||
closeOnEscape: true,
|
||||
resizable: true,
|
||||
title: "Discard - " + participantId,
|
||||
minHeight: 80,
|
||||
minWidth: 200,
|
||||
width: 600,
|
||||
height: 300
|
||||
});
|
||||
if(!discardPublic) {
|
||||
this.createPile(participantId, "Discard Pile", "discardPileDialogs", "discardPileGroups");
|
||||
}
|
||||
|
||||
this.discardPileDialogs[participantId] = discardPileDialog;
|
||||
this.discardPileGroups[participantId] = new NormalCardGroup(discardPileDialog, function (card) {
|
||||
return true;
|
||||
}, false);
|
||||
|
||||
this.discardPileGroups[participantId].setBounds(this.padding, this.padding, 580 - 2 * (this.padding), 250 - 2 * (this.padding));
|
||||
|
||||
discardPileDialog.bind("dialogresize", function () {
|
||||
that.dialogResize(discardPileDialog, that.discardPileGroups[participantId]);
|
||||
});
|
||||
|
||||
var adventureDeckDialog = $("<div></div>").dialog({
|
||||
autoOpen: false,
|
||||
closeOnEscape: true,
|
||||
resizable: true,
|
||||
title: "Adventure deck - " + participantId,
|
||||
minHeight: 80,
|
||||
minWidth: 200,
|
||||
width: 600,
|
||||
height: 300
|
||||
});
|
||||
|
||||
this.adventureDeckDialogs[participantId] = adventureDeckDialog;
|
||||
this.adventureDeckGroups[participantId] = new NormalCardGroup(adventureDeckDialog, function (card) {
|
||||
return true;
|
||||
}, false);
|
||||
|
||||
this.adventureDeckGroups[participantId].setBounds(this.padding, this.padding, 580 - 2 * (this.padding), 250 - 2 * (this.padding));
|
||||
|
||||
adventureDeckDialog.bind("dialogresize", function () {
|
||||
that.dialogResize(adventureDeckDialog, that.adventureDeckGroups[participantId]);
|
||||
});
|
||||
this.createPile(participantId, "Adventure Deck", "adventureDeckDialogs", "adventureDeckGroups");
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.allPlayerIds.length; i++) {
|
||||
var deadPileDialog = $("<div></div>").dialog({
|
||||
autoOpen: false,
|
||||
closeOnEscape: true,
|
||||
resizable: true,
|
||||
title: "Dead pile - " + this.allPlayerIds[i],
|
||||
minHeight: 80,
|
||||
minWidth: 200,
|
||||
width: 600,
|
||||
height: 300
|
||||
});
|
||||
this.deadPileDialogs[this.allPlayerIds[i]] = deadPileDialog;
|
||||
this.deadPileGroups[this.allPlayerIds[i]] = new NormalCardGroup(deadPileDialog, function (card) {
|
||||
return true;
|
||||
}, false);
|
||||
|
||||
participantId = this.allPlayerIds[i];
|
||||
|
||||
this.createPile(participantId, "Dead Pile", "deadPileDialogs", "deadPileGroups");
|
||||
|
||||
if(discardPublic) {
|
||||
this.createPile(participantId, "Discard Pile", "discardPileDialogs", "discardPileGroups");
|
||||
}
|
||||
|
||||
// var deadPileDialog = $("<div></div>").dialog({
|
||||
// autoOpen: false,
|
||||
// closeOnEscape: true,
|
||||
// resizable: true,
|
||||
// title: "Dead pile - " + this.allPlayerIds[i],
|
||||
// minHeight: 80,
|
||||
// minWidth: 200,
|
||||
// width: 600,
|
||||
// height: 300
|
||||
// });
|
||||
// this.deadPileDialogs[this.allPlayerIds[i]] = deadPileDialog;
|
||||
// this.deadPileGroups[this.allPlayerIds[i]] = new NormalCardGroup(deadPileDialog, function (card) {
|
||||
// return true;
|
||||
// }, false);
|
||||
|
||||
this.deadPileGroups[this.allPlayerIds[i]].setBounds(this.padding, this.padding, 580 - 2 * (this.padding), 250 - 2 * (this.padding));
|
||||
// this.deadPileGroups[this.allPlayerIds[i]].setBounds(this.padding, this.padding, 580 - 2 * (this.padding), 250 - 2 * (this.padding));
|
||||
|
||||
deadPileDialog.bind("dialogresize", (function (dialog, index) {
|
||||
return function () {
|
||||
that.dialogResize(dialog, that.deadPileGroups[that.allPlayerIds[index]]);
|
||||
}
|
||||
})(deadPileDialog, i));
|
||||
// deadPileDialog.bind("dialogresize", (function (dialog, index) {
|
||||
// return function () {
|
||||
// that.dialogResize(dialog, that.deadPileGroups[that.allPlayerIds[index]]);
|
||||
// }
|
||||
// })(deadPileDialog, i));
|
||||
}
|
||||
|
||||
this.initializeGameUI();
|
||||
this.initializeGameUI(discardPublic);
|
||||
this.layoutUI(true);
|
||||
},
|
||||
|
||||
createAdventureDeckPiles: function(id) {
|
||||
var adventureDeckDialog = $("<div></div>").dialog({
|
||||
autoOpen: false,
|
||||
closeOnEscape: true,
|
||||
resizable: true,
|
||||
title: "Adventure deck - " + participantId,
|
||||
minHeight: 80,
|
||||
minWidth: 200,
|
||||
width: 600,
|
||||
height: 300
|
||||
});
|
||||
|
||||
this.adventureDeckDialogs[participantId] = adventureDeckDialog;
|
||||
this.adventureDeckGroups[participantId] = new NormalCardGroup(adventureDeckDialog, function (card) {
|
||||
return true;
|
||||
}, false);
|
||||
|
||||
this.adventureDeckGroups[participantId].setBounds(this.padding, this.padding, 580 - 2 * (this.padding), 250 - 2 * (this.padding));
|
||||
|
||||
adventureDeckDialog.bind("dialogresize", function () {
|
||||
that.dialogResize(adventureDeckDialog, that.adventureDeckGroups[participantId]);
|
||||
});
|
||||
},
|
||||
|
||||
createPile: function(playerId, name, dialogsName, groupsName) {
|
||||
var dialog = $("<div></div>").dialog({
|
||||
autoOpen: false,
|
||||
closeOnEscape: true,
|
||||
resizable: true,
|
||||
title: name + " - " + playerId,
|
||||
minHeight: 80,
|
||||
minWidth: 200,
|
||||
width: 600,
|
||||
height: 300
|
||||
});
|
||||
|
||||
this[dialogsName][playerId] = dialog;
|
||||
this[groupsName][playerId] = new NormalCardGroup(dialog, function (card) {
|
||||
return true;
|
||||
}, false);
|
||||
|
||||
this[groupsName][playerId].setBounds(this.padding, this.padding, 580 - 2 * (this.padding), 250 - 2 * (this.padding));
|
||||
|
||||
var that = this;
|
||||
|
||||
dialog.bind("dialogresize", function () {
|
||||
that.dialogResize(dialog, that[groupsName][playerId]);
|
||||
});
|
||||
},
|
||||
|
||||
getDecisionParameter: function (decision, name) {
|
||||
var parameters = decision.getElementsByTagName("parameter");
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
"order": 2,
|
||||
"sites":"FELLOWSHIP",
|
||||
"cancelRingBearerSkirmish":true,
|
||||
"winAtEndOfRegroup":true,
|
||||
"discardPileIsPublic":true,
|
||||
"set":[1, 2, 3, 101],
|
||||
"erratasets": [
|
||||
51,52,53
|
||||
@@ -41,6 +43,8 @@
|
||||
"code":"pc_movie",
|
||||
"order": 4,
|
||||
"sites":"KING",
|
||||
"winAtEndOfRegroup":true,
|
||||
"discardPileIsPublic":true,
|
||||
"set":[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 101],
|
||||
"erratasets": [
|
||||
51,52,53,54,55,56,57,58,59,60
|
||||
@@ -60,6 +64,8 @@
|
||||
"code":"pc_expanded",
|
||||
"order": 6,
|
||||
"sites":"SHADOWS",
|
||||
"winAtEndOfRegroup":true,
|
||||
"discardPileIsPublic":true,
|
||||
"set":[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 100, 101],
|
||||
"erratasets": [
|
||||
51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69
|
||||
|
||||
@@ -40,6 +40,7 @@ public class JSONDefs {
|
||||
public boolean cancelRingBearerSkirmish = false;
|
||||
public boolean ruleOfFour = true;
|
||||
public boolean winAtEndOfRegroup = false;
|
||||
public boolean discardPileIsPublic = false;
|
||||
public boolean winOnControlling5Sites = false;
|
||||
public boolean playtest = false;
|
||||
public boolean validateShadowFPCount = true;
|
||||
|
||||
@@ -7,13 +7,14 @@ public enum Zone implements Filterable {
|
||||
|
||||
// Public knowledge but not in play
|
||||
STACKED("stacked", true, true, false), DEAD("dead pile", true, true, false),
|
||||
REMOVED("removed", true, true, false),
|
||||
|
||||
// Private knowledge
|
||||
HAND("hand", false, true, false), DISCARD("discard", false, true, false),
|
||||
ADVENTURE_DECK("adventureDeck", false, true, false),
|
||||
|
||||
// Nobody sees
|
||||
VOID("void", false, false, false), VOID_FROM_HAND("voidFromHand", false, false, false), DECK("deck", false, false, false), REMOVED("removed", false, false, false);
|
||||
VOID("void", false, false, false), VOID_FROM_HAND("voidFromHand", false, false, false), DECK("deck", false, false, false);
|
||||
|
||||
private final String _humanReadable;
|
||||
private final boolean _public;
|
||||
|
||||
@@ -16,7 +16,7 @@ public interface GameStateListener {
|
||||
|
||||
public void cardsRemoved(String playerPerforming, Collection<PhysicalCard> cards);
|
||||
|
||||
public void setPlayerOrder(List<String> playerIds);
|
||||
public void initializeBoard(List<String> playerIds, boolean discardIsPublic);
|
||||
|
||||
public void setPlayerPosition(String playerId, int i);
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ public interface LotroFormat {
|
||||
|
||||
public boolean winWhenShadowReconciles();
|
||||
|
||||
public boolean discardPileIsPublic();
|
||||
|
||||
public boolean winOnControlling5Sites();
|
||||
|
||||
public boolean isPlaytest();
|
||||
|
||||
@@ -46,6 +46,8 @@ public class EventSerializer {
|
||||
eventElem.setAttribute("message", gameEvent.getMessage());
|
||||
if (gameEvent.getVersion() != null)
|
||||
eventElem.setAttribute("version", gameEvent.getVersion().toString());
|
||||
if (gameEvent.getType() == GameEvent.Type.PARTICIPANTS)
|
||||
eventElem.setAttribute("discardPublic", String.valueOf(gameEvent.isDiscardPublic()));
|
||||
if (gameEvent.getGameStats() != null)
|
||||
serializeGameStats(doc, eventElem, gameEvent.getGameStats());
|
||||
if (gameEvent.getAwaitingDecision() != null)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.gempukku.lotro.game.state;
|
||||
|
||||
import com.gempukku.lotro.common.Token;
|
||||
import com.gempukku.lotro.common.Zone;
|
||||
import com.gempukku.lotro.communication.GameStateListener;
|
||||
import com.gempukku.lotro.game.LotroFormat;
|
||||
import com.gempukku.lotro.game.PhysicalCard;
|
||||
import com.gempukku.lotro.logic.decisions.AwaitingDecision;
|
||||
import com.gempukku.lotro.logic.timing.GameStats;
|
||||
@@ -19,9 +21,12 @@ public class GameCommunicationChannel implements GameStateListener, LongPollable
|
||||
private final int _channelNumber;
|
||||
private volatile WaitingRequest _waitingRequest;
|
||||
|
||||
public GameCommunicationChannel(String self, int channelNumber) {
|
||||
private final LotroFormat _format;
|
||||
|
||||
public GameCommunicationChannel(String self, int channelNumber, LotroFormat format) {
|
||||
_self = self;
|
||||
_channelNumber = channelNumber;
|
||||
_format = format;
|
||||
}
|
||||
|
||||
public int getChannelNumber() {
|
||||
@@ -29,10 +34,14 @@ public class GameCommunicationChannel implements GameStateListener, LongPollable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOrder(List<String> participants) {
|
||||
public void initializeBoard(List<String> participants, boolean discardIsPublic) {
|
||||
List<String> participantIds = new LinkedList<>();
|
||||
participantIds.addAll(participants);
|
||||
appendEvent(new GameEvent(PARTICIPANTS).participantId(_self).allParticipantIds(participantIds));
|
||||
appendEvent(new GameEvent(PARTICIPANTS)
|
||||
.participantId(_self)
|
||||
.allParticipantIds(participantIds)
|
||||
.discardPublic(discardIsPublic)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -107,7 +116,8 @@ public class GameCommunicationChannel implements GameStateListener, LongPollable
|
||||
|
||||
@Override
|
||||
public void cardCreated(PhysicalCard card) {
|
||||
if (card.getZone().isPublic() || (card.getZone().isVisibleByOwner() && card.getOwner().equals(_self)))
|
||||
boolean publicDiscard = card.getZone() == Zone.DISCARD && _format.discardPileIsPublic();
|
||||
if (card.getZone().isPublic() || publicDiscard || (card.getZone().isVisibleByOwner() && card.getOwner().equals(_self)))
|
||||
appendEvent(new GameEvent(PUT_CARD_INTO_PLAY).card(card));
|
||||
}
|
||||
|
||||
@@ -120,7 +130,8 @@ public class GameCommunicationChannel implements GameStateListener, LongPollable
|
||||
public void cardsRemoved(String playerPerforming, Collection<PhysicalCard> cards) {
|
||||
Set<PhysicalCard> removedCardsVisibleByPlayer = new HashSet<>();
|
||||
for (PhysicalCard card : cards) {
|
||||
if (card.getZone().isPublic() || (card.getZone().isVisibleByOwner() && card.getOwner().equals(_self)))
|
||||
boolean publicDiscard = card.getZone() == Zone.DISCARD && _format.discardPileIsPublic();
|
||||
if (card.getZone().isPublic() || publicDiscard || (card.getZone().isVisibleByOwner() && card.getOwner().equals(_self)))
|
||||
removedCardsVisibleByPlayer.add(card);
|
||||
}
|
||||
if (removedCardsVisibleByPlayer.size() > 0)
|
||||
|
||||
@@ -57,6 +57,7 @@ public class GameEvent {
|
||||
private AwaitingDecision _awaitingDecision;
|
||||
private ZonedDateTime _timestamp;
|
||||
private Integer _version;
|
||||
private boolean _discardIsPublic;
|
||||
|
||||
public GameEvent(Type type) {
|
||||
_type = type;
|
||||
@@ -241,4 +242,13 @@ public class GameEvent {
|
||||
_phase = phase;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isDiscardPublic() {
|
||||
return _discardIsPublic;
|
||||
}
|
||||
|
||||
public GameEvent discardPublic(boolean discardPublic) {
|
||||
_discardIsPublic = discardPublic;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ public class GameState {
|
||||
private static final int LAST_MESSAGE_STORED_COUNT = 15;
|
||||
private PlayerOrder _playerOrder;
|
||||
|
||||
private LotroFormat _format;
|
||||
|
||||
private final Map<String, List<PhysicalCardImpl>> _adventureDecks = new HashMap<>();
|
||||
private final Map<String, List<PhysicalCardImpl>> _decks = new HashMap<>();
|
||||
private final Map<String, List<PhysicalCardImpl>> _hands = new HashMap<>();
|
||||
@@ -67,9 +69,10 @@ public class GameState {
|
||||
return _nextCardId++;
|
||||
}
|
||||
|
||||
public void init(PlayerOrder playerOrder, String firstPlayer, Map<String, List<String>> cards, Map<String, String> ringBearers, Map<String, String> rings, LotroCardBlueprintLibrary library, GameStats gameStats) {
|
||||
public void init(PlayerOrder playerOrder, String firstPlayer, Map<String, List<String>> cards, Map<String, String> ringBearers, Map<String, String> rings, LotroCardBlueprintLibrary library, LotroFormat format) {
|
||||
_playerOrder = playerOrder;
|
||||
_currentPlayerId = firstPlayer;
|
||||
_format = format;
|
||||
|
||||
for (Map.Entry<String, List<String>> stringListEntry : cards.entrySet()) {
|
||||
String playerId = stringListEntry.getKey();
|
||||
@@ -101,7 +104,7 @@ public class GameState {
|
||||
}
|
||||
|
||||
for (GameStateListener listener : getAllGameStateListeners()) {
|
||||
listener.setPlayerOrder(playerOrder.getAllPlayers());
|
||||
listener.initializeBoard(playerOrder.getAllPlayers(), format.discardPileIsPublic());
|
||||
}
|
||||
|
||||
//This needs done after the Player Order initialization has been issued, or else the player
|
||||
@@ -198,7 +201,7 @@ public class GameState {
|
||||
|
||||
private void sendStateToPlayer(String playerId, GameStateListener listener, GameStats gameStats) {
|
||||
if (_playerOrder != null) {
|
||||
listener.setPlayerOrder(_playerOrder.getAllPlayers());
|
||||
listener.initializeBoard(_playerOrder.getAllPlayers(), _format.discardPileIsPublic());
|
||||
if (_currentPlayerId != null)
|
||||
listener.setCurrentPlayerId(_currentPlayerId);
|
||||
if (_currentPhase != null)
|
||||
|
||||
@@ -80,45 +80,54 @@ public class PlayerReconcilesAction implements Action {
|
||||
@Override
|
||||
public Effect nextEffect(LotroGame game) {
|
||||
if (_effectQueue == null) {
|
||||
game.getGameState().sendMessage(_playerId + " reconciles");
|
||||
|
||||
_effectQueue = new LinkedList<>();
|
||||
|
||||
final int handSize = game.getFormat().getHandSize();
|
||||
|
||||
GameState gameState = _game.getGameState();
|
||||
final Set<? extends PhysicalCard> cardsInHand = new HashSet<PhysicalCard>(gameState.getHand(_playerId));
|
||||
if (cardsInHand.size() > handSize) {
|
||||
_effectQueue.add(new PlayoutDecisionEffect(_playerId,
|
||||
new CardsSelectionDecision(1, "Choose cards to discard down to "+handSize, cardsInHand, cardsInHand.size() - handSize, cardsInHand.size() - handSize) {
|
||||
@Override
|
||||
public void decisionMade(String result) throws DecisionResultInvalidException {
|
||||
Set<PhysicalCard> cards = getSelectedCardsByResponse(result);
|
||||
_effectQueue.add(new DiscardCardsFromHandEffect(null, _playerId, cards, false));
|
||||
_effectQueue.add(
|
||||
new TriggeringResultEffect(new ReconcileResult(_playerId), "Player reconciled"));
|
||||
}
|
||||
}));
|
||||
} else if (cardsInHand.size() > 0) {
|
||||
_effectQueue.add(new PlayoutDecisionEffect(_playerId,
|
||||
new CardsSelectionDecision(1, "Reconcile - choose card to discard or press DONE", cardsInHand, 0, 1) {
|
||||
@Override
|
||||
public void decisionMade(String result) throws DecisionResultInvalidException {
|
||||
Set<PhysicalCard> selectedCards = getSelectedCardsByResponse(result);
|
||||
if (selectedCards.size() > 0) {
|
||||
_effectQueue.add(new DiscardCardsFromHandEffect(null, _playerId, selectedCards, false));
|
||||
|
||||
if(game.getFormat().winWhenShadowReconciles() && game.getGameState().getCurrentPhase() == Phase.REGROUP
|
||||
&& game.getGameState().getCurrentSiteNumber() == 9) {
|
||||
game.getGameState().sendMessage("End of regroup phase reached.");
|
||||
}
|
||||
else {
|
||||
game.getGameState().sendMessage(_playerId + " reconciles");
|
||||
|
||||
if (cardsInHand.size() > handSize) {
|
||||
_effectQueue.add(new PlayoutDecisionEffect(_playerId,
|
||||
new CardsSelectionDecision(1, "Choose cards to discard down to "+handSize, cardsInHand, cardsInHand.size() - handSize, cardsInHand.size() - handSize) {
|
||||
@Override
|
||||
public void decisionMade(String result) throws DecisionResultInvalidException {
|
||||
Set<PhysicalCard> cards = getSelectedCardsByResponse(result);
|
||||
_effectQueue.add(new DiscardCardsFromHandEffect(null, _playerId, cards, false));
|
||||
_effectQueue.add(
|
||||
new TriggeringResultEffect(new ReconcileResult(_playerId), "Player reconciled"));
|
||||
}
|
||||
int cardsInHandAfterDiscard = cardsInHand.size() - selectedCards.size();
|
||||
if (cardsInHandAfterDiscard < handSize) {
|
||||
_effectQueue.add(new DrawCardsEffect(PlayerReconcilesAction.this, _playerId, handSize - cardsInHandAfterDiscard));
|
||||
}));
|
||||
} else if (cardsInHand.size() > 0) {
|
||||
_effectQueue.add(new PlayoutDecisionEffect(_playerId,
|
||||
new CardsSelectionDecision(1, "Reconcile - choose card to discard or press DONE", cardsInHand, 0, 1) {
|
||||
@Override
|
||||
public void decisionMade(String result) throws DecisionResultInvalidException {
|
||||
Set<PhysicalCard> selectedCards = getSelectedCardsByResponse(result);
|
||||
if (selectedCards.size() > 0) {
|
||||
_effectQueue.add(new DiscardCardsFromHandEffect(null, _playerId, selectedCards, false));
|
||||
}
|
||||
int cardsInHandAfterDiscard = cardsInHand.size() - selectedCards.size();
|
||||
if (cardsInHandAfterDiscard < handSize) {
|
||||
_effectQueue.add(new DrawCardsEffect(PlayerReconcilesAction.this, _playerId, handSize - cardsInHandAfterDiscard));
|
||||
}
|
||||
_effectQueue.add(
|
||||
new TriggeringResultEffect(new ReconcileResult(_playerId), "Player reconciled"));
|
||||
}
|
||||
_effectQueue.add(
|
||||
new TriggeringResultEffect(new ReconcileResult(_playerId), "Player reconciled"));
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
_effectQueue.add(new DrawCardsEffect(PlayerReconcilesAction.this, _playerId, handSize));
|
||||
_effectQueue.add(
|
||||
new TriggeringResultEffect(new ReconcileResult(_playerId), "Player reconciled"));
|
||||
}));
|
||||
} else {
|
||||
_effectQueue.add(new DrawCardsEffect(PlayerReconcilesAction.this, _playerId, handSize));
|
||||
_effectQueue.add(
|
||||
new TriggeringResultEffect(new ReconcileResult(_playerId), "Player reconciled"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ public class DefaultLotroGame implements LotroGame {
|
||||
@Override
|
||||
public void setPlayerOrder(PlayerOrder playerOrder, String firstPlayer) {
|
||||
final GameStats gameStats = _turnProcedure.getGameStats();
|
||||
_gameState.init(playerOrder, firstPlayer, cards, ringBearers, rings, library, gameStats);
|
||||
_gameState.init(playerOrder, firstPlayer, cards, ringBearers, rings, library, format);
|
||||
}
|
||||
}, characterDeathRule);
|
||||
_userFeedback = userFeedback;
|
||||
|
||||
@@ -31,7 +31,7 @@ public class WinConditionRule {
|
||||
if (isWinAtReconcile(game)) {
|
||||
if (effectResults.getType() == EffectResult.Type.RECONCILE
|
||||
&& !((ReconcileResult) effectResults).getPlayerId().equals(game.getGameState().getCurrentPlayerId())) {
|
||||
game.playerWon(game.getGameState().getCurrentPlayerId(), "Surviving till Shadow player reconciles on site 9");
|
||||
game.playerWon(game.getGameState().getCurrentPlayerId(), "Surviving to end of Regroup phase on site 9");
|
||||
}
|
||||
} else if (effectResults.getType() == EffectResult.Type.START_OF_PHASE) {
|
||||
game.playerWon(game.getGameState().getCurrentPlayerId(), "Surviving to Regroup phase on site 9");
|
||||
|
||||
@@ -49,7 +49,7 @@ public class GameRecorder {
|
||||
final ZonedDateTime startDate = ZonedDateTime.now(ZoneOffset.UTC);
|
||||
final Map<String, GameCommunicationChannel> recordingChannels = new HashMap<>();
|
||||
for (String playerId : lotroGame.getPlayersPlaying()) {
|
||||
var recordChannel = new GameCommunicationChannel(playerId, 0);
|
||||
var recordChannel = new GameCommunicationChannel(playerId, 0, format);
|
||||
lotroGame.addGameStateListener(playerId, recordChannel);
|
||||
recordingChannels.put(playerId, recordChannel);
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ public class LotroGameMediator {
|
||||
int number = _channelNextIndex;
|
||||
_channelNextIndex++;
|
||||
|
||||
GameCommunicationChannel participantCommunicationChannel = new GameCommunicationChannel(playerName, number);
|
||||
GameCommunicationChannel participantCommunicationChannel = new GameCommunicationChannel(playerName, number, _lotroGame.getFormat());
|
||||
_communicationChannels.put(playerName, participantCommunicationChannel);
|
||||
|
||||
_lotroGame.addGameStateListener(playerName, participantCommunicationChannel);
|
||||
|
||||
@@ -24,6 +24,7 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
private final boolean _canCancelRingBearerSkirmish;
|
||||
private final boolean _hasRuleOfFour;
|
||||
private final boolean _winAtEndOfRegroup;
|
||||
private final boolean _discardPileIsPublic;
|
||||
private final boolean _winOnControlling5Sites;
|
||||
private int _minimumDeckSize = 60;
|
||||
private final List<String> _bannedCards = new ArrayList<>();
|
||||
@@ -42,7 +43,7 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
public DefaultLotroFormat(AdventureLibrary adventureLibrary, LotroCardBlueprintLibrary library, JSONDefs.Format def) throws InvalidPropertiesFormatException{
|
||||
this(library, adventureLibrary.getAdventure(def.adventure), def.name, def.code, def.order, def.surveyUrl, SitesBlock.valueOf(def.sites),
|
||||
def.validateShadowFPCount, def.minimumDeckSize, def.maximumSameName, def.mulliganRule, def.cancelRingBearerSkirmish,
|
||||
def.ruleOfFour, def.winAtEndOfRegroup, def.winOnControlling5Sites, def.playtest, def.hall);
|
||||
def.ruleOfFour, def.winAtEndOfRegroup, def.discardPileIsPublic, def.winOnControlling5Sites, def.playtest, def.hall);
|
||||
|
||||
if(def.set != null)
|
||||
def.set.forEach(this::addValidSet);
|
||||
@@ -71,8 +72,8 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
Adventure adventure, String name, String code, int order, String surveyUrl,
|
||||
SitesBlock siteBlock,
|
||||
boolean validateShadowFPCount, int minimumDeckSize, int maximumSameName, boolean mulliganRule,
|
||||
boolean canCancelRingBearerSkirmish, boolean hasRuleOfFour, boolean winAtEndOfRegroup, boolean winOnControlling5Sites,
|
||||
boolean playtest, boolean hallVisible) {
|
||||
boolean canCancelRingBearerSkirmish, boolean hasRuleOfFour, boolean winAtEndOfRegroup, boolean discardPileIsPublic,
|
||||
boolean winOnControlling5Sites, boolean playtest, boolean hallVisible) {
|
||||
_adventure = adventure;
|
||||
_library = library;
|
||||
_name = name;
|
||||
@@ -87,6 +88,7 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
_canCancelRingBearerSkirmish = canCancelRingBearerSkirmish;
|
||||
_hasRuleOfFour = hasRuleOfFour;
|
||||
_winAtEndOfRegroup = winAtEndOfRegroup;
|
||||
_discardPileIsPublic = discardPileIsPublic;
|
||||
_winOnControlling5Sites = winOnControlling5Sites;
|
||||
_isPlaytest = playtest;
|
||||
_hallVisible = hallVisible;
|
||||
@@ -129,6 +131,9 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
return _winAtEndOfRegroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean discardPileIsPublic() { return _discardPileIsPublic; }
|
||||
|
||||
@Override
|
||||
public boolean canCancelRingBearerSkirmish() {
|
||||
return _canCancelRingBearerSkirmish;
|
||||
@@ -717,6 +722,7 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
cancelRingBearerSkirmish = _canCancelRingBearerSkirmish;
|
||||
ruleOfFour = _hasRuleOfFour;
|
||||
winAtEndOfRegroup = _winAtEndOfRegroup;
|
||||
discardPileIsPublic = _discardPileIsPublic;
|
||||
winOnControlling5Sites = _winOnControlling5Sites;
|
||||
playtest = _isPlaytest;
|
||||
validateShadowFPCount = _validateShadowFPCount;
|
||||
|
||||
@@ -94,6 +94,42 @@ public class Card_01_055_ErrataTests
|
||||
scn.FreepsMoveCharToTable(galadriel);
|
||||
scn.FreepsMoveCardToSupportArea(mirror);
|
||||
|
||||
PhysicalCardImpl card1 = scn.GetShadowCard("allyHome3_1");
|
||||
PhysicalCardImpl card2 = scn.GetShadowCard("allyHome6_1");
|
||||
PhysicalCardImpl card3 = scn.GetShadowCard("galadriel");
|
||||
scn.ShadowMoveCardToHand(card1, card2, card3);
|
||||
scn.ShadowMoveCharToTable("runner");
|
||||
|
||||
scn.StartGame();
|
||||
|
||||
scn.SkipToPhase(Phase.MANEUVER);
|
||||
assertTrue(scn.FreepsActionAvailable(mirror));
|
||||
assertEquals(0, scn.GetWoundsOn(galadriel));
|
||||
assertEquals(3, scn.GetShadowHandCount());
|
||||
assertEquals(1, scn.GetShadowDeckCount());
|
||||
assertEquals(Zone.HAND, card1.getZone());
|
||||
|
||||
scn.FreepsUseCardAction(mirror);
|
||||
assertEquals(3, scn.GetFreepsCardChoiceCount());
|
||||
scn.FreepsDismissRevealedCards();
|
||||
scn.ShadowDismissRevealedCards();
|
||||
scn.FreepsChooseCardBPFromSelection(card1);
|
||||
assertEquals(1, scn.GetWoundsOn(galadriel));
|
||||
assertEquals(2, scn.GetShadowHandCount());
|
||||
assertEquals(2, scn.GetShadowDeckCount());
|
||||
assertEquals(Zone.DECK, card1.getZone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ManeuverAbilityReveals3RandomCardsFromShadowHand() throws DecisionResultInvalidException, CardNotFoundException {
|
||||
//Pre-game setup
|
||||
GenericCardTestHelper scn = GetScenario();
|
||||
|
||||
PhysicalCardImpl mirror = scn.GetFreepsCard("mirror");
|
||||
PhysicalCardImpl galadriel = scn.GetFreepsCard("galadriel");
|
||||
scn.FreepsMoveCharToTable(galadriel);
|
||||
scn.FreepsMoveCardToSupportArea(mirror);
|
||||
|
||||
PhysicalCardImpl card1 = scn.GetShadowCard("allyHome3_1");
|
||||
PhysicalCardImpl card2 = scn.GetShadowCard("allyHome6_1");
|
||||
PhysicalCardImpl card3 = scn.GetShadowCard("galadriel");
|
||||
@@ -114,11 +150,6 @@ public class Card_01_055_ErrataTests
|
||||
assertEquals(3, scn.GetFreepsCardChoiceCount());
|
||||
scn.FreepsDismissRevealedCards();
|
||||
scn.ShadowDismissRevealedCards();
|
||||
scn.FreepsChooseCardBPFromSelection(card1);
|
||||
assertEquals(1, scn.GetWoundsOn(galadriel));
|
||||
assertEquals(3, scn.GetShadowHandCount());
|
||||
assertEquals(1, scn.GetShadowDeckCount());
|
||||
assertEquals(Zone.DECK, card1.getZone());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user