Guest User

Untitled

a guest
Apr 25th, 2016
583
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. javascript: (function() {
  2. /*
  3.  * Duelyst data export bookmarklet.
  4.  *
  5.  * Licenced under the wtfpl licence version 2.
  6.  * http://www.wtfpl.net/txt/copying/
  7.  *
  8.  * This bookmarklet, when executed, opens a new page in your browser and
  9.  * displays information regarding your duelyst account. This version displays:
  10.  * - All squads you built (including a duelystdb link).
  11.  * - An overview of the number of craftable cards in the game.
  12.  * - An overview of of the number of craftable cards in your collection.
  13.  * - A detailed overview of your collection of craftable cards.
  14.  *
  15.  * Planned features for to be added:
  16.  * - Improve the card collection summary.
  17.  *
  18.  * The bookmarklet needs to be executed when logged in on your duelyst account.
  19.  * First you need to install the bookmarklet [1], then you also need to allow
  20.  * popus from the beta.duelyst.com domain [2].
  21.  *
  22.  * The bookmarklet has been tested with Duelyst 0.63.2.
  23.  *
  24.  *
  25.  * [1]
  26.  * https://crossbrowsertesting.com/faq/how-do-i-install-bookmarklet-google-chrome-mac-os
  27.  * [2] https://support.google.com/chrome/answer/95472
  28.  */
  29. /*
  30.  * Contains the main code of the bookmarklet.
  31.  */
  32.  
  33. /*
  34.  * Contains the global resources of the code.
  35.  */
  36.  
  37. /**
  38.  * The number of factions (including neutral) in the game.
  39.  *
  40.  * There are also hidden factions, like the tutorial teachers. They are not
  41.  * counted.
  42.  */
  43. const faction_count = 7;
  44.  
  45. /** The names of the factions in the game. */
  46. const faction_names = [
  47.   "Lyonar",
  48.   "Songhai",
  49.   "Vetruvian",
  50.   "Abyssian",
  51.   "Magmar",
  52.   "Vanar",
  53.   "Neutral"
  54. ];
  55.  
  56. /** The number of card rarities in the game. */
  57. const rarity_count = 4;
  58.  
  59. /** The names of the card rarities. */
  60. const rarity_names = [ "Common", "Rare", "Epic", "Legendary" ];
  61.  
  62. /** The cost to craft a card based on its rarity. */
  63. const spirit_craft_cost = [ 40, 100, 350, 900 ];
  64.  
  65. /** The value of disenchanting a card based on its rarity. */
  66. const spirit_disenchant_value = [ 10, 20, 100, 350 ];
  67.  
  68. /**
  69.  * The maximum copies of a card in a deck.
  70.  *
  71.  * This value is not based on the rarity of the card.
  72.  */
  73. const maximum_copies_per_deck = 3;
  74. /*
  75.  * Contains the basic library functions.
  76.  */
  77.  
  78. /**
  79.  * Returns the attributes object of a faction.
  80.  *
  81.  * @param id                      The ID of the faction.
  82.  */
  83. var get_faction = function(id) {
  84.   return GameDataManager.instance.factionsCollection._byId[id].attributes;
  85. };
  86.  
  87. /**
  88.  * Returns the attributes object of a card.
  89.  *
  90.  * @param id                      The ID of the card.
  91.  */
  92. var get_card = function(id) {
  93.   return GameDataManager.instance.cardsCollection._byId[id].attributes;
  94. };
  95.  
  96. /**
  97.  * `Sorts' a set of cards.
  98.  *
  99.  * This function creates an array with the sorted order of cards.
  100.  * It expects a map with:
  101.  * - Key    The type does not matter.
  102.  * - Value  A card record containing at least:
  103.  *          * name  The name of the card.
  104.  *          * mana  The amount of mana the card costs.
  105.  *
  106.  * @param cards                   The set of cards to sort.
  107.  *
  108.  * @returns                       An sorted array with the keys of cards.
  109.  */
  110. var sort_cards = function(cards) {
  111.   var result = Object.keys(cards);
  112.  
  113.   result.sort(function(rhs, lhs) {
  114.     var compare = cards[rhs].mana < cards[lhs].mana ||
  115.                   (cards[rhs].mana == cards[lhs].mana &&
  116.                    cards[rhs].name < cards[lhs].name);
  117.  
  118.     return compare ? -1 : 1;
  119.   });
  120.  
  121.   return result;
  122. };
  123.  
  124. /**
  125.  * Escapes HTML special characters in a string.
  126.  *
  127.  * @param html                    The string to escape.
  128.  *
  129.  * @returns                       The escaped string.
  130.  */
  131. var escape_html = function(html) {
  132.   var replacement_table = {
  133.     "&" : "&amp;",
  134.     "<" : "&lt;",
  135.     ">" : "&gt;",
  136.     '"' : '&quot;',
  137.     "'" : '&apos;'
  138.   };
  139.  
  140.   return html.replace(/[&<>"']/g, function(c) {
  141.     return replacement_table[c];
  142.   });
  143. };
  144.  
  145. /**
  146.  * `Shows a set' of cards.
  147.  *
  148.  * The function creates HTML table for a set of cards.
  149.  * It expects a map with:
  150.  * - Key    The type does not matter.
  151.  * - Value  A card record containing at least:
  152.  *          * name        The name of the card.
  153.  *          * is_general  Is the card the general?
  154.  *          * mana        The amount of mana the card costs.
  155.  *          * count       The number of copies of the card.
  156.  *
  157.  * @returns                       The HTML output.
  158.  */
  159. var show_cards = function(cards, title) {
  160.   var keys = sort_cards(cards);
  161.  
  162.   var result = title;
  163.   result += "<table>";
  164.   keys.forEach(function(i) {
  165.     if (!cards[i].is_general) {
  166.       result += "<tr><td>" + cards[i].mana + "</td><td>" +
  167.                 escape_html(cards[i].name) + "</td><td>" + cards[i].count +
  168.                 "</td></tr>";
  169.     }
  170.   });
  171.   result += "<table>";
  172.  
  173.   return result;
  174. };
  175.  
  176. /**
  177.  * `Shows' a card matrix.
  178.  *
  179.  * The function creates a HTML table for a matrix of cards. The matrix will be
  180.  * displayed in a table. The function adds the titles for the matrix.
  181.  *
  182.  * @param matrix                  The matrix to show. The matrix must be
  183.  *                                compatible with the one created by
  184.  *                                create_matrix.
  185.  */
  186. var show_matrix = function(matrix) {
  187.   var result = "<table>";
  188.  
  189.   result += "<tr><td></td>";
  190.   for (var i = 0; i < rarity_count; ++i) {
  191.     result += "<td>" + rarity_names[i] + "</td>";
  192.   }
  193.   result += "</tr>";
  194.  
  195.   for (var f = 0; f < faction_count; ++f) {
  196.     result += "<tr>";
  197.     result += "<td>" + faction_names[f] + "</td>";
  198.     for (var r = 0; r < rarity_count; ++r) {
  199.       result += "<td>" + matrix[f][r] + "</td>";
  200.     }
  201.     result += "</tr>";
  202.   }
  203.  
  204.   result += "</table>";
  205.  
  206.   return result;
  207. };
  208.  
  209. /**
  210.  * Returns the number of usable copies of a card.
  211.  *
  212.  * @param count                   The number of copies of a card.
  213.  *
  214.  * @returns                       The usable number of copies of the card.
  215.  */
  216. var get_usable_count = function(count) {
  217.   return Math.min(count, 3);
  218. };
  219.  
  220. /**
  221.  * Returns the number of disenchantable copies of a card.
  222.  *
  223.  * @param count                   The number of copies of a card.
  224.  *
  225.  * @returns                       The disenchanteble number of copies of the
  226.  *                                card.
  227.  */
  228. var get_disenchant_count = function(count) {
  229.   return Math.max(count - 3, 0);
  230. };
  231.  
  232. /**
  233.  * Returns the number of copies of the card the user misses.
  234.  *
  235.  * @param count                   The number of copies of a card.
  236.  *
  237.  * @returns                       The missing number of copies of the card.
  238.  */
  239. function get_missing_count(count) {
  240.   return Math.max(maximum_copies_per_deck - count, 0);
  241. }
  242. /*
  243.  * Contains the basic card class.
  244.  */
  245.  
  246. /**
  247.  * Contains the definition of a card.
  248.  *
  249.  * The class has the following member variables:
  250.  * * id                           The ID of the card as used by Duelyst.
  251.  * * name                         The in-game name of the card.
  252.  * * mana                         The amount of mana the card costs.
  253.  * * count                        The number of copies of the card. The meaning
  254.  *                                of count depends on the context where the
  255.  *                                card is used.
  256.  * * is_general                   Is the card the general?
  257.  * * faction_index                The index of the faction as returned by
  258.  *                                @ref get_card_faction_index
  259.  *  * rarity_index                The index of the rarity as returned by
  260.  *                                @ref get_card_rarity_index.
  261.  */
  262. class tcard {
  263.  
  264.   /**
  265.    * Constructor.
  266.    *
  267.    * @param card                  A Duelyst card record as returned by
  268.    *                              @ref get_card.
  269.    * @param count                 he number of copies of the card.
  270.    */
  271.   constructor(card, count) {
  272.     this.id = card.id;
  273.     this.name = card.name;
  274.  
  275.     this.mana = card.manaCost;
  276.     this.count = count;
  277.     this.is_general = card.isGeneral;
  278.  
  279.     this.faction_index = get_card_faction_index(card);
  280.     this.rarity_index = get_card_rarity_index(card);
  281.   }
  282.  
  283.   /** Returns a HTML table row for a deck list. */
  284.   get deck_list_html() {
  285.     return `<tr><td>${this.mana}</td>` + `<td>${escape_html(this.name)}</td>` +
  286.            `<td>${this.count}</td></tr>`;
  287.   }
  288.  
  289.   /** Returns a CSV table row for a deck list.*/
  290.   get deck_list_csv() {
  291.     return `${this.mana}\t${this.name}\t${this.count}\n`
  292.   }
  293.  
  294.   /** Returns a Duelystdb card record for a deck list. */
  295.   get deck_list_duelystdb() {
  296.     return `${this.count}:${this.id}`
  297.   }
  298.  
  299.   /** Returns the field names for a collection list. */
  300.   static get_collection_list_field_names() {
  301.     return [ "Mana", "Name", "Rarity", "Count" ];
  302.   }
  303.  
  304.   /** Returns a HTML table row for a collection list. */
  305.   get collection_list_html() {
  306.     return `<tr><td>${this.mana}</td>` + `<td>${escape_html(this.name)}</td>` +
  307.            `<td>${rarity_names[this.rarity_index]}</td>` +
  308.            `<td>${this.count}</td></tr>`;
  309.   }
  310.  
  311.  
  312.   /** Returns the field names for a detailed collection list. */
  313.   static get_collection_detailed_list_field_names() {
  314.     return [ "ID", "Name", "Mana", "Rarity", "Faction", "Count" ];
  315.   }
  316.  
  317.   /** Returns a CSV table row for a detailed collection list.*/
  318.   get collection_detailed_list_csv() {
  319.     return `${this.id}\t` + `${this.name}\t` + `${this.mana}\t` +
  320.            `${rarity_names[this.rarity_index]}\t` +
  321.            `${faction_names[this.faction_index]}\t` + `${this.count}\n`;
  322.   }
  323. }
  324. /*
  325.  * Contains the deck export code.
  326.  */
  327.  
  328.  
  329. /**
  330.  * Contains the definition of a deck.
  331.  *
  332.  * The class has the following member variables:
  333.  * * id                           The ID of the deck as used by Duelyst.
  334.  * * name                         The in-game name of the deck.
  335.  * * faction                      The in-game name of the faction.
  336.  * * title                        The title of the deck.
  337.  * * cards                        A map with cards in the deck
  338.  *                                - Key    The ID of the card.
  339.  *                                - Value  A @ref tcard object.
  340.  */
  341. class tdeck {
  342.   /**
  343.    * Constructor
  344.    *
  345.    * @param deck                  A Duelyst deck record.
  346.    */
  347.   constructor(deck) {
  348.     this.id = deck.id;
  349.     this.name = deck.name;
  350.     this.faction = get_faction(deck.faction_id).name;
  351.     this.title = `${this.name} (${this.faction})`;
  352.     this.cards = this.load_cards_(deck);
  353.   }
  354.  
  355.   /** Returns the TOC section of the deck. */
  356.   get toc() {
  357.     const link = `<a href="#deck_${this.id}">${escape_html(this.title)}</a>`;
  358.  
  359.     const result = `<li>${link} [${this.download_}] [${this.duelystdb_}]</li>`;
  360.  
  361.     return result;
  362.   }
  363.  
  364.   /** Returns the body section of the deck. */
  365.   get body() {
  366.     let result = `<h2 id="deck_${this.id}">${escape_html(this.title)}</h2>`;
  367.     result += html_;
  368.  
  369.     return result;
  370.   }
  371.  
  372.   /**
  373.    * Loads the cards in the deck.
  374.    *
  375.    * @param deck                  A Duelyst deck record.
  376.    *
  377.    * @returns                     The loaded cards.
  378.    */
  379.   load_cards_(deck) {
  380.     let cards = {};
  381.     deck.cards.forEach(function(card) {
  382.       if (!cards[card.id]) {
  383.         cards[card.id] = 1;
  384.       } else {
  385.         ++cards[card.id];
  386.       }
  387.     });
  388.  
  389.     let result = {};
  390.     for (let i in cards) {
  391.       result[i] = new tcard(get_card(i), cards[i]);
  392.     };
  393.     return result;
  394.   }
  395.  
  396.   /**
  397.    * Creates a download link for the deck.
  398.    *
  399.    * The download link contains a link to a CSV file. The file is stored in the
  400.    * HTML link. This makes it easy to download a deck.
  401.    */
  402.   get download_() {
  403.     const filename = `${this.name}.csv`;
  404.     let data = `${this.title}\n`;
  405.  
  406.     sort_cards(this.cards).forEach(function(i) {
  407.       if (!this.cards[i].is_general) {
  408.         data += this.cards[i].deck_list_csv;
  409.       }
  410.     }, this);
  411.  
  412.     const result = `<a download="${filename}" ` +
  413.                    `href="data:application/octet-stream;base64,${btoa(data)}"` +
  414.                    `>download</a>`;
  415.  
  416.     return result;
  417.   }
  418.  
  419.   /**
  420.    * Creates a link for the squad on the duelystdb site.
  421.    *
  422.    * The link contains a path and a parameter string. Some testing revealed the
  423.    * following properties of the string:
  424.    * - The parameter string is encoded as Base64 string.
  425.    * - The string contains a list of card pairs separated by a comma.
  426.    * - Every pair has two field separated by a colon:
  427.    *   - The number of copies of the card.
  428.    *   - The ID of the card as used in the game.
  429.    * - The list of pairs must be sorted by the card ID.
  430.    *
  431.    * @param cards                 A map of cards containg at least:
  432.    *                              - Key    The ID of the card.
  433.    *                              - Value  The card record containing:
  434.    *                                       * count  The number of copies in the
  435.    *                                                deck.
  436.    */
  437.   get duelystdb_() {
  438.     let data = "";
  439.     Object.keys(this.cards).sort().forEach(function(i) {
  440.       if (data != "") {
  441.         data += ",";
  442.       }
  443.       data += this.cards[i].deck_list_duelystdb;
  444.     }, this);
  445.  
  446.     const result = `<a href="http://duelystdb.com/squad/build#${btoa(data)}"` +
  447.                    ` target="_blank">duelystdb</a>`;
  448.  
  449.     return result;
  450.   }
  451.  
  452.   /** Creates the HTML table for the deck. */
  453.   get html_() {
  454.     let result = "<table>";
  455.     sort_cards(this.cards).forEach(function(i) {
  456.       if (!this.cards[i].is_general) {
  457.         result += this.cards[i].deck_list_html;
  458.       }
  459.     }, this);
  460.     result += "<table>";
  461.  
  462.     return result;
  463.   }
  464. }
  465.  
  466. /**
  467.  * Contains the collection of all user created decks.
  468.  *
  469.  * The class has the following member variables:
  470.  * * decks                        An array with @ref tdeck objects.
  471.  */
  472. class tdeck_collection {
  473.   /** Constructor. */
  474.   constructor() {
  475.     this.decks = this.load_decks_();
  476.   }
  477.  
  478.   /** Returns the TOC section of the deck collection. */
  479.   get toc() {
  480.     let result = `<li><a href="#decks">Squads</a></li><ul>`;
  481.     this.decks.forEach(function(deck) {
  482.       result += deck.toc;
  483.     });
  484.     result += "</ul>";
  485.  
  486.     return result;
  487.   }
  488.  
  489.   /** Returns the body section of the deck collection. */
  490.   get body() {
  491.     let result = `<h1 id="decks">Squads</h1>`;
  492.     this.decks.forEach(function(deck) {
  493.       result += `<h2 id="deck_${deck.id}">${escape_html(deck.title)}</h2>`;
  494.       result += deck.html_;
  495.     });
  496.  
  497.     return result;
  498.   }
  499.  
  500.   /** Returns the decks created by the user. */
  501.   load_decks_() {
  502.     let result = [];
  503.     InventoryManager.instance.decksCollection.models.forEach(function(model) {
  504.       result.push(new tdeck(model.attributes));
  505.     });
  506.     return result;
  507.   }
  508. }
  509. /*
  510.  * Contains the CSV specific functions.
  511.  */
  512. /*
  513.  * Contains the code to create a duelystdb link.
  514.  */
  515. /*
  516.  * Contains the functions regarding the user's card collection and the cards
  517.  * available in the game.
  518.  */
  519.  
  520. /**
  521.  * Is the card available for crafting?
  522.  *
  523.  * The function's implementation has been determined experimentally:
  524.  * - rarityIsCraftable  Only seems to list cards which can be crafted.
  525.  * - factionId          Determines the faction
  526.  *                      * IDs [1, 6] are the in game factions.
  527.  *                      * ID 100 are the neutrals.
  528.  *                      * ID 200 are the tutorial teachers.
  529.  * - isAvailable        Determines whether the card is available for usage. For
  530.  *                      example the monthly rewards are in the card database
  531.  *                      before they can be used, when the new month starts this
  532.  *                      flag is updated.
  533.  *
  534.  * @param card                    A card as returned by get_card.
  535.  *
  536.  * @returns                       True of craftable, false otherwise.
  537.  */
  538. var is_card_craftable = function(card) {
  539.   return card.rarityIsCraftable && card.factionId != 200 && card.isAvailable;
  540. };
  541.  
  542. /**
  543.  * Returns the faction index of a card.
  544.  *
  545.  * The returned index can be used as index in an array.
  546.  *
  547.  * The game's faction index is explained in is_card_craftable.
  548.  *
  549.  * @pre                           The faction is collectable, so one of the in
  550.  *                                game factions or a neutral card.
  551.  *
  552.  * @param card                    A card as returned by get_card.
  553.  *
  554.  * @returns                       A zero-based index.
  555.  */
  556. var get_card_faction_index = function(card) {
  557.   /*
  558.    * The min function is used to map neutrals after the normal factions.
  559.    */
  560.   return Math.min(card.factionId, faction_count) - 1;
  561. };
  562.  
  563. /**
  564.  * Returns the rarity index of a card.
  565.  *
  566.  * The returned index can be used as index in an array.
  567.  *
  568.  * @param card                    A card as returned by get_card.
  569.  *
  570.  * @returns                       A zero-based index.
  571.  */
  572. var get_card_rarity_index = function(card) {
  573.   return card.rarityId - 1
  574. };
  575.  
  576. /**
  577.  * Loads all craftable cards in the game.
  578.  *
  579.  * @returns                       An array with card record. The array is not
  580.  *                                sorted. The record has the following fields:
  581.  *                                - id             The ID of the card as used
  582.  *                                                 by Duelyst.
  583.  *                                - count          The number of copies of the
  584.  *                                                 card. In order to be
  585.  *                                                 compatible with other card
  586.  *                                                 records a count is required.
  587.  *                                                 The number is always 1.
  588.  *                                - name           The game of the card.
  589.  *                                - mana           The amount of mana the card
  590.  *                                                 costs.
  591.  *                                - faction_index  The index of the faction as
  592.  *                                                 returned by
  593.  *                                                 get_card_faction_index
  594.  *                                - rarity_index   The index of the rarity as
  595.  *                                                 returned by
  596.  *                                                 get_card_rarity_index
  597.  */
  598. var load_card_database = function() {
  599.   var result = [];
  600.   GameDataManager.instance.cardsCollection.models.forEach(function(model) {
  601.     var card = model.attributes;
  602.     if (!is_card_craftable(card)) {
  603.       return;
  604.     }
  605.  
  606.     var record = {};
  607.     record.id = card.id;
  608.     record.count = 1;
  609.  
  610.     record.name = card.name;
  611.     record.mana = card.manaCost;
  612.  
  613.     record.faction_index = get_card_faction_index(card);
  614.     record.rarity_index = get_card_rarity_index(card);
  615.  
  616.     result.push(record);
  617.  
  618.   });
  619.   return result;
  620. };
  621.  
  622. /**
  623.  * Loads all cards collected by the player.
  624.  *
  625.  * @returns                       An array with card record. The array is not
  626.  *                                sorted. The record is the same as returned by
  627.  *                                load_card_database, except for the count
  628.  *                                field. The count field contains the number of
  629.  *                                copies the player owns. (The number of items
  630.  *                                in this array is less than or equal to the
  631.  *                                number of items in the array returned by
  632.  *                                load_card_database.)
  633.  */
  634. var load_collection = function() {
  635.   var result = [];
  636.  
  637.   InventoryManager.instance.cardsCollection.models.forEach(function(model) {
  638.     var card = get_card(model.id);
  639.     if (!card.rarityIsCraftable) {
  640.       return;
  641.     }
  642.  
  643.     var record = {};
  644.     record.id = model.id;
  645.     record.count = model.attributes.count;
  646.  
  647.     record.name = card.name;
  648.     record.mana = card.manaCost;
  649.  
  650.     record.faction_index = get_card_faction_index(card);
  651.     record.rarity_index = get_card_rarity_index(card);
  652.  
  653.     result.push(record);
  654.   });
  655.  
  656.   return result;
  657. };
  658.  
  659. /**
  660.  * Create an empty matrix.
  661.  *
  662.  * It creates a 2-dimensional matrix faction_count x rarity_count initialised
  663.  * with 0.
  664.  */
  665. var create_matrix = function() {
  666.   var result = new Array(7);
  667.   for (var i = 0; i < 7; ++i) {
  668.     result[i] = new Array(4);
  669.     for (var j = 0; j < 4; ++j) {
  670.       result[i][j] = 0;
  671.     }
  672.   }
  673.   return result;
  674. };
  675.  
  676. /**
  677.  * Validates whether a faction and rarity index are valid.
  678.  *
  679.  * @param faction_index           The faction index to validate.
  680.  * @param rarity_index            The rarity index to validate.
  681.  *
  682.  * @returns                       Whether the indices are valid.
  683.  */
  684. var validate_index = function(faction_index, rarity_index) {
  685.   if (faction_index < 0) {
  686.     console.error("Faction index »" + faction_index + "« underflow.");
  687.     return false;
  688.   }
  689.  
  690.   if (faction_index >= faction_count) {
  691.     console.error("Faction index »" + faction_index + "« overflow.");
  692.     return false;
  693.   }
  694.  
  695.   if (rarity_index < 0) {
  696.     console.error("Rarity index »" + rarity_index + "« underflow.");
  697.     return false;
  698.   }
  699.  
  700.   if (rarity_index >= rarity_count) {
  701.     console.error("Rarity index »" + rarity_index + "« overflow.");
  702.     return false;
  703.   }
  704.  
  705.   return true;
  706. };
  707.  
  708. /**
  709.  * Builds a matrix with the number of collected usable cards.
  710.  *
  711.  * @param collection              A card collection as generated by
  712.  *                                load_collection.
  713.  *
  714.  * @returns                       A matrix as generated by create_matrix, but
  715.  *                                filled with the proper values.
  716.  */
  717. var build_collected_matrix = function(collection) {
  718.   var result = create_matrix();
  719.  
  720.   collection.forEach(function(card) {
  721.     if (!validate_index(card.faction_index, card.rarity_index)) {
  722.       return;
  723.     }
  724.     result[card.faction_index][card.rarity_index] +=
  725.         get_usable_count(card.count);
  726.   });
  727.  
  728.   return result;
  729. };
  730.  
  731. /**
  732.  * Builds a matrix with the number of collected unusable cards.
  733.  *
  734.  * @param collection              A card collection as generated by
  735.  *                                load_collection.
  736.  *
  737.  * @returns                       A matrix as generated by create_matrix, but
  738.  *                                filled with the proper values.
  739.  */
  740. var build_disenchant_matrix = function(collection) {
  741.   var result = create_matrix();
  742.  
  743.   collection.forEach(function(card) {
  744.     if (!validate_index(card.faction_index, card.rarity_index)) {
  745.       return;
  746.     }
  747.     result[card.faction_index][card.rarity_index] +=
  748.         get_disenchant_count(card.count);
  749.   });
  750.  
  751.   return result;
  752. };
  753.  
  754. /**
  755.  * `Shows' the body section for the game's card database.
  756.  *
  757.  * @returns                       The string containing the body section.
  758.  */
  759. var show_card_database = function() {
  760.   var result = "<h2 id=\"cp_database\">Card database</h2>";
  761.  
  762.   result +=
  763.       "<p>The table below shows the number of craftable cards in the game. " +
  764.       "In order to determine the number of cards required for a full " +
  765.       " collection these numbers need to be multiplied by the maximum " +
  766.       "number of copies in a squad (" + maximum_copies_per_deck + ").</p>";
  767.  
  768.   result += show_matrix(build_collected_matrix(load_card_database()));
  769.  
  770.   return result;
  771. };
  772.  
  773. /**
  774.  * `Shows' the body section for the user's useful card collection.
  775.  *
  776.  * @returns                       The string containing the body section.
  777.  */
  778. var show_collected_database = function(collection) {
  779.   var result = "<h2 id=\"cp_collected\">Card collection</h2>";
  780.  
  781.   result += "<p>The table below shows the number of usable cards collected. " +
  782.             " The definition of usable here is : &quot;A maximum of " +
  783.             maximum_copies_per_deck +
  784.             " copies of a card&quot;. Whether  the card is playable is " +
  785.             "in the eye of the beholder.</p>";
  786.  
  787.   result += show_matrix(build_collected_matrix(collection));
  788.  
  789.   result +=
  790.       "<p>In order to determine the spirit value of your collection, the " +
  791.       "number of cards need to be multiplied by their crafting cost as " +
  792.       "shown in the table below.</p>";
  793.  
  794.   result += "<table>";
  795.   result += "<tr><td>Rarity</td><td>Craft cost</td></tr>";
  796.   for (var i = 0; i < rarity_count; ++i) {
  797.     result += "<tr>";
  798.     result += "<td>" + rarity_names[i] + "</td>";
  799.     result += "<td>" + spirit_craft_cost[i] + "</td>";
  800.     result += "</tr>";
  801.   }
  802.   result += "</table>";
  803.  
  804.   return result;
  805. };
  806.  
  807. /**
  808.  * `Shows' the body section for the user's useless card collection.
  809.  *
  810.  * @returns                       The string containing the body section.
  811.  */
  812. var show_disenchantable_database = function(collection) {
  813.   var result = "<h2 id=\"cp_disenchantable\">Disenchant collection</h2>";
  814.  
  815.   result +=
  816.       "<p>The table below shows the number of useless cards collected. " +
  817.       "The definition of useless here is: &quot;Every card you have more " +
  818.       "than " + maximum_copies_per_deck + " copies of&quot;.</p>";
  819.  
  820.   result += show_matrix(build_disenchant_matrix(collection));
  821.  
  822.   result +=
  823.       "<p>In order to determine the amount of spirit that can be gained by " +
  824.       "disenchanting, the number of cards need to be multiplied by their " +
  825.       "disenchant value as shown in the table below.</p>";
  826.  
  827.   result += "<table>";
  828.   result += "<tr><td>Rarity</td><td>Disenchant value</td></tr>";
  829.   for (var i = 0; i < rarity_count; ++i) {
  830.     result += "<tr>";
  831.     result += "<td>" + rarity_names[i] + "</td>";
  832.     result += "<td>" + spirit_disenchant_value[i] + "</td>";
  833.     result += "</tr>";
  834.   }
  835.   result += "</table>";
  836.  
  837.   return result;
  838. };
  839.  
  840. /**
  841.  * `Shows' the card collection progress.
  842.  *
  843.  * It creates the header and body sections for the following data sets:
  844.  * - The game's card database.
  845.  * - The user's useful card collection.
  846.  * - The user's useless card collection.
  847.  *
  848.  * @returns                       A tuple containing:
  849.  *                                # The TOC part of the data.
  850.  *                                # The body part of the data.
  851.  */
  852. var show_collection_progress = function() {
  853.   var header = "<li><a href=\"#cp\">Collection progress</a></li><ul>";
  854.   header += "<li><a href=\"#cp_database\">Card database</a></li>";
  855.   header += "<li><a href=\"#cp_collected\">Card collection</a></li>";
  856.   header += "<li><a href=\"#cp_disenchantable\">Disenchant collection</a></li>";
  857.   header += "</ul>";
  858.  
  859.   var body = "<h1 id=\"cp\">Collection progress</h1>";
  860.   body += "This sections show the progress of your collection, the number " +
  861.           "of cards collected and the number that can savely be disenchanted.";
  862.  
  863.   body += show_card_database();
  864.  
  865.   var collection = load_collection();
  866.   body += show_collected_database(collection);
  867.   body += show_disenchantable_database(collection);
  868.  
  869.   var result = [ header, body ];
  870.   return result;
  871. };
  872.  
  873. /**
  874.  * Create an empty card list.
  875.  *
  876.  * @returns                       A card list. This is a matrix of @ref
  877.  *                                faction_count elements, initialised with
  878.  *                                empty arrays.
  879.  */
  880. function create_card_list() {
  881.   let result = [];
  882.   for (let i = 0; i < faction_count; ++i) {
  883.     result.push([]);
  884.   }
  885.   return result;
  886. }
  887.  
  888. /**
  889.  * Helper class to generate output for collection views.
  890.  *
  891.  * @note initially had helper functions but they had issues with calling the
  892.  * static @ref tcard member @ref get_collection_list_field_names. Also a helper
  893.  * class with static member functions didn't work. So now have a circular
  894.  * class, where the view holds the output generator and the output generator
  895.  * holds the collection view. (Not really happy with the design, but it works.)
  896.  *
  897.  * The class holds the following member variables:
  898.  * * collection                   The collection view we generate the output
  899.  *                                for.
  900.  *
  901.  */
  902. class tcollection_output_generator {
  903.   /**
  904.    * Constructor
  905.    *
  906.    * @param collection            The collection view we generate the output
  907.    *                              for. It requires the object to have the
  908.    *                              following members:
  909.    *                              * anchor    A HTML anchor name for the
  910.    *                                          section. For the faction based
  911.    *                                          anchors it uses this as a
  912.    *                                          basename.
  913.    *                              * title     The title of the collection view.
  914.    *                              * filename  The filename of the CSV files.
  915.    *                              * cards     The cards in the view.
  916.    */
  917.   constructor(collection) {
  918.     this.collection = collection;
  919.   }
  920.  
  921.   /** Returns the TOC section for the collection detail section. */
  922.   get details_toc() {
  923.     let result = `<li><a href="#${this.collection.anchor}">` +
  924.                  `${this.collection.title}</a> ` +
  925.                  `[${this.details_download_}]</li><ul>`;
  926.  
  927.     for (let i = 0; i < faction_count; ++i) {
  928.       result += `<li><a href="#${this.collection.anchor}_${i}">` +
  929.                 `${faction_names[i]}</a></li>`;
  930.     }
  931.     result += "</ul>";
  932.     return result;
  933.   }
  934.  
  935.   /** Returns the body section for the collection detail section. */
  936.   get details_body() {
  937.     let result =
  938.         `<h2 id="${this.collection.anchor}">${this.collection.title}</h2>`;
  939.  
  940.     for (let f = 0; f < faction_count; ++f) {
  941.       result +=
  942.           `<h3 id="${this.collection.anchor}_${f}">${faction_names[f]}</h3>`;
  943.  
  944.       if (this.collection.cards[f].length == 0) {
  945.         result += "There are no cards available in this section.";
  946.         continue;
  947.       }
  948.  
  949.       result += "<table><tr>";
  950.  
  951.       tcard.get_collection_list_field_names().forEach(function(name) {
  952.         result += `<td>${name}</td>`;
  953.       });
  954.       result += "</tr>";
  955.  
  956.       sort_cards(this.collection.cards[f]).forEach(function(c) {
  957.         result += `\n${this.collection.cards[f][c].collection_list_html}`;
  958.       }, this);
  959.  
  960.       result += "</table>";
  961.     }
  962.     return result;
  963.   }
  964.  
  965.   /** Returns the download links for the collection detail section. */
  966.   get details_download_() {
  967.     let data = "";
  968.     tcard.get_collection_detailed_list_field_names().forEach(function(name) {
  969.       if (data != "") {
  970.         data += "\t";
  971.       }
  972.       data += name;
  973.     });
  974.     data += "\n";
  975.  
  976.     for (let f = 0; f < faction_count; ++f) {
  977.       this.collection.cards[f].forEach(function(card) {
  978.         data += card.collection_detailed_list_csv;
  979.       });
  980.     }
  981.  
  982.     const result = `<a download="${this.collection.filename}" ` +
  983.                    `href="data:application/octet-stream;base64,${btoa(data)}"` +
  984.                    `>download</a>`;
  985.  
  986.     return result;
  987.   }
  988. }
  989.  
  990. /**
  991.  * Class containing a list with all cards available in the game.
  992.  *
  993.  * The class is a data collector for a @ref tcollection_output_generator. It
  994.  * has all member required for this class and an additional member, the output
  995.  * generator object.
  996.  */
  997. class tcards_available {
  998.   constructor() {
  999.     this.anchor = "cd_available";
  1000.     this.title = "Available";
  1001.     this.filename = "cards_available.csv";
  1002.     this.cards = this.load_cards_();
  1003.     this.output_generator = new tcollection_output_generator(this);
  1004.   }
  1005.  
  1006.  
  1007.   /** Forwarded to @ref tcollection_output_generator.details_toc. */
  1008.   get details_toc() {
  1009.     return this.output_generator.details_toc;
  1010.   }
  1011.  
  1012.   /** Forwarded to @ref tcollection_output_generator.details_body. */
  1013.   get details_body() {
  1014.     return this.output_generator.details_body;
  1015.   }
  1016.  
  1017.  
  1018.   /** Loads the cards. */
  1019.   load_cards_() {
  1020.     let result = create_card_list();
  1021.     GameDataManager.instance.cardsCollection.models.forEach(function(model) {
  1022.       let card = model.attributes;
  1023.       if (!is_card_craftable(card)) {
  1024.         return;
  1025.       }
  1026.       card = new tcard(card, 1);
  1027.       result[card.faction_index].push(card);
  1028.     });
  1029.     return result;
  1030.   }
  1031. }
  1032.  
  1033. /**
  1034.  * Class containing a list with all cards collected by the user.
  1035.  *
  1036.  * The class is a data collector for a @ref tcollection_output_generator. It
  1037.  * has all member required for this class and an additional member, the output
  1038.  * generator object.
  1039.  */
  1040. class tcards_collected {
  1041.   constructor() {
  1042.     this.anchor = "cd_collected";
  1043.     this.title = "Collected";
  1044.     this.filename = "cards_collected.csv";
  1045.     this.cards = this.load_cards_();
  1046.     this.output_generator = new tcollection_output_generator(this);
  1047.   }
  1048.  
  1049.  
  1050.   /** Forwarded to @ref tcollection_output_generator.details_toc. */
  1051.   get details_toc() {
  1052.     return this.output_generator.details_toc;
  1053.   }
  1054.  
  1055.   /** Forwarded to @ref tcollection_output_generator.details_body. */
  1056.   get details_body() {
  1057.     return this.output_generator.details_body;
  1058.   }
  1059.  
  1060.  
  1061.   /** Loads the cards. */
  1062.   load_cards_() {
  1063.     let result = create_card_list();
  1064.     InventoryManager.instance.cardsCollection.models.forEach(function(model) {
  1065.       let card = get_card(model.id);
  1066.       if (!card.rarityIsCraftable) {
  1067.         return;
  1068.       }
  1069.       card = new tcard(card, model.attributes.count);
  1070.       result[card.faction_index].push(card);
  1071.  
  1072.     });
  1073.     return result;
  1074.   }
  1075. }
  1076.  
  1077. /**
  1078.  * Class containing a list with all useful cards collected by the user.
  1079.  *
  1080.  * This class is a view of the collected cards by the user.
  1081.  *
  1082.  * The class is a data collector for a @ref tcollection_output_generator. It
  1083.  * has all member required for this class and an additional member, the output
  1084.  * generator object.
  1085.  */
  1086. class tcards_useful {
  1087.   /**
  1088.    * Constructor
  1089.    *
  1090.    * @param cards                 The cards member of a @ref tcards_collected
  1091.    *                              object containing all cards collected by the
  1092.    *                              user.
  1093.    */
  1094.   constructor(cards) {
  1095.     this.anchor = "cd_useful";
  1096.     this.title = "Useful";
  1097.     this.filename = "cards_useful.csv";
  1098.     this.cards = this.load_cards_(cards);
  1099.     this.output_generator = new tcollection_output_generator(this);
  1100.   }
  1101.  
  1102.  
  1103.   /** Forwarded to @ref tcollection_output_generator.details_toc. */
  1104.   get details_toc() {
  1105.     return this.output_generator.details_toc;
  1106.   }
  1107.  
  1108.   /** Forwarded to @ref tcollection_output_generator.details_body. */
  1109.   get details_body() {
  1110.     return this.output_generator.details_body;
  1111.   }
  1112.  
  1113.  
  1114.   /**
  1115.    * Creates a view of the collected cards.
  1116.    *
  1117.    * @param cards                 The cards member of a @ref tcards_collected
  1118.    *                              object containing all cards collected by the
  1119.    *                              user.
  1120.    */
  1121.   load_cards_(cards) {
  1122.     let result = create_card_list();
  1123.     for (let f = 0; f < faction_count; ++f) {
  1124.       for (let c in cards[f]) {
  1125.         let card = Object.create(cards[f][c]);
  1126.         card.count = get_usable_count(card.count);
  1127.         result[f].push(card);
  1128.       }
  1129.     }
  1130.     return result;
  1131.   }
  1132. }
  1133.  
  1134. /**
  1135.  * Class containing a list with all useless cards collected by the user.
  1136.  *
  1137.  * This class is a view of the collected cards by the user.
  1138.  *
  1139.  * The class is a data collector for a @ref tcollection_output_generator. It
  1140.  * has all member required for this class and an additional member, the output
  1141.  * generator object.
  1142.  */
  1143. class tcards_useless {
  1144.   /**
  1145.    * Constructor
  1146.    *
  1147.    * @param cards                 The cards member of a @ref tcards_collected
  1148.    *                              object containing all cards collected by the
  1149.    *                              user.
  1150.    */
  1151.   constructor(cards) {
  1152.     this.anchor = "cd_useless";
  1153.     this.title = "Useless";
  1154.     this.filename = "cards_useless.csv";
  1155.     this.cards = this.load_cards_(cards);
  1156.     this.output_generator = new tcollection_output_generator(this);
  1157.   }
  1158.  
  1159.  
  1160.   /** Forwarded to @ref tcollection_output_generator.details_toc. */
  1161.   get details_toc() {
  1162.     return this.output_generator.details_toc;
  1163.   }
  1164.  
  1165.   /** Forwarded to @ref tcollection_output_generator.details_body. */
  1166.   get details_body() {
  1167.     return this.output_generator.details_body;
  1168.   }
  1169.  
  1170.  
  1171.   /**
  1172.    * Creates a view of the collected cards.
  1173.    *
  1174.    * @param cards                 The cards member of a @ref tcards_collected
  1175.    *                              object containing all cards collected by the
  1176.    *                              user.
  1177.    */
  1178.   load_cards_(cards) {
  1179.     let result = create_card_list();
  1180.     for (let f = 0; f < faction_count; ++f) {
  1181.       for (let c in cards[f]) {
  1182.         const count = get_disenchant_count(cards[f][c].count);
  1183.         if (count > 0) {
  1184.           let card = Object.create(cards[f][c]);
  1185.           card.count = count;
  1186.           result[f].push(card);
  1187.         }
  1188.       }
  1189.     }
  1190.     return result;
  1191.   }
  1192. }
  1193.  
  1194. /**
  1195.  * Class containing a list with all cards the user misses for a full collection.
  1196.  *
  1197.  * This class is a view of the collected cards by the user.
  1198.  *
  1199.  * The class is a data collector for a @ref tcollection_output_generator. It
  1200.  * has all member required for this class and an additional member, the output
  1201.  * generator object.
  1202.  */
  1203. class tcards_missing {
  1204.   /**
  1205.    * Constructor
  1206.    *
  1207.    * @param available             A cards member of a @ref tcards_available
  1208.    *                              object containing all cards available in the
  1209.    *                              game.
  1210.    * @param collected             The cards member of a @ref tcards_collected
  1211.    *                              object containing all cards collected by the
  1212.    *                              user.
  1213.    */
  1214.   constructor(available, collected) {
  1215.     this.anchor = "cd_missing";
  1216.     this.title = "Missing";
  1217.     this.filename = "cards_missing.csv";
  1218.     this.cards = this.load_cards_(available, collected);
  1219.     this.output_generator = new tcollection_output_generator(this);
  1220.   }
  1221.  
  1222.  
  1223.   /** Forwarded to @ref tcollection_output_generator.details_toc. */
  1224.   get details_toc() {
  1225.     return this.output_generator.details_toc;
  1226.   }
  1227.  
  1228.   /** Forwarded to @ref tcollection_output_generator.details_body. */
  1229.   get details_body() {
  1230.     return this.output_generator.details_body;
  1231.   }
  1232.  
  1233.  
  1234.   /**
  1235.    * Creates a view of the collected cards.
  1236.    *
  1237.    * @param available             A cards member of a @ref tcards_available
  1238.    *                              object containing all cards available in the
  1239.    *                              game.
  1240.    * @param collected             The cards member of a @ref tcards_collected
  1241.    *                              object containing all cards collected by the
  1242.    *                              user.
  1243.    */
  1244.   load_cards_(available, collected) {
  1245.     let result = create_card_list();
  1246.     for (let f = 0; f < faction_count; ++f) {
  1247.       for (let c in available[f]) {
  1248.         /*
  1249.          * Find the card in our collection, so we can determine how many copies
  1250.          * we are missing. (It is possible we have 0 copies of the card and
  1251.          * will not be found in our collection.)
  1252.          */
  1253.         const index = collected[f]
  1254.                           .map(function(card) {
  1255.                             return card.id;
  1256.                           })
  1257.                           .indexOf(available[f][c].id);
  1258.  
  1259.         const count = index == -1
  1260.                           ? maximum_copies_per_deck
  1261.                           : get_missing_count(collected[f][index].count);
  1262.  
  1263.         if (count != 0) {
  1264.           let card = Object.create(available[f][c]);
  1265.           card.count = count;
  1266.           result[f].push(card);
  1267.         }
  1268.       }
  1269.     }
  1270.  
  1271.     return result;
  1272.   }
  1273. }
  1274.  
  1275. /**
  1276. * Contains the collection of in-game and user owned cards.
  1277. *
  1278. * The class holds two main variables containing the set of in-game craftable
  1279. * cards and the set of cards owned by the user. Next to these variables it
  1280. * contains views of the collection: the useful and useless cards. The
  1281. * usefulness is determined whether a card can be put in a deck, not whether
  1282. * it
  1283. * is a good idea to include a card in your deck.
  1284. *
  1285. * @note The class is still work-in-progress and future versions of the class
  1286. * will hold and generate more information.
  1287. *
  1288. * The class has the following member variables:
  1289. * * available                    A @ref tcards_available object with the cards
  1290. *                                available in the game. The count of the cards
  1291. *                                is always 1.
  1292. * * collected                    A @ref tcards_collected object with all cards
  1293. *                                collected by the user. The count of the cards
  1294. *                                is the number in the user's collection.
  1295. * * useful                       A @ref tcards_useful object with all useful
  1296. *                                cards collected by the user. The count of the
  1297. *                                cards <= 3.
  1298. * * useless                      A @ref tcards_useless object with all useless
  1299. *                                cards collected by the user. The count of
  1300. *                                cards > 0. If the user had collected less
  1301. *                                than 4 copies of a card it is not in this
  1302. *                                object.
  1303. */
  1304. class tcard_collection {
  1305.   constructor() {
  1306.     this.available = new tcards_available();
  1307.     this.collected = new tcards_collected();
  1308.     this.useful = new tcards_useful(this.collected.cards);
  1309.     this.useless = new tcards_useless(this.collected.cards);
  1310.     this.missing =
  1311.         new tcards_missing(this.available.cards, this.collected.cards);
  1312.   }
  1313.  
  1314.   get toc() {
  1315.     let result = "";
  1316.  
  1317.     result += `<li><a href="#cd">Collection details</a></li><ul>`;
  1318.     result += this.available.details_toc;
  1319.     result += this.collected.details_toc;
  1320.     result += this.useful.details_toc;
  1321.     result += this.useless.details_toc;
  1322.     result += this.missing.details_toc;
  1323.     result += "</ul>";
  1324.  
  1325.     return result;
  1326.   }
  1327.  
  1328.   get body() {
  1329.     let result = "";
  1330.  
  1331.     result += `<h1 id="cd">Collection details</h1>`;
  1332.     result += this.available.details_body;
  1333.     result += this.collected.details_body;
  1334.     result += this.useful.details_body;
  1335.     result += this.useless.details_body;
  1336.     result += this.missing.details_body;
  1337.  
  1338.     return result;
  1339.   }
  1340. }
  1341.  
  1342. var main = function() {
  1343.   var collection_progress = show_collection_progress();
  1344.  
  1345.   const deck_collection = new tdeck_collection();
  1346.   const card_collection = new tcard_collection();
  1347.  
  1348.   var page = "data:text/html;charset=utf-8,<html>";
  1349.   page += "<head><title>Duelyst export</title></head><body>";
  1350.  
  1351.   /*** TOC ***/
  1352.   page += "<h1>Table of contents</h1><ul>";
  1353.   page += deck_collection.toc;
  1354.   page += collection_progress[0];
  1355.   page += card_collection.toc;
  1356.  
  1357.   /*** Data ***/
  1358.   page += "<h1 id=\"decks\">Squads</h1><ul>";
  1359.   page += "</ul>";
  1360.   page += deck_collection.body;
  1361.   page += collection_progress[1];
  1362.   page += card_collection.body;
  1363.  
  1364.   page += "</body></html>";
  1365.   window.open(page);
  1366. };
  1367.  
  1368. main();
  1369. })();
  1370. /*
  1371.  * Changelog:
  1372.  * 0.4.0
  1373.  * - Fixed a bug in the crafting cost table.
  1374.  * - Added a CSV file download link for decks.
  1375.  * - Changed refactored code.
  1376.  * - Added details of your card collection and available cards.
  1377.  *
  1378.  * 2016.03.09 0.3.0
  1379.  * - Added experimental support for duelystdb links.
  1380.  *
  1381.  * 2016.03.09 0.2.0
  1382.  * - Added the collection progress part.
  1383.  *
  1384.  * 2016.03.05 0.1.0 - Initial release.
  1385.  */
Add Comment
Please, Sign In to add comment