Deck import tool
This commit is contained in:
@@ -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<Type, Object> 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<Type, Object> 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<CardCollection.Item> items = collection.getAll();
|
||||
List<CardCollection.Item> 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<String, String> headers = new HashMap<String, String>();
|
||||
processDeliveryServiceNotification(request, headers);
|
||||
|
||||
responseWriter.writeXmlResponse(doc, headers);
|
||||
}
|
||||
|
||||
private void getCollection(HttpRequest request, String collectionType, ResponseWriter responseWriter) throws Exception {
|
||||
QueryStringDecoder queryDecoder = new QueryStringDecoder(request.getUri());
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 title='Deck list'><span class='ui-icon ui-icon-suitcase'></span></button>").button();
|
||||
this.manageDecksDiv.append(deckListBut);
|
||||
|
||||
var importDeckBut = $("<button title='Import Deck'><span class='ui-icon ui-icon-arrowthickstop-1-s'></span></button>").button();
|
||||
this.manageDecksDiv.append(importDeckBut);
|
||||
|
||||
this.manageDecksDiv.append("<span id='editingDeck'>New deck</span>");
|
||||
|
||||
@@ -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 = $('<div></div>').dialog({
|
||||
closeOnEscape:true,
|
||||
resizable:true,
|
||||
title:"Import deck"
|
||||
});
|
||||
}
|
||||
that.deckImportDialog.html("");
|
||||
var deckImport = $("<textarea rows='5' cols='30' id='deckImport' decklist='decklist'></textarea>");
|
||||
var getDecklistTextBut = $("<button title='Import'>Import</button>").button();
|
||||
|
||||
var importDialogDiv = $("<div></div>");
|
||||
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;
|
||||
|
||||
@@ -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<String> _fellowshipSiteCheck = new ArrayList<String>(Arrays.asList("Council Courtyard",
|
||||
"Ford of Bruinen", "Frodo's Bedroom", "Rivendell Terrace", "Rivendell Valley", "Rivendell Waterfall",
|
||||
"House of Elrond"));
|
||||
private List<String> _towersSiteCheck = new ArrayList<String>(Arrays.asList("Derndingle", "Eastfold",
|
||||
"Fangorn Forest", "Plains of Rohan Camp", "Rohirrim Village", "Uruk Camp", "Wold of Rohan"));
|
||||
private List<String> _kingSiteCheck = new ArrayList<String>(Arrays.asList("King's Tent", "Rohirrim Camp",
|
||||
"West Road"));
|
||||
|
||||
public <T extends CardItem> List<T> process(String rawDecklist, Iterable<T> items, LotroCardBlueprintLibrary cardLibrary) {
|
||||
List<String> decklist = getDecklist(rawDecklist);
|
||||
_siteBlock = null;
|
||||
|
||||
List<T> result = new ArrayList<T>();
|
||||
Set<T> pendingSites = new HashSet<T>();
|
||||
Map<String, LotroCardBlueprint> 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<String, LotroCardBlueprint> 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<String> getDecklist(String rawDecklist) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
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("_");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user