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:
Christian 'ketura' McCarty
2023-05-13 17:37:02 -05:00
parent f604caa31a
commit e046cf1b8d
17 changed files with 246 additions and 127 deletions

View File

@@ -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");

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -18,6 +18,8 @@ public interface LotroFormat {
public boolean winWhenShadowReconciles();
public boolean discardPileIsPublic();
public boolean winOnControlling5Sites();
public boolean isPlaytest();

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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"));
}
}
}

View File

@@ -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;

View File

@@ -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");

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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());
}
}