Map cards now save and load and validate for pertinent formats
This commit is contained in:
@@ -140,7 +140,7 @@ public class DeckRequestHandler extends LotroServerRequestHandler implements Uri
|
||||
|
||||
int fpCount = 0;
|
||||
int shadowCount = 0;
|
||||
for (String card : deck.getAdventureCards()) {
|
||||
for (String card : deck.getDrawDeckCards()) {
|
||||
Side side = _library.getLotroCardBlueprint(card).getSide();
|
||||
if (side == Side.SHADOW)
|
||||
shadowCount++;
|
||||
@@ -369,7 +369,7 @@ public class DeckRequestHandler extends LotroServerRequestHandler implements Uri
|
||||
result.append("<b>Ring:</b> " + generateCardTooltip(_library.getLotroCardBlueprint(ring), ring) + "<br/>");
|
||||
|
||||
DefaultCardCollection deckCards = new DefaultCardCollection();
|
||||
for (String card : deck.getAdventureCards())
|
||||
for (String card : deck.getDrawDeckCards())
|
||||
deckCards.addItem(_library.getBaseBlueprintId(card), 1);
|
||||
for (String site : deck.getSites())
|
||||
deckCards.addItem(_library.getBaseBlueprintId(site), 1);
|
||||
@@ -577,13 +577,19 @@ public class DeckRequestHandler extends LotroServerRequestHandler implements Uri
|
||||
deckElem.appendChild(ring);
|
||||
}
|
||||
|
||||
if (deck.getMap() != null) {
|
||||
Element map = doc.createElement("map");
|
||||
map.setAttribute("blueprintId", deck.getMap());
|
||||
deckElem.appendChild(map);
|
||||
}
|
||||
|
||||
for (CardItem cardItem : _sortAndFilterCards.process("sort:siteNumber,twilight", createCardItems(deck.getSites()), _library, _formatLibrary)) {
|
||||
Element site = doc.createElement("site");
|
||||
site.setAttribute("blueprintId", cardItem.getBlueprintId());
|
||||
deckElem.appendChild(site);
|
||||
}
|
||||
|
||||
for (CardItem cardItem : _sortAndFilterCards.process("sort:cardType,culture,name", createCardItems(deck.getAdventureCards()), _library, _formatLibrary)) {
|
||||
for (CardItem cardItem : _sortAndFilterCards.process("sort:cardType,culture,name", createCardItems(deck.getDrawDeckCards()), _library, _formatLibrary)) {
|
||||
Element card = doc.createElement("card");
|
||||
String side;
|
||||
try {
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
Map
|
||||
<div class="helper-text" >
|
||||
(Click here to select your Map.)
|
||||
<br/><br/>(Maps are now required for PC-Movie decks. They determine what site path you will use.)
|
||||
<br/><br/><i><a href="https://lotrtcgpc.net" target="_blank">See here for details.</a></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -623,8 +623,12 @@ var PCCards = {
|
||||
//Aragorn,Elessar Telcontar FA
|
||||
'10_127' : 'https://i.lotrtcgpc.net/promos/LOTR-EN10U025.0_card.jpg',
|
||||
//Arwen, Queen of Elves and Men FA
|
||||
'10_128' : 'https://i.lotrtcgpc.net/promos/LOTR-EN10U006.0_card.jpg'
|
||||
'10_128' : 'https://i.lotrtcgpc.net/promos/LOTR-EN10U006.0_card.jpg',
|
||||
|
||||
|
||||
//Maps
|
||||
'100_2' : 'https://i.lotrtcgpc.net/sets/vset0/LOTR-ENV0S002.0_card.jpg',
|
||||
'100_3' : 'https://i.lotrtcgpc.net/sets/vset0/LOTR-ENV0S003.0_card.jpg',
|
||||
'100_4' : 'https://i.lotrtcgpc.net/sets/vset0/LOTR-ENV0S004.0_card.jpg'
|
||||
}
|
||||
|
||||
|
||||
@@ -842,6 +842,15 @@ var GempLotrDeckBuildingUI = Class.extend({
|
||||
cards.push($(this).data("card").blueprintId);
|
||||
});
|
||||
result += cards;
|
||||
|
||||
let formatCode = this.formatSelect.val();
|
||||
if(formatCode == "pc_movie" || formatCode == "test_pc_movie") {
|
||||
result += "|";
|
||||
var map = $(".card", this.mapDiv);
|
||||
if (map.length > 0)
|
||||
result += map.data("card").blueprintId;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
},
|
||||
@@ -1075,6 +1084,10 @@ var GempLotrDeckBuildingUI = Class.extend({
|
||||
if (ring.length > 0)
|
||||
this.addCardToContainer(ring[0].getAttribute("blueprintId"), "deck", this.ringDiv, false).addClass("cardInDeck");
|
||||
|
||||
var map = root.getElementsByTagName("map");
|
||||
if (map.length > 0)
|
||||
this.addCardToContainer(map[0].getAttribute("blueprintId"), "deck", this.mapDiv, false).addClass("cardInDeck");
|
||||
|
||||
var sites = root.getElementsByTagName("site");
|
||||
for (var i = 0; i < sites.length; i++)
|
||||
this.addCardToContainer(sites[i].getAttribute("blueprintId"), "deck", this.siteDiv, false).addClass("cardInDeck");
|
||||
@@ -1158,7 +1171,7 @@ var GempLotrDeckBuildingUI = Class.extend({
|
||||
this.mapDiv.css({ display:"block", position:"absolute", left:padding, top:manageHeight + 2 * padding+ rowHeight, width:Math.floor((sitesWidth - padding) / 2), height:rowHeight });
|
||||
this.mapGroup.setBounds(0, 0, Math.floor((sitesWidth - padding) / 2), rowHeight);
|
||||
|
||||
this.siteDiv.css({ position:"absolute", left:padding, top:manageHeight + 3 * padding + 2 * rowHeight, width:sitesWidth, height:deckHeight - rowHeight - 2 * padding});
|
||||
this.siteDiv.css({ position:"absolute", left:padding, top:manageHeight + 3 * padding + 2 * rowHeight, width:sitesWidth, height:deckHeight - 2*rowHeight - 2 * padding});
|
||||
this.siteGroup.setBounds(0, 0, sitesWidth, deckHeight - 2*rowHeight - 2 * padding);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
{
|
||||
100_2: {
|
||||
cardInfo: {
|
||||
imagePath: sets/LOTR-ENV0S002.0.jpg
|
||||
javaClass: false
|
||||
version: 0
|
||||
collInfo: V0 2
|
||||
rarity: X
|
||||
setNum: V0
|
||||
cardNum: 2
|
||||
style: Standard
|
||||
}
|
||||
title: Path of the Fellowship
|
||||
unique: false
|
||||
type: Map
|
||||
twilight: 0
|
||||
effects: [
|
||||
{
|
||||
type: DeckBuildingRestriction
|
||||
pile: sites
|
||||
#We do it this way instead of checking for fellowship
|
||||
# so that the error message shows all sites that don't
|
||||
# match, instead of listing all the ones that do and
|
||||
# making the user figure out which one isn't listed.
|
||||
filter: or(siteblock(towers),siteblock(king),siteblock(shadows))
|
||||
max: 0
|
||||
text: Adventure Deck must only contain sites from the Fellowship block path.
|
||||
}
|
||||
]
|
||||
gametext: Before the game begins, play this to your support area. Your adventure deck must only contain cards from the Fellowship site path.
|
||||
lore: ""
|
||||
promotext: ""
|
||||
alts: {
|
||||
promos: {
|
||||
}
|
||||
errata: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
100_3: {
|
||||
cardInfo: {
|
||||
imagePath: sets/LOTR-ENV0S003.0.jpg
|
||||
javaClass: false
|
||||
version: 0
|
||||
collInfo: V0 3
|
||||
rarity: X
|
||||
setNum: V0
|
||||
cardNum: 3
|
||||
style: Standard
|
||||
}
|
||||
title: Road to the Towers
|
||||
unique: false
|
||||
type: Map
|
||||
twilight: 0
|
||||
effects: [
|
||||
{
|
||||
type: DeckBuildingRestriction
|
||||
pile: sites
|
||||
#We do it this way instead of checking for fellowship
|
||||
# so that the error message shows all sites that don't
|
||||
# match, instead of listing all the ones that do and
|
||||
# making the user figure out which one isn't listed.
|
||||
filter: or(siteblock(fellowship),siteblock(king),siteblock(shadows))
|
||||
max: 0
|
||||
text: Adventure deck must only contain sites from the Towers block site path.
|
||||
}
|
||||
]
|
||||
gametext: Before the game begins, play this to your support area. Your adventure deck must only contain cards from the Towers site path.
|
||||
lore: ""
|
||||
promotext: ""
|
||||
alts: {
|
||||
promos: {
|
||||
}
|
||||
errata: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
100_4: {
|
||||
cardInfo: {
|
||||
imagePath: sets/LOTR-ENV0S004.0.jpg
|
||||
javaClass: false
|
||||
//parentId: 1_319
|
||||
//One of: Variant, Errata, Reprint
|
||||
//parentType: Variant
|
||||
//parentPath: alts/promo
|
||||
version: 0
|
||||
collInfo: V0 4
|
||||
rarity: X
|
||||
setNum: V0
|
||||
cardNum: 4
|
||||
style: Standard
|
||||
}
|
||||
title: Journey of the King
|
||||
unique: false
|
||||
type: Map
|
||||
twilight: 0
|
||||
effects: [
|
||||
{
|
||||
type: DeckBuildingRestriction
|
||||
pile: sites
|
||||
#We do it this way instead of checking for fellowship
|
||||
# so that the error message shows all sites that don't
|
||||
# match, instead of listing all the ones that do and
|
||||
# making the user figure out which one isn't listed.
|
||||
filter: or(siteblock(fellowship),siteblock(towers),siteblock(shadows))
|
||||
max: 0
|
||||
text: Adventure Deck must only contain sites from the King block site path.
|
||||
}
|
||||
]
|
||||
gametext: Before the game begins, play this to your support area. Your adventure deck must only contain cards from the King site path.
|
||||
lore: ""
|
||||
promotext: ""
|
||||
alts: {
|
||||
promos: {
|
||||
}
|
||||
errata: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,11 +53,15 @@
|
||||
name: Movie Block (PC)
|
||||
code: pc_movie
|
||||
order: 4
|
||||
sites: KING
|
||||
sites: MULTIPATH
|
||||
usesMaps: true
|
||||
winAtEndOfRegroup: true
|
||||
discardPileIsPublic: true
|
||||
set: [
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 101
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 101
|
||||
]
|
||||
banned: [
|
||||
"100_1"
|
||||
]
|
||||
erratasets: [
|
||||
51,52,53,54,55,56,57,58,59,60
|
||||
@@ -595,6 +599,7 @@
|
||||
sites: MULTIPATH
|
||||
winAtEndOfRegroup: true
|
||||
discardPileIsPublic: true
|
||||
usesMaps: true
|
||||
hall: true
|
||||
playtest: true
|
||||
set: [
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
package com.gempukku.lotro.common;
|
||||
|
||||
public enum CardType implements Filterable {
|
||||
THE_ONE_RING, SITE, COMPANION, ALLY, MINION, POSSESSION, ARTIFACT, EVENT, CONDITION, FOLLOWER, ADVENTURE
|
||||
THE_ONE_RING,
|
||||
SITE,
|
||||
//Characters
|
||||
COMPANION, ALLY, MINION,
|
||||
//Items
|
||||
POSSESSION, ARTIFACT,
|
||||
EVENT,
|
||||
CONDITION,
|
||||
FOLLOWER,
|
||||
//Unused
|
||||
ADVENTURE,
|
||||
|
||||
//Player's Council card types
|
||||
MAP
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ public class JSONDefs {
|
||||
public int minimumDeckSize = 60;
|
||||
public int maximumSameName = 4;
|
||||
public boolean mulliganRule = true;
|
||||
public boolean usesMaps = false;
|
||||
public ArrayList<Integer> set;
|
||||
public ArrayList<String> banned = new ArrayList<>();
|
||||
public ArrayList<String> restricted = new ArrayList<>();
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.gempukku.lotro.common;
|
||||
|
||||
|
||||
/**
|
||||
* Represents the different kinds of deck piles that the player comes to
|
||||
* the table with.
|
||||
*/
|
||||
public enum PileType implements Filterable {
|
||||
FREE_PEOPLES, SHADOW, ADVENTURE, RING_BEARER, RING, MAP;
|
||||
|
||||
public static PileType Parse(String value) {
|
||||
value = value
|
||||
.toUpperCase()
|
||||
.replace(" ", "_")
|
||||
.replace("-", "_");
|
||||
|
||||
if(value.contains("FREEPS") || value.contains("FREE") || value.contains("PEOPLE"))
|
||||
return FREE_PEOPLES;
|
||||
|
||||
if(value.contains("SITE") || value.contains("ADVENTURE"))
|
||||
return ADVENTURE;
|
||||
|
||||
for (PileType type : values()) {
|
||||
if (type.toString().equalsIgnoreCase(value))
|
||||
return type;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.gempukku.lotro.common;
|
||||
|
||||
public record Result(boolean success, String message) {
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import com.gempukku.lotro.filters.Filters;
|
||||
import com.gempukku.lotro.game.ExtraPlayCost;
|
||||
import com.gempukku.lotro.game.LotroCardBlueprint;
|
||||
import com.gempukku.lotro.game.PhysicalCard;
|
||||
import com.gempukku.lotro.game.PhysicalCardImpl;
|
||||
import com.gempukku.lotro.game.state.LotroGame;
|
||||
import com.gempukku.lotro.logic.PlayUtils;
|
||||
import com.gempukku.lotro.logic.actions.*;
|
||||
@@ -82,8 +83,14 @@ public class BuiltLotroCardBlueprint implements LotroCardBlueprint {
|
||||
|
||||
private ExtraPossessionClassTest extraPossessionClassTest;
|
||||
|
||||
private PreGameDeckValidation deckValidation;
|
||||
|
||||
// Building methods
|
||||
|
||||
public void setDeckValidation(PreGameDeckValidation validation) {
|
||||
this.deckValidation = validation;
|
||||
}
|
||||
|
||||
public void setAllyHomeSites(SitesBlock block, int[] numbers) {
|
||||
this.allyHomeBlock = block;
|
||||
this.allyHomeSites = numbers;
|
||||
@@ -332,6 +339,14 @@ public class BuiltLotroCardBlueprint implements LotroCardBlueprint {
|
||||
|
||||
// Implemented methods
|
||||
|
||||
@Override
|
||||
public Result validatePreGameDeckCheck(List<PhysicalCardImpl> freeps, List<PhysicalCardImpl> shadow,
|
||||
List<PhysicalCardImpl> sites, PhysicalCardImpl rb, PhysicalCardImpl ring, PhysicalCardImpl map) {
|
||||
if(deckValidation != null)
|
||||
return deckValidation.validatePreGameDeckCheck(freeps, shadow, sites, rb, ring, map);
|
||||
|
||||
return new Result(true, null);
|
||||
}
|
||||
@Override
|
||||
public Side getSide() {
|
||||
return side;
|
||||
@@ -975,10 +990,10 @@ public class BuiltLotroCardBlueprint implements LotroCardBlueprint {
|
||||
throw new InvalidCardDefinitionException("Card has to have a title");
|
||||
if (cardType == null)
|
||||
throw new InvalidCardDefinitionException("Card has to have a type");
|
||||
if (cardType != CardType.THE_ONE_RING && cardType != CardType.SITE && side == null)
|
||||
throw new InvalidCardDefinitionException("Only The One Ring does not have a side defined");
|
||||
if (cardType != CardType.THE_ONE_RING && cardType != CardType.SITE && culture == null)
|
||||
throw new InvalidCardDefinitionException("Only The One Ring does not have a culture defined");
|
||||
if (cardType != CardType.THE_ONE_RING && cardType != CardType.SITE && cardType != CardType.MAP && side == null)
|
||||
throw new InvalidCardDefinitionException("All cards except The One Ring, Sites, and Maps must have a side defined");
|
||||
if (cardType != CardType.THE_ONE_RING && cardType != CardType.SITE && cardType != CardType.MAP && culture == null)
|
||||
throw new InvalidCardDefinitionException("All cards except The One Ring, Sites, and Maps have a culture defined");
|
||||
if (siteNumber != 0
|
||||
&& cardType != CardType.SITE
|
||||
&& cardType != CardType.MINION)
|
||||
|
||||
@@ -33,7 +33,7 @@ public class LotroCardBlueprintBuilder implements CardGenerationEnvironment {
|
||||
fieldProcessors.put("itemclass", new PossessionClassFieldProcessor());
|
||||
fieldProcessors.put("keyword", new KeywordFieldProcessor());
|
||||
fieldProcessors.put("keywords", new KeywordFieldProcessor());
|
||||
fieldProcessors.put("twilight", new CostFieldProcessor());
|
||||
fieldProcessors.put("twilight", new TwilightCostFieldProcessor());
|
||||
fieldProcessors.put("strength", new StrengthFieldProcessor());
|
||||
fieldProcessors.put("vitality", new VitalityFieldProcessor());
|
||||
fieldProcessors.put("resistance", new ResistanceFieldProcessor());
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.gempukku.lotro.cards.build;
|
||||
|
||||
import com.gempukku.lotro.common.Result;
|
||||
import com.gempukku.lotro.game.PhysicalCard;
|
||||
import com.gempukku.lotro.game.PhysicalCardImpl;
|
||||
import com.gempukku.lotro.game.state.LotroGame;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PreGameDeckValidation {
|
||||
Result validatePreGameDeckCheck(List<PhysicalCardImpl> freeps, List<PhysicalCardImpl> shadow,
|
||||
List<PhysicalCardImpl> sites, PhysicalCardImpl rb, PhysicalCardImpl ring, PhysicalCardImpl map);
|
||||
}
|
||||
@@ -22,6 +22,7 @@ public class EffectFieldProcessor implements FieldProcessor {
|
||||
effectProcessors.put("activatedtrigger", new ActivatedTriggerEffectProcessor());
|
||||
effectProcessors.put("aidcost", new AidCost());
|
||||
effectProcessors.put("copycard", new CopyCard());
|
||||
effectProcessors.put("deckbuildingrestriction", new DeckBuildingRestrictionGameTextProcessor());
|
||||
effectProcessors.put("discardedfromplaytrigger", new DiscardedFromPlayTriggerEffectProcessor());
|
||||
effectProcessors.put("discount", new PotentialDiscount());
|
||||
effectProcessors.put("event", new EventEffectProcessor());
|
||||
|
||||
@@ -21,7 +21,7 @@ public class FieldUtils {
|
||||
if (value == null)
|
||||
return defaultValue;
|
||||
if (!(value instanceof Number))
|
||||
throw new InvalidCardDefinitionException("Unknown type in " + key + " field");
|
||||
throw new InvalidCardDefinitionException("Value in " + key + " field must be an integer, not an evaluated value.");
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.gempukku.lotro.cards.build.CardGenerationEnvironment;
|
||||
import com.gempukku.lotro.cards.build.FieldProcessor;
|
||||
import com.gempukku.lotro.cards.build.InvalidCardDefinitionException;
|
||||
|
||||
public class CostFieldProcessor implements FieldProcessor {
|
||||
public class TwilightCostFieldProcessor implements FieldProcessor {
|
||||
@Override
|
||||
public void processField(String key, Object value, BuiltLotroCardBlueprint blueprint, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
blueprint.setCost(FieldUtils.getInteger(value, key));
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.gempukku.lotro.cards.build.field.effect;
|
||||
|
||||
import com.gempukku.lotro.cards.build.*;
|
||||
import com.gempukku.lotro.cards.build.field.EffectProcessor;
|
||||
import com.gempukku.lotro.cards.build.field.FieldUtils;
|
||||
import com.gempukku.lotro.common.PileType;
|
||||
import com.gempukku.lotro.common.Filterable;
|
||||
import com.gempukku.lotro.common.Result;
|
||||
import com.gempukku.lotro.filters.Filter;
|
||||
import com.gempukku.lotro.filters.Filters;
|
||||
import com.gempukku.lotro.game.LotroCardBlueprint;
|
||||
import com.gempukku.lotro.game.PhysicalCardImpl;
|
||||
import com.gempukku.lotro.logic.GameUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DeckBuildingRestrictionGameTextProcessor implements EffectProcessor {
|
||||
@Override
|
||||
public void processEffect(JSONObject effectObj, BuiltLotroCardBlueprint blueprint, CardGenerationEnvironment environment) throws InvalidCardDefinitionException {
|
||||
FieldUtils.validateAllowedFields(effectObj, "filter", "pile", "min", "max", "text");
|
||||
|
||||
final int max = FieldUtils.getInteger(effectObj.get("max"), "max", 1000);
|
||||
final int min = FieldUtils.getInteger(effectObj.get("max"), "min", 0);
|
||||
final String pileStr = FieldUtils.getString(effectObj.get("pile"), "pile");
|
||||
final PileType pileType = PileType.Parse(pileStr);
|
||||
final String failureMsg = FieldUtils.getString(effectObj.get("text"), "text");
|
||||
|
||||
if(StringUtils.isBlank(failureMsg))
|
||||
throw new InvalidCardDefinitionException("'text' field is required for deck validation operations to communicate to the player what validation they are faling in the deckbuilder. Ensure it is user friendly.");
|
||||
if(max < min)
|
||||
throw new InvalidCardDefinitionException("'max' field may not be less than 'min' field for deck validations.");
|
||||
if(min < 0)
|
||||
throw new InvalidCardDefinitionException("'min' field may not be less than zero for deck validations.");
|
||||
|
||||
final String filterStr = FieldUtils.getString(effectObj.get("filter"), "attachedTo", "any");
|
||||
final var filterSource = environment.getFilterFactory().generateFilter(filterStr, environment);
|
||||
|
||||
|
||||
|
||||
blueprint.setDeckValidation(
|
||||
(freeps, shadow, sites, rb, ring, map) -> {
|
||||
|
||||
var filter = Filters.and(filterSource.getFilterable(null));
|
||||
var found = new ArrayList<LotroCardBlueprint>();
|
||||
|
||||
if(pileType == null || pileType == PileType.FREE_PEOPLES) {
|
||||
found.addAll(validate(freeps, filter));
|
||||
}
|
||||
if(pileType == null || pileType == PileType.SHADOW) {
|
||||
found.addAll(validate(shadow, filter));
|
||||
}
|
||||
if(pileType == null || pileType == PileType.ADVENTURE) {
|
||||
found.addAll(validate(sites, filter));
|
||||
}
|
||||
if(pileType == null || pileType == PileType.RING_BEARER) {
|
||||
found.addAll(validate(Collections.singletonList(rb), filter));
|
||||
}
|
||||
if(pileType == null || pileType == PileType.RING) {
|
||||
found.addAll(validate(Collections.singletonList(ring), filter));
|
||||
}
|
||||
if(pileType == null || pileType == PileType.MAP) {
|
||||
found.addAll(validate(Collections.singletonList(map), filter));
|
||||
}
|
||||
|
||||
final String successMsg = "Deck meets restrictions placed by " + GameUtils.getFullName(blueprint) + ".";
|
||||
final String fullFailureMsg = "Deck does not meet restrictions placed by " + GameUtils.getFullName(blueprint) + ": "
|
||||
+ failureMsg + ". ";
|
||||
|
||||
if(found.size() > max || found.size() < min)
|
||||
return new Result(false, fullFailureMsg + "Found " + found.size() + ": "
|
||||
+ found.stream().map(GameUtils::getFullName).collect(Collectors.joining(", ")));
|
||||
|
||||
return new Result(true, successMsg);
|
||||
});
|
||||
|
||||
blueprint.setExtraPossessionClassTest(
|
||||
(game, self, attachedTo) -> {
|
||||
DefaultActionContext actionContext = new DefaultActionContext(self.getOwner(), game, self, null, null);
|
||||
final Filterable attachedFilterable = filterSource.getFilterable(actionContext);
|
||||
return Filters.and(attachedFilterable).accepts(game, attachedTo);
|
||||
});
|
||||
}
|
||||
|
||||
protected List<LotroCardBlueprint> validate(List<PhysicalCardImpl> cards, Filter filter) {
|
||||
var found = new ArrayList<LotroCardBlueprint>();
|
||||
for(var card : cards) {
|
||||
if(filter.accepts(null, card)) {
|
||||
found.add(card.getBlueprint());
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
}
|
||||
@@ -129,4 +129,9 @@ public interface LotroCardBlueprint {
|
||||
void appendAidCosts(LotroGame game, CostToEffectAction action, PhysicalCard self);
|
||||
|
||||
boolean skipUniquenessCheck();
|
||||
|
||||
default Result validatePreGameDeckCheck(List<PhysicalCardImpl> freeps, List<PhysicalCardImpl> shadow,
|
||||
List<PhysicalCardImpl> sites, PhysicalCardImpl rb, PhysicalCardImpl ring, PhysicalCardImpl map) {
|
||||
return new Result(true, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,61 +8,62 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface LotroFormat {
|
||||
public boolean isOrderedSites();
|
||||
boolean isOrderedSites();
|
||||
|
||||
public boolean canCancelRingBearerSkirmish();
|
||||
boolean canCancelRingBearerSkirmish();
|
||||
|
||||
public boolean hasRuleOfFour();
|
||||
boolean hasRuleOfFour();
|
||||
boolean usesMaps();
|
||||
|
||||
public boolean hasMulliganRule();
|
||||
boolean hasMulliganRule();
|
||||
|
||||
public boolean winWhenShadowReconciles();
|
||||
boolean winWhenShadowReconciles();
|
||||
|
||||
public boolean discardPileIsPublic();
|
||||
boolean discardPileIsPublic();
|
||||
|
||||
public boolean winOnControlling5Sites();
|
||||
boolean winOnControlling5Sites();
|
||||
|
||||
public boolean isPlaytest();
|
||||
public boolean hallVisible();
|
||||
boolean isPlaytest();
|
||||
boolean hallVisible();
|
||||
|
||||
public String getName();
|
||||
String getName();
|
||||
|
||||
public String getCode();
|
||||
public int getOrder();
|
||||
String getCode();
|
||||
int getOrder();
|
||||
|
||||
public String validateCard(String cardId);
|
||||
String validateCard(String cardId);
|
||||
|
||||
public List<String> validateDeck(LotroDeck deck);
|
||||
public String validateDeckForHall(LotroDeck deck);
|
||||
List<String> validateDeck(LotroDeck deck);
|
||||
String validateDeckForHall(LotroDeck deck);
|
||||
|
||||
public LotroDeck applyErrata(LotroDeck deck);
|
||||
LotroDeck applyErrata(LotroDeck deck);
|
||||
|
||||
public List<Integer> getValidSets();
|
||||
List<Integer> getValidSets();
|
||||
|
||||
public List<String> getBannedCards();
|
||||
List<String> getBannedCards();
|
||||
|
||||
public List<String> getRestrictedCards();
|
||||
List<String> getRestrictedCards();
|
||||
|
||||
public List<String> getValidCards();
|
||||
List<String> getValidCards();
|
||||
|
||||
public List<String> getLimit2Cards();
|
||||
List<String> getLimit2Cards();
|
||||
|
||||
public List<String> getLimit3Cards();
|
||||
List<String> getLimit3Cards();
|
||||
|
||||
public List<String> getRestrictedCardNames();
|
||||
List<String> getRestrictedCardNames();
|
||||
|
||||
public Map<String,String> getErrataCardMap();
|
||||
Map<String,String> getErrataCardMap();
|
||||
|
||||
public String applyErrata(String bpID);
|
||||
String applyErrata(String bpID);
|
||||
|
||||
public List<String> findBaseCards(String bpID);
|
||||
List<String> findBaseCards(String bpID);
|
||||
|
||||
public SitesBlock getSiteBlock();
|
||||
SitesBlock getSiteBlock();
|
||||
|
||||
public String getSurveyUrl();
|
||||
String getSurveyUrl();
|
||||
|
||||
public int getHandSize();
|
||||
int getHandSize();
|
||||
|
||||
public Adventure getAdventure();
|
||||
public JSONDefs.Format Serialize();
|
||||
Adventure getAdventure();
|
||||
JSONDefs.Format Serialize();
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class EventSerializer {
|
||||
deckElement.setAttribute("rb", deck.getRingBearer());
|
||||
deckElement.setAttribute("ring", deck.getRing());
|
||||
deckElement.setAttribute("sites", String.join(",", deck.getSites()));
|
||||
deckElement.setAttribute("deck", String.join(",", deck.getAdventureCards()));
|
||||
deckElement.setAttribute("deck", String.join(",", deck.getDrawDeckCards()));
|
||||
|
||||
eventElem.appendChild(deckElement);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class DefaultLotroGame implements LotroGame {
|
||||
|
||||
LotroDeck lotroDeck = decks.get(playerId);
|
||||
deck.addAll(lotroDeck.getSites());
|
||||
deck.addAll(lotroDeck.getAdventureCards());
|
||||
deck.addAll(lotroDeck.getDrawDeckCards());
|
||||
|
||||
cards.put(playerId, deck);
|
||||
ringBearers.put(playerId, lotroDeck.getRingBearer());
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.List;
|
||||
public class LotroDeck {
|
||||
private String _ringBearer;
|
||||
private String _ring;
|
||||
private String _map;
|
||||
private final List<String> _siteCards = new ArrayList<>();
|
||||
private final List<String> _nonSiteCards = new ArrayList<>();
|
||||
private final String _deckName;
|
||||
@@ -29,6 +30,7 @@ public class LotroDeck {
|
||||
public void setRing(String ring) {
|
||||
_ring = ring;
|
||||
}
|
||||
public void setMap(String map) { _map = map; }
|
||||
|
||||
public void addCard(String card) {
|
||||
_nonSiteCards.add(card);
|
||||
@@ -38,7 +40,7 @@ public class LotroDeck {
|
||||
_siteCards.add(card);
|
||||
}
|
||||
|
||||
public List<String> getAdventureCards() {
|
||||
public List<String> getDrawDeckCards() {
|
||||
return Collections.unmodifiableList(_nonSiteCards);
|
||||
}
|
||||
|
||||
@@ -54,6 +56,10 @@ public class LotroDeck {
|
||||
return _ring;
|
||||
}
|
||||
|
||||
public String getMap() {
|
||||
return _map;
|
||||
}
|
||||
|
||||
public String getTargetFormat() { return _targetFormat; }
|
||||
public void setTargetFormat(String value) { _targetFormat = value; }
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.gempukku.lotro.collection;
|
||||
|
||||
import com.gempukku.lotro.common.Names;
|
||||
import com.gempukku.lotro.competitive.PlayerStanding;
|
||||
import com.gempukku.lotro.game.*;
|
||||
import com.gempukku.lotro.game.formats.LotroFormatLibrary;
|
||||
import com.gempukku.lotro.logic.GameUtils;
|
||||
@@ -137,7 +136,7 @@ public class DeckRenderer {
|
||||
}
|
||||
|
||||
DefaultCardCollection deckCards = new DefaultCardCollection();
|
||||
for (String card : deck.getAdventureCards()) {
|
||||
for (String card : deck.getDrawDeckCards()) {
|
||||
deckCards.addItem(_bpLibrary.getBaseBlueprintId(card), 1);
|
||||
}
|
||||
for (String site : deck.getSites()) {
|
||||
@@ -216,7 +215,7 @@ public class DeckRenderer {
|
||||
}
|
||||
|
||||
DefaultCardCollection deckCards = new DefaultCardCollection();
|
||||
for (String card : deck.getAdventureCards()) {
|
||||
for (String card : deck.getDrawDeckCards()) {
|
||||
deckCards.addItem(_bpLibrary.getBaseBlueprintId(card), 1);
|
||||
}
|
||||
for (String site : deck.getSites()) {
|
||||
|
||||
@@ -17,10 +17,14 @@ public class DeckSerialization {
|
||||
sb.append(deck.getSites().get(i));
|
||||
}
|
||||
sb.append("|");
|
||||
for (int i = 0; i < deck.getAdventureCards().size(); i++) {
|
||||
for (int i = 0; i < deck.getDrawDeckCards().size(); i++) {
|
||||
if (i > 0)
|
||||
sb.append(",");
|
||||
sb.append(deck.getAdventureCards().get(i));
|
||||
sb.append(deck.getDrawDeckCards().get(i));
|
||||
}
|
||||
if (deck.getMap() != null) {
|
||||
sb.append("|");
|
||||
sb.append(deck.getMap());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
@@ -47,6 +51,11 @@ public class DeckSerialization {
|
||||
if (!card.equals(""))
|
||||
deck.addCard(card);
|
||||
}
|
||||
if (parts.length > 4)
|
||||
for (String card : parts[4].split(",")) {
|
||||
if (!card.equals(""))
|
||||
deck.setMap(card);
|
||||
}
|
||||
|
||||
return deck;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class CollectionUtils {
|
||||
incrementCardCount(counts, ringBearer, 1);
|
||||
for (String site : deck.getSites())
|
||||
incrementCardCount(counts, site, 1);
|
||||
for (String adventureCard : deck.getAdventureCards())
|
||||
for (String adventureCard : deck.getDrawDeckCards())
|
||||
incrementCardCount(counts, adventureCard, 1);
|
||||
return counts;
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ public class GameRecorder {
|
||||
deckElement.setAttribute("rb", deck.getRingBearer());
|
||||
deckElement.setAttribute("ring", deck.getRing());
|
||||
deckElement.setAttribute("sites", String.join(",", deck.getSites()));
|
||||
deckElement.setAttribute("deck", String.join(",", deck.getAdventureCards()));
|
||||
deckElement.setAttribute("deck", String.join(",", deck.getDrawDeckCards()));
|
||||
|
||||
info.appendChild(deckElement);
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ public class LotroServer extends AbstractServer {
|
||||
cnt++;
|
||||
}
|
||||
|
||||
if (cnt != 3)
|
||||
if (cnt < 3 || cnt > 4)
|
||||
return null;
|
||||
|
||||
return _deckDao.buildDeckFromContents(deckName, contents, targetFormat, notes);
|
||||
|
||||
@@ -57,7 +57,7 @@ public class ReplayMetadata {
|
||||
TargetFormat = deck.getTargetFormat();
|
||||
DeckName = deck.getDeckName();
|
||||
AdventureDeck = deck.getSites();
|
||||
DrawDeck = deck.getAdventureCards();
|
||||
DrawDeck = deck.getDrawDeckCards();
|
||||
RingBearer = deck.getRingBearer();
|
||||
Ring = deck.getRing();
|
||||
}};
|
||||
|
||||
@@ -21,6 +21,7 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
private boolean _validateShadowFPCount = true;
|
||||
private int _maximumSameName = 4;
|
||||
private final boolean _mulliganRule;
|
||||
private final boolean _usesMaps;
|
||||
private final boolean _canCancelRingBearerSkirmish;
|
||||
private final boolean _hasRuleOfFour;
|
||||
private final boolean _winAtEndOfRegroup;
|
||||
@@ -40,10 +41,28 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
private final List<String> _limit3Cards = new ArrayList<>();
|
||||
private final Map<String,String> _errataCardMap = new TreeMap<>();
|
||||
|
||||
|
||||
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.findBlock(def.sites),
|
||||
def.validateShadowFPCount, def.minimumDeckSize, def.maximumSameName, def.mulliganRule, def.cancelRingBearerSkirmish,
|
||||
def.ruleOfFour, def.winAtEndOfRegroup, def.discardPileIsPublic, def.winOnControlling5Sites, def.playtest, def.hall);
|
||||
|
||||
_adventure = adventureLibrary.getAdventure(def.adventure);
|
||||
_library = library;
|
||||
_name = def.name;
|
||||
_code = def.code;
|
||||
_order = def.order;
|
||||
_surveyUrl = def.surveyUrl;
|
||||
_siteBlock = SitesBlock.findBlock(def.sites);
|
||||
_validateShadowFPCount = def.validateShadowFPCount;
|
||||
_minimumDeckSize = def.minimumDeckSize;
|
||||
_maximumSameName = def.maximumSameName;
|
||||
_mulliganRule = def.mulliganRule;
|
||||
_usesMaps = def.usesMaps;
|
||||
_canCancelRingBearerSkirmish = def.cancelRingBearerSkirmish;
|
||||
_hasRuleOfFour = def.ruleOfFour;
|
||||
_winAtEndOfRegroup = def.winAtEndOfRegroup;
|
||||
_discardPileIsPublic = def.discardPileIsPublic;
|
||||
_winOnControlling5Sites = def.winOnControlling5Sites;
|
||||
_isPlaytest = def.playtest;
|
||||
_hallVisible = def.hall;
|
||||
|
||||
if(def.set != null)
|
||||
def.set.forEach(this::addValidSet);
|
||||
@@ -68,32 +87,6 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
def.errata.forEach(this::addCardErrata);
|
||||
}
|
||||
|
||||
public DefaultLotroFormat(LotroCardBlueprintLibrary library,
|
||||
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 discardPileIsPublic,
|
||||
boolean winOnControlling5Sites, boolean playtest, boolean hallVisible) {
|
||||
_adventure = adventure;
|
||||
_library = library;
|
||||
_name = name;
|
||||
_code = code;
|
||||
_order = order;
|
||||
_surveyUrl = surveyUrl;
|
||||
_siteBlock = siteBlock;
|
||||
_validateShadowFPCount = validateShadowFPCount;
|
||||
_minimumDeckSize = minimumDeckSize;
|
||||
_maximumSameName = maximumSameName;
|
||||
_mulliganRule = mulliganRule;
|
||||
_canCancelRingBearerSkirmish = canCancelRingBearerSkirmish;
|
||||
_hasRuleOfFour = hasRuleOfFour;
|
||||
_winAtEndOfRegroup = winAtEndOfRegroup;
|
||||
_discardPileIsPublic = discardPileIsPublic;
|
||||
_winOnControlling5Sites = winOnControlling5Sites;
|
||||
_isPlaytest = playtest;
|
||||
_hallVisible = hallVisible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Adventure getAdventure() {
|
||||
return _adventure;
|
||||
@@ -144,6 +137,11 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
return _hasRuleOfFour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesMaps() {
|
||||
return _usesMaps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean winOnControlling5Sites() {
|
||||
return _winOnControlling5Sites;
|
||||
@@ -296,7 +294,7 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
if (_validCards.contains(blueprintId) || _errataCardMap.containsValue(blueprintId))
|
||||
return null;
|
||||
|
||||
if (_validSets.size() > 0 && !isValidInSets(blueprintId))
|
||||
if (!_validSets.isEmpty() && !isValidInSets(blueprintId))
|
||||
return "Deck contains card not from valid set: " + GameUtils.getFullName(_library.getLotroCardBlueprint(blueprintId));
|
||||
|
||||
// Banned cards
|
||||
@@ -362,6 +360,12 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
result.add(valid);
|
||||
}
|
||||
|
||||
// Map
|
||||
valid = validateMap(deck);
|
||||
if(valid != null && !valid.isEmpty()) {
|
||||
result.add(valid);
|
||||
}
|
||||
|
||||
// Deck
|
||||
valid = validateDeckStructure(deck);
|
||||
if(valid != null && !valid.isEmpty()) {
|
||||
@@ -370,7 +374,7 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
|
||||
String prevLine = "";
|
||||
String newLine = "";
|
||||
for (String card : deck.getAdventureCards()){
|
||||
for (String card : deck.getDrawDeckCards()){
|
||||
newLine = validateCard(card);
|
||||
if(newLine == null || newLine.isEmpty())
|
||||
continue;
|
||||
@@ -413,7 +417,7 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
if (deck.getRingBearer() != null) {
|
||||
processCardCounts(deck.getRingBearer(), cardCountByName, cardCountByBaseBlueprintId);
|
||||
}
|
||||
for (String blueprintId : deck.getAdventureCards())
|
||||
for (String blueprintId : deck.getDrawDeckCards())
|
||||
processCardCounts(blueprintId, cardCountByName, cardCountByBaseBlueprintId);
|
||||
for (String blueprintId : deck.getSites())
|
||||
processCardCounts(blueprintId, cardCountByName, cardCountByBaseBlueprintId);
|
||||
@@ -475,7 +479,7 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
if (deck.getRing() != null) {
|
||||
deckWithErrata.setRing(applyErrata(deck.getRing()));
|
||||
}
|
||||
for (String card : deck.getAdventureCards()) {
|
||||
for (String card : deck.getDrawDeckCards()) {
|
||||
deckWithErrata.addCard(applyErrata(card));
|
||||
}
|
||||
for (String site : deck.getSites()) {
|
||||
@@ -510,13 +514,13 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
|
||||
private String validateDeckStructure(LotroDeck deck) {
|
||||
String result = "";
|
||||
if (deck.getAdventureCards().size() < _minimumDeckSize) {
|
||||
result += "Deck contains below minimum number of cards: " + deck.getAdventureCards().size() + "<" + _minimumDeckSize + ".\n";
|
||||
if (deck.getDrawDeckCards().size() < _minimumDeckSize) {
|
||||
result += "Deck contains below minimum number of cards: " + deck.getDrawDeckCards().size() + "<" + _minimumDeckSize + ".\n";
|
||||
}
|
||||
if (_validateShadowFPCount) {
|
||||
int shadow = 0;
|
||||
int fp = 0;
|
||||
for (String blueprintId : deck.getAdventureCards()) {
|
||||
for (String blueprintId : deck.getDrawDeckCards()) {
|
||||
try {
|
||||
LotroCardBlueprint card = _library.getLotroCardBlueprint(blueprintId);
|
||||
if (card.getSide() == Side.SHADOW)
|
||||
@@ -673,6 +677,52 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
return validateCard(ringbp);
|
||||
}
|
||||
|
||||
private String validateMap(LotroDeck deck) {
|
||||
String mapBP = deck.getMap();
|
||||
if (!_usesMaps)
|
||||
return null;
|
||||
if (mapBP == null)
|
||||
return "Deck doesn't have a Map (try the King map if converting a Movie deck).";
|
||||
try {
|
||||
var map = _library.getLotroCardBlueprint(mapBP);
|
||||
if (map.getCardType() != CardType.MAP)
|
||||
return "Card assigned as Map is not a Map card.";
|
||||
|
||||
var check = validateCard(mapBP);
|
||||
if(check != null)
|
||||
return check;
|
||||
|
||||
var freeps = new ArrayList<PhysicalCardImpl>();
|
||||
var shadow = new ArrayList<PhysicalCardImpl>();
|
||||
var sites = new ArrayList<PhysicalCardImpl>();
|
||||
for(var bp : deck.getDrawDeckCards()) {
|
||||
var blueprint = _library.getLotroCardBlueprint(bp);
|
||||
if(blueprint.getSide() == Side.FREE_PEOPLE) {
|
||||
freeps.add(new PhysicalCardImpl(0, bp, "", blueprint));
|
||||
}
|
||||
else if(blueprint.getSide() == Side.SHADOW) {
|
||||
shadow.add(new PhysicalCardImpl(0, bp, "", blueprint));
|
||||
}
|
||||
}
|
||||
|
||||
for(var bp : deck.getSites()) {
|
||||
sites.add(new PhysicalCardImpl(0, bp, "", _library.getLotroCardBlueprint(bp)));
|
||||
}
|
||||
|
||||
var rb = new PhysicalCardImpl(0, deck.getRingBearer(), "", _library.getLotroCardBlueprint(deck.getRingBearer()));
|
||||
var ring = new PhysicalCardImpl(0, deck.getRing(), "", _library.getLotroCardBlueprint(deck.getRing()));
|
||||
|
||||
var result = map.validatePreGameDeckCheck(freeps, shadow, sites, rb, ring, new PhysicalCardImpl(0, mapBP, "", map));
|
||||
if(!result.success())
|
||||
return result.message();
|
||||
return null;
|
||||
}
|
||||
catch(CardNotFoundException exception)
|
||||
{
|
||||
return CardRemovedError + ": " + mapBP;
|
||||
}
|
||||
}
|
||||
|
||||
private String validateRingBearer(LotroDeck deck) {
|
||||
String rb = deck.getRingBearer();
|
||||
if (rb == null)
|
||||
@@ -730,6 +780,7 @@ public class DefaultLotroFormat implements LotroFormat {
|
||||
minimumDeckSize = _minimumDeckSize;
|
||||
maximumSameName = _maximumSameName;
|
||||
mulliganRule = _mulliganRule;
|
||||
usesMaps = _usesMaps;
|
||||
set = null;
|
||||
banned = null;
|
||||
restricted = null;
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
@@ -602,7 +601,7 @@ public class HallServer extends AbstractServer {
|
||||
for (String site : lotroDeck.getSites())
|
||||
filteredSpecialCardsDeck.addSite(filterCard(site, ownedCollection));
|
||||
|
||||
for (Map.Entry<String, Integer> cardCount : CollectionUtils.getTotalCardCount(lotroDeck.getAdventureCards()).entrySet()) {
|
||||
for (Map.Entry<String, Integer> cardCount : CollectionUtils.getTotalCardCount(lotroDeck.getDrawDeckCards()).entrySet()) {
|
||||
String blueprintId = cardCount.getKey();
|
||||
int count = cardCount.getValue();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user