// Constants ////////////////////////////////////////////////////////////////////////////////////////////////////////// var ERROR_DISPLAY_DURATION = 10000; // ms var FADE_DURATION = 250; // ms var SLIDE_DURATION = 600; // ms // Global Variables /////////////////////////////////////////////////////////////////////////////////////////////////// var __inventory = createManifest(); var __loadingCount = 0; var __recipebooks = []; var __toolsIncluded = false; // UI Functions /////////////////////////////////////////////////////////////////////////////////////////////////////// $(function() { $("#recipebook_load_button").click(onRecipeLoadButtonClicked); $("#crafting_count").change(onCraftingSelectorChanged); $("#tools_included").change(onIncludeToolsChanged); $("#crafting_selector").focus(onCraftingSelectorFocused); $("#inventory").keyup(onInventoryChanged); loadRecipebook("data/vanilla.json"); loadRecipebook("data/buildcraft.json"); loadRecipebook("data/industrial_craft.json"); }); function onCraftingSelectorChanged() { var recipeName = $("#crafting_selector").val(); var count = parseInt($("#crafting_count option:selected").val()); if (count === undefined) return; var recipe = findRecipe(recipeName); if (recipe === undefined) { $("#crafting_output").slideUp(SLIDE_DURATION); } else { var inventory = __inventory.copy(); var craftedItems = createManifest(); var missingMaterials = createManifest(); for (var i = 0; i < count; i++) { recipe.craft(inventory, craftedItems, missingMaterials); if (inventory.contains(count, recipeName)) break; } $("#missing_materials").html(missingMaterials.toHtml()); $("#crafted_items").html(craftedItems.toHtml()); $("#leftover_materials").html(inventory.toHtml()); $("#crafting_output").slideDown(SLIDE_DURATION); } updatePageState(count, recipeName); } function onCraftingSelectorFocused() { $("#crafting_selector").autocomplete("search"); } function onIncludeToolsChanged() { __toolsIncluded = $("#tools_included").is(":checked"); onCraftingSelectorChanged() } function onInventoryChanged() { __inventory.removeAll(); var inventoryText = $("#inventory").val(); if (inventoryText) { var inventoryLines = inventoryText.split("\n"); for (var i = 0; i < inventoryLines.length; i++) { var line = inventoryLines[i].trim(); var match = /^([0-9]+)(.*)$/.exec(line); var count = (match) ? parseInt(match[1]) : 1; var itemName = (match) ? match[2].trim() : line; if (itemName.length > 0) { __inventory.add(count, itemName); } } } onCraftingSelectorChanged(); } function onRecipeBookLoaded() { if (__loadingCount > 0 || __recipebooks.length === 0) { $("#crafting").fadeOut(FADE_DURATION); } else if (__loadingCount === 0 && __recipebooks.length > 0) { updateCraftingSelector(); parseUrlParameters(); $("#crafting").fadeIn(FADE_DURATION); } } function onRecipeLoadButtonClicked() { $("#reipebook_load_button").attr("disabled", true); loadRecipebook($("#recipebook_url").val(), function() { $("#recipebook_url").val(""); $("#reipebook_load_button").removeAttr("disabled"); }); } // UI Helper Functions //////////////////////////////////////////////////////////////////////////////////////////////// function loadRecipebook(recipebookUrl) { __loadingCount++; $.ajax({ url: recipebookUrl, dataType: "text", success: function(text) { try { var recipebook = createRecipebook(recipebookUrl, $.parseJSON(text)); __recipebooks.push(recipebook); recipebook.asTableRow().insertBefore($("#recipebook_table tr").last()); } catch (e) { $("#load_error").html(e); $("#load_error").slideDown(FADE_DURATION).delay(ERROR_DISPLAY_DURATION).slideUp(FADE_DURATION); } }, error: function(response, status, error) { $("#load_error").html(status + ": " + error); $("#load_error").slideDown(FADE_DURATION).delay(ERROR_DISPLAY_DURATION).slideUp(FADE_DURATION); }, complete: function() { __loadingCount--; onRecipeBookLoaded(); } }); } function parseUrlParameters() { var quantity = $.url().param('quantity'); if (quantity !== undefined) { $("#crafting_count").val(quantity); if ($("#crafting_count option:selected").val() === undefined) { $("#crafting_count").val(1); $("#crafting_error").html("Unsupported quantity: " + quantity); $("#crafting_error").fadeIn(FADE_DURATION).delay(ERROR_DISPLAY_DURATION).fadeOut(FADE_DURATION); } } var recipeName = $.url().param('recipeName'); if (recipeName !== undefined) { $("#crafting_selector").val(recipeName); if (findRecipe(recipeName) !== undefined) { onCraftingSelectorChanged(); } else if (recipeName.length > 0) { $("#crafting_error").html("Cannot find recipe for " + recipeName); $("#crafting_error").fadeIn(FADE_DURATION).delay(ERROR_DISPLAY_DURATION).fadeOut(FADE_DURATION); } } } function updateCraftingSelector() { var $selector = $("crafting_selector"); var names = []; $(__recipebooks).each(function(i, recipebook) { for (var name in recipebook.recipes) { names.push(name); } }); names.sort(); $("#crafting_selector").autocomplete({ source: names, delay: 0, minLength: 0, change: onCraftingSelectorChanged, close: onCraftingSelectorChanged, select: onCraftingSelectorChanged, }); } function updatePageState(count, recipeName) { var newTitle = "Crafting Guide"; var newLink = window.location.pathname; var state = {} if (recipeName !== undefined && recipeName.length > 0) { newTitle = "Crafting Guide: " + count + " " + recipeName; newLink = "?count=" + count + "&recipeName=" + encodeURIComponent(recipeName); newLink = newLink.replace(/%20/g, "+"); state = {count: count, name: recipeName}; } document.title = newTitle; history.pushState(state, newTitle, newLink); ga('send', 'pageview'); } // Manifest Object //////////////////////////////////////////////////////////////////////////////////////////////////// function createManifest() { var object = {materials: {}}; object.add = function(count, name) { if (object.materials[name] === undefined) { object.materials[name] = count; } else { object.materials[name] = object.materials[name] + count; } }; object.contains = function(count, name) { if (object.materials[name] === undefined) return false; return (object.materials[name] >= count); }; object.copy = function(count, name) { var result = createManifest(); for (var name in object.materials) { result.add(object.materials[name], name); } return result; } object.remove = function(count, name) { if (object.materials[name] !== undefined) { if (object.materials[name] <= count) { delete object.materials[name] } else { object.materials[name] -= count; } } }; object.removeAll = function() { object.materials = {}; } object.toHtml = function() { var result = ""; var count = 0; for (var name in object.materials) { result += "
  • " + object.materials[name] + " " + name + "
  • "; count += 1 } if (count === 0) result = "
  • Nothing
  • "; return result; }; return object; } // Recipe Object ////////////////////////////////////////////////////////////////////////////////////////////////////// function createRecipe(data) { var object = {output: [], input: [], tools: data.tools}; $.each(data.output, function(index, value) { object.output.push({count: value[0], name: value[1]}); }); $.each(data.input, function(index, value) { object.input.push({count: value[0], name: value[1]}); }); // Methods //////////////////////////////////////////// object.craft = function(inventory, craftedItems, missingMaterials) { if (inventory === undefined) inventory = createManifest(); if (craftedItems === undefined) craftedItems = createManifest(); if (missingMaterials === undefined) missingMaterials = createManifest(); var toCraftManifest = createManifest(); function drainCraftingManifest(consumeItems) { for (var name in toCraftManifest.materials) { var count = toCraftManifest.materials[name]; var itemRecipe = findRecipe(name); for (var i = 0; i < count; i++) { if (inventory.contains(1, name)) { if (consumeItems) { inventory.remove(1, name); } } else if (itemRecipe === undefined) { missingMaterials.add(1, name); if (! consumeItems) { inventory.add(1, name); } } else { itemRecipe.craft(inventory, craftedItems, missingMaterials); if (consumeItems) { inventory.remove(1, name); } } } } toCraftManifest.removeAll(); } if (__toolsIncluded) { $(object.tools).each(function(i, tool) { if (! inventory.contains(1, tool)) { toCraftManifest.add(1, tool); } }); drainCraftingManifest(false); } $(object.input).each(function(i, input) { for (var j = 0; j < input.count; j++) { toCraftManifest.add(1, input.name); } }); drainCraftingManifest(true); $(object.output).each(function(i, output) { craftedItems.add(output.count, output.name); inventory.add(output.count, output.name); }); }; return object; } // RecipeBook Object ////////////////////////////////////////////////////////////////////////////////////////////////// function createRecipebook(sourceUrl, data) { var object = { name: data.name, description: data.description, sourceUrl: sourceUrl, recipes: {} }; $(data.recipes).each(function(i, data) { var recipe = createRecipe(data); $(recipe.output).each(function(j, output) { object.recipes[output.name] = recipe }); }); object.asTableRow = function() { return $( "" + "" + object.name + "" + "" + object.description + "" + "" ); }; return object; } function findRecipe(name) { for (var i = 0; i < __recipebooks.length; i++) { var recipe = __recipebooks[i].recipes[name]; if (recipe !== undefined) return recipe; } return undefined; }