Merge remote-tracking branch 'marcin/master'

This commit is contained in:
Christian 'ketura' McCarty
2021-01-03 13:36:24 -06:00
14 changed files with 335 additions and 16 deletions

View File

@@ -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) {
@@ -62,6 +66,50 @@ public class CollectionRequestHandler extends LotroServerRequestHandler implemen
}
}
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());
String participantId = getQueryParameterSafely(queryDecoder, "participantId");

View File

@@ -205,7 +205,7 @@
},
"0_64": {
"title": "*Gandalf",
"subtitle": "Stormcraw",
"subtitle": "Stormcrow",
"culture": "gandalf",
"cost": 4,
"type": "companion",

View File

@@ -373,7 +373,7 @@
},
"1_17": {
"title": "*Grimir",
"subtitle": "Dwarven Elder,",
"subtitle": "Dwarven Elder",
"culture": "dwarven",
"cost": 1,
"type": "ally",

View File

@@ -353,7 +353,7 @@
}
},
"1_179": {
"title": "Goblin Scavenger",
"title": "Goblin Scavengers",
"culture": "moria",
"cost": 3,
"type": "minion",

View File

@@ -1003,7 +1003,7 @@
}
},
"1_354": {
"title": "Anduin Wilderlands",
"title": "Anduin Wilderland",
"cost": 6,
"type": "site",
"site": 7,

View File

@@ -514,7 +514,7 @@
"19_14": {
"title": "Not Bound To His Fate",
"culture": "gondor",
"cost": 3,
"cost": 2,
"type": "condition",
"strength": 1,
"target": "culture(gondor),man",

View File

@@ -75,7 +75,7 @@
]
},
"9_29": {
"title": "Slippery as Fished",
"title": "Slippery as Fishes",
"side": "shadow",
"culture": "gollum",
"cost": 1,

View File

@@ -107,10 +107,9 @@ var CardFilter = Class.extend({
this.fullFilterDiv = $("<div></div>");
this.setSelect = $("<select style='width: 130px; font-size: 80%;'>"
+ "<option value=''>All Sets</option>"
+ "<option value='0-32'>All Sets</option>"
+ "<option value='0-19'>Official Sets</option>"
+ "<option value='30-32'>The Hobbit Sets</option>"
+ "<option value='40'>Second Edition Sets</option>"
+ "<option disabled>----------</option>"
+ "<option value='fotr_block'>Fellowship Block</option>"
+ "<option value='ttt_block'>Towers Block</option>"
@@ -124,7 +123,6 @@ var CardFilter = Class.extend({
+ "<option value='standard'>Standard</option>"
+ "<option value='hobbit_tsr'> The Hobbit: The Short Rest</option>"
+ "<option value='hobbit_tcb'> The Hobbit: The Clouds Burst</option>"
+ "<option value='2nd_ed'>Second Edition</option>"
+ "<option disabled>----------</option>"
+ "<option value='0'>00 - Promo</option>"
+ "<option value='1'>01 - The Fellowship of the Ring</option>"
@@ -149,7 +147,6 @@ var CardFilter = Class.extend({
+ "<option value='30'>30 - The Hobbit: Main Deck</option>"
+ "<option value='31'>31 - The Hobbit: Expansion 1</option>"
+ "<option value='32'>32 - The Hobbit: Expansion 2</option>"
+ "<option value='40'>40 - Second Edition</option>"
+ "</select>");
this.nameInput = $("<input type='text' placeholder='Card name' value='' style='width: 110px; font-size: 70%;'>");
this.sortSelect = $("<select style='width: 80px; font-size: 80%;'>"

View File

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

View File

@@ -40,6 +40,7 @@ var GempLotrDeckBuildingUI = Class.extend({
selectionDialog:null,
selectionGroup:null,
packSelectionId:null,
deckImportDialog:null,
cardFilter:null,
@@ -99,6 +100,9 @@ var GempLotrDeckBuildingUI = Class.extend({
var copyDeckBut = $("<button title='Copy deck to new'><span class='ui-icon ui-icon-copy'></span></button>").button();
this.manageDecksDiv.append(copyDeckBut);
var importDeckBut = $("<button title='Import Deck'><span class='ui-icon ui-icon-arrowthickstop-1-s'></span></button>").button();
this.manageDecksDiv.append(importDeckBut);
var deckListBut = $("<button title='Deck list'><span class='ui-icon ui-icon-suitcase'></span></button>").button();
this.manageDecksDiv.append(deckListBut);
@@ -169,6 +173,12 @@ var GempLotrDeckBuildingUI = Class.extend({
that.loadDeckList();
});
importDeckBut.click(
function () {
that.deckName = null;
that.importDecklist();
});
this.collectionDiv = $("#collectionDiv");
$("#collectionSelect").change(
@@ -296,6 +306,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;
this.comm.getDecks(function (xml) {

View File

@@ -144,6 +144,7 @@
"rarityFile": "/set40-rarity.txt",
"originalSet": false,
"merchantable": false,
"needsLoading": false
"needsLoading": false,
"playable": false
}
]

View File

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

View File

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

View File

@@ -293,7 +293,7 @@
"winAtEndOfRegroup":true,
"ruleOfFour":false,
"winOnControlling5Sites":true,
"hall":true
"hall":false
},
{
"name":"Hobbit: The Short Rest",