diff --git a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/CollectionRequestHandler.java b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/CollectionRequestHandler.java index 775b2588a..bcd2436e1 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/CollectionRequestHandler.java +++ b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/CollectionRequestHandler.java @@ -36,6 +36,7 @@ public class CollectionRequestHandler extends LotroServerRequestHandler implemen private LotroCardBlueprintLibrary _library; private LotroFormatLibrary _formatLibrary; private SortAndFilterCards _sortAndFilterCards; + private ImportCards _importCards; public CollectionRequestHandler(Map context) { super(context); @@ -47,12 +48,15 @@ public class CollectionRequestHandler extends LotroServerRequestHandler implemen _library = extractObject(context, LotroCardBlueprintLibrary.class); _formatLibrary = extractObject(context, LotroFormatLibrary.class); _sortAndFilterCards = new SortAndFilterCards(); + _importCards = new ImportCards(); } @Override public void handleRequest(String uri, HttpRequest request, Map context, ResponseWriter responseWriter, String remoteIp) throws Exception { if (uri.equals("") && request.getMethod() == HttpMethod.GET) { getCollectionTypes(request, responseWriter); + } else if (uri.startsWith("/import/") && request.getMethod() == HttpMethod.GET) { + importCollection(request, responseWriter); } else if (uri.startsWith("/") && request.getMethod() == HttpMethod.POST) { openPack(request, uri.substring(1), responseWriter); } else if (uri.startsWith("/") && request.getMethod() == HttpMethod.GET) { @@ -61,6 +65,50 @@ public class CollectionRequestHandler extends LotroServerRequestHandler implemen throw new HttpProcessingException(404); } } + + private void importCollection(HttpRequest request, ResponseWriter responseWriter) throws Exception { + QueryStringDecoder queryDecoder = new QueryStringDecoder(request.getUri()); + String participantId = getQueryParameterSafely(queryDecoder, "participantId"); + String rawDecklist = getQueryParameterSafely(queryDecoder, "decklist"); + + Player resourceOwner = getResourceOwnerSafely(request, participantId); + + CardCollection collection = constructCollection(resourceOwner, "default"); + + if (collection == null) + throw new HttpProcessingException(404); + + Iterable items = collection.getAll(); + List importResult = _importCards.process(rawDecklist, items, _library); + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + + Document doc = documentBuilder.newDocument(); + + Element collectionElem = doc.createElement("collection"); + collectionElem.setAttribute("count", String.valueOf(importResult.size())); + doc.appendChild(collectionElem); + + for (int i = 0; i < importResult.size(); i++) { + CardCollection.Item item = importResult.get(i); + String blueprintId = item.getBlueprintId(); + if (item.getType() == CardCollection.Item.Type.CARD) { + Element card = doc.createElement("card"); + card.setAttribute("count", String.valueOf(item.getCount())); + card.setAttribute("blueprintId", blueprintId); + LotroCardBlueprint blueprint = _library.getLotroCardBlueprint(blueprintId); + appendCardSide(card, blueprint); + appendCardGroup(card, blueprint); + collectionElem.appendChild(card); + } + } + + Map headers = new HashMap(); + processDeliveryServiceNotification(request, headers); + + responseWriter.writeXmlResponse(doc, headers); + } private void getCollection(HttpRequest request, String collectionType, ResponseWriter responseWriter) throws Exception { QueryStringDecoder queryDecoder = new QueryStringDecoder(request.getUri()); diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/communication.js b/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/communication.js index f978e6195..fbad21339 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/communication.js +++ b/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/communication.js @@ -369,6 +369,19 @@ var GempLotrCommunication = Class.extend({ dataType:"xml" }); }, + importCollection:function (decklist, callback, errorMap) { + $.ajax({ + type:"GET", + url:this.url + "/collection/import/", + cache:false, + data:{ + participantId:getUrlParam("participantId"), + decklist:decklist}, + success:this.deliveryCheck(callback), + error:this.errorCheck(errorMap), + dataType:"xml" + }); + }, openPack:function (collectionType, pack, callback, errorMap) { $.ajax({ type:"POST", diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/deckBuildingUi.js b/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/deckBuildingUi.js index 8ca8df810..f3dd801a8 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/deckBuildingUi.js +++ b/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/deckBuildingUi.js @@ -40,6 +40,7 @@ var GempLotrDeckBuildingUI = Class.extend({ selectionDialog:null, selectionGroup:null, packSelectionId:null, + deckImportDialog:null, cardFilter:null, @@ -101,6 +102,9 @@ var GempLotrDeckBuildingUI = Class.extend({ var deckListBut = $("").button(); this.manageDecksDiv.append(deckListBut); + + var importDeckBut = $("").button(); + this.manageDecksDiv.append(importDeckBut); this.manageDecksDiv.append("New deck"); @@ -169,6 +173,12 @@ var GempLotrDeckBuildingUI = Class.extend({ that.loadDeckList(); }); + importDeckBut.click( + function () { + that.deckName = null; + that.importDecklist(); + }); + this.collectionDiv = $("#collectionDiv"); $("#collectionSelect").change( @@ -295,6 +305,107 @@ var GempLotrDeckBuildingUI = Class.extend({ } }); }, + + importDecklist:function () { + var that = this; + if (that.deckImportDialog == null) { + that.deckImportDialog = $('
').dialog({ + closeOnEscape:true, + resizable:true, + title:"Import deck" + }); + } + that.deckImportDialog.html(""); + var deckImport = $(""); + var getDecklistTextBut = $("").button(); + + var importDialogDiv = $("
"); + importDialogDiv.append(deckImport); + importDialogDiv.append(getDecklistTextBut); + that.deckImportDialog.append(importDialogDiv); + + getDecklistTextBut.click( + function () { + var decklist = $('textarea[decklist="decklist"]').val() + that.parseDecklist(decklist); + } + ); + that.deckImportDialog.dialog("open"); + }, + + parseDecklist:function(rawText) { + this.clearDeck(); + var that = this; + var rawTextList = rawText.split("\n"); + var formattedText = ""; + for (var i = 0; i < rawTextList.length; i++) { + if (rawTextList[i] != "") { + var line = that.removeNotes(rawTextList[i]).toLowerCase(); + line = line.replace(/[\*•]/g,"").replace(/’/g,"'") + .replace(/starting|start|ring-bearer:|ring:/g,"") + formattedText = formattedText + line.trim() + "~"; + } + } + + this.importDeckCollection(formattedText, function (xml) { + log(xml); + + var cards = xml.documentElement.getElementsByTagName("card"); + for (var i = 0; i < cards.length; i++) { + var cardElem = cards[i]; + var blueprintId = cardElem.getAttribute("blueprintId"); + var side = cardElem.getAttribute("side"); + var group = cardElem.getAttribute("group"); + if (group == "ringBearer") { + that.addCardToContainer(blueprintId, "special", that.ringBearerDiv, false).addClass("cardInDeck"); + that.layoutSpecialGroups(); + } + else if (group == "ring") { + that.addCardToContainer(blueprintId, "special", that.ringDiv, false).addClass("cardInDeck"); + that.layoutSpecialGroups(); + } + else { + that.addCardToDeckAndLayout(blueprintId, side); + } + } + $("#editingDeck").text("Imported Deck (unsaved)"); + }); + }, + + removeNotes:function(line) { + var processedLine = line; + var hasNotes = false; + var start = line.indexOf("("); + var end = line.indexOf(")", start); + if (start < 0 && end < 0) { + start = line.indexOf("["); + end = line.indexOf("]", start); + } + if (start > 0) { + processedLine = line.slice(0,start) + if (end > 0) { + processedLine = processedLine + line.slice(end+1); + } + } + else if (end > 0) { + processedLine = line.slice(end+1); + } + if (processedLine.indexOf("(") > -1 || processedLine.indexOf(")") > -1 || + processedLine.indexOf("[") > -1 || processedLine.indexOf("]") > -1) { + return this.removeNotes(processedLine); + } + return processedLine; + }, + + importDeckCollection:function (decklist, callback) { + this.comm.importCollection(decklist, function (xml) { + callback(xml); + }, { + "414":function () { + alert("Deck too large to import."); + } + }); + }, loadDeckList:function () { var that = this; diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/ImportCards.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/ImportCards.java new file mode 100644 index 000000000..dc8432132 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/ImportCards.java @@ -0,0 +1,149 @@ +package com.gempukku.lotro.game; + +import com.gempukku.lotro.common.*; +import com.gempukku.lotro.logic.GameUtils; + +import java.util.*; + +public class ImportCards { + private SitesBlock _siteBlock; + + //For a deck to be legal in a Pre-shadows format, it must contain one of these sites + private List _fellowshipSiteCheck = new ArrayList(Arrays.asList("Council Courtyard", + "Ford of Bruinen", "Frodo's Bedroom", "Rivendell Terrace", "Rivendell Valley", "Rivendell Waterfall", + "House of Elrond")); + private List _towersSiteCheck = new ArrayList(Arrays.asList("Derndingle", "Eastfold", + "Fangorn Forest", "Plains of Rohan Camp", "Rohirrim Village", "Uruk Camp", "Wold of Rohan")); + private List _kingSiteCheck = new ArrayList(Arrays.asList("King's Tent", "Rohirrim Camp", + "West Road")); + + public List process(String rawDecklist, Iterable items, LotroCardBlueprintLibrary cardLibrary) { + List decklist = getDecklist(rawDecklist); + _siteBlock = null; + + List result = new ArrayList(); + Set pendingSites = new HashSet(); + Map cardBlueprintMap = new HashMap<>(); + for (String line : decklist) { + for (T item : items) { + String blueprintId = item.getBlueprintId(); + if (isPack(blueprintId)) + continue; + try { + cardBlueprintMap.put(blueprintId, cardLibrary.getLotroCardBlueprint(blueprintId)); + int outcome = importCriteria(cardLibrary, cardBlueprintMap, blueprintId, line); + if (outcome == 1) { + result.add(item); + } + if (outcome == 0) + pendingSites.add(item); + } catch (CardNotFoundException e) { + // Ignore the card + } + } + } + + if (_siteBlock == null) + _siteBlock = SitesBlock.SHADOWS; + for (T siteItem : pendingSites) { + String siteId = siteItem.getBlueprintId(); + try { + LotroCardBlueprint siteBlueprint = cardLibrary.getLotroCardBlueprint(siteId); + if (siteBlockFilter(siteBlueprint)) + result.add(siteItem); + } catch (CardNotFoundException e) { + // Ignore the card + } + } + + return result; + } + + private int importCriteria(LotroCardBlueprintLibrary library, Map cardBlueprint, String blueprintId, String title) { + final LotroCardBlueprint blueprint = cardBlueprint.get(blueprintId); + if (exactMatch(blueprint,title)) { + if (setFilter(blueprintId)) { + if (blueprint.getCardType() == CardType.SITE) { + if (_siteBlock == null) + findSiteBlock(blueprint); + //add all sites to the set, as some formats would add duplicates otherwise + return 0; + } + return 1; + } + } + return -1; + } + + private boolean exactMatch(LotroCardBlueprint blueprint, String title) { + if (blueprint == null || !replaceSpecialCharacters(GameUtils.getFullName(blueprint).toLowerCase()).equals(title)) + return false; + return true; + } + + private boolean setFilter(String blueprintId) { + try { + int setNo = Integer.parseInt(blueprintId.split("_")[0]); + if (setNo < 20) + return true; + } catch (Exception e) { + //Not a card + } + return false; + } + + private boolean siteBlockFilter(LotroCardBlueprint blueprint) { + if (blueprint.getSiteBlock() == _siteBlock) + return true; + return false; + } + + private void findSiteBlock(LotroCardBlueprint cardBlueprint) { + String site = cardBlueprint.getTitle(); + if (_fellowshipSiteCheck.contains(site)) + _siteBlock = SitesBlock.FELLOWSHIP; + else if (_towersSiteCheck.contains(site)) + _siteBlock = SitesBlock.TWO_TOWERS; + else if (_kingSiteCheck.contains(site)) + _siteBlock = SitesBlock.KING; + } + + private List getDecklist(String rawDecklist) { + List result = new ArrayList(); + for (String line : rawDecklist.split("~")) { + if (line.length() == 0) + continue; + int quantity = 1; + line = line.toLowerCase(); + String cardLine = ""; + if (Character.isDigit(line.charAt(0))) { + quantity = Character.getNumericValue(line.charAt(0)); + cardLine = line.substring(line.indexOf(" ")); + } + else if (Character.isDigit(line.charAt(line.length()-1))) { + quantity = Character.getNumericValue(line.charAt(line.length()-1)); + cardLine = line.substring(0,line.indexOf(" ",line.length()-3)); + } + else { + quantity = 1; + cardLine = line; + } + for (int i = 0; i < quantity; i++) + result.add(replaceSpecialCharacters(cardLine).trim()); + } + return result; + } + + private String replaceSpecialCharacters(String text) { + return text + .replace('é', 'e') + .replace('ú', 'u') + .replace('ë', 'e') + .replace('û', 'u') + .replace('ó', 'o'); + } + + private static boolean isPack(String blueprintId) { + return !blueprintId.contains("_"); + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/SortAndFilterCards.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/SortAndFilterCards.java index fa37615f6..ce100c672 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/SortAndFilterCards.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/SortAndFilterCards.java @@ -165,19 +165,19 @@ public class SortAndFilterCards { final LotroCardBlueprint blueprint = cardBlueprint.get(blueprintId); if (blueprint.getCardType() == CardType.SITE) { if (blueprint.getSiteBlock() == SitesBlock.FELLOWSHIP) { - if ("fotr_block,lotr".contains(set)) { + if ("fotr_block".contains(set)) { return true; } return false; } if (blueprint.getSiteBlock() == SitesBlock.TWO_TOWERS) { - if ("towers_standard,ttt_block,lotr".contains(set)) { + if ("towers_standard,ttt_block".contains(set)) { return true; } return false; } if (blueprint.getSiteBlock() == SitesBlock.KING) { - if ("king_block,rotk_sta,movie,lotr".contains(set)) { + if ("king_block,rotk_sta,movie".contains(set)) { return true; } return false;