diff --git a/src/scripts/constants.coffee b/src/scripts/constants.coffee index 074728e71..4ed133b7e 100644 --- a/src/scripts/constants.coffee +++ b/src/scripts/constants.coffee @@ -56,7 +56,7 @@ Text.title = 'Crafting Guide for Minecraft | The Ultimate Step-by-Step Tutorial exports.Url = Url = {} Url.crafting = _.template "/crafting/<%= inventoryText %>" Url.itemIcon = _.template "/data/<%= modSlug %>/<%= modVersion %>/images/<%= slug %>.png" -Url.item = _.template "/item/<%= slug %>" +Url.item = _.template "/mod/<%= modSlug %>/<%= slug %>" Url.mod = _.template "/mod/<%= modSlug %>" Url.modData = _.template "/data/<%= modSlug %>/mod.cg" Url.modVersion = _.template "/data/<%= modSlug %>/<%= modVersion %>/mod-version.cg" diff --git a/src/scripts/controllers/full_recipe_controller.coffee b/src/scripts/controllers/full_recipe_controller.coffee index ea2f355d0..2fcf6d6d7 100644 --- a/src/scripts/controllers/full_recipe_controller.coffee +++ b/src/scripts/controllers/full_recipe_controller.coffee @@ -68,8 +68,8 @@ module.exports = class FullRecipeController extends BaseController result = [] if @model? for stack in @model.tools - name = @modPack.findName stack.slug - result.push name if name? + item = @modPack.findItem stack.slug + result.push item.name if item? return result _refreshInputs: -> diff --git a/src/scripts/controllers/item_controller.coffee b/src/scripts/controllers/item_controller.coffee index 868753b7f..61f294431 100644 --- a/src/scripts/controllers/item_controller.coffee +++ b/src/scripts/controllers/item_controller.coffee @@ -28,7 +28,7 @@ module.exports = class ItemController extends BaseController super refresh: -> - display = @_modPack.findItemDisplay @model.slug + display = @_modPack.findItemDisplay @model.qualifiedSlug @$icon.attr 'src', display.iconUrl @$name.html display.itemName diff --git a/src/scripts/controllers/item_page_controller.coffee b/src/scripts/controllers/item_page_controller.coffee index b672376e4..4ddceb9b9 100644 --- a/src/scripts/controllers/item_page_controller.coffee +++ b/src/scripts/controllers/item_page_controller.coffee @@ -57,7 +57,7 @@ module.exports = class ItemPageController extends BaseController $('title').html if @model.item? then "#{@model.item.name} | #{Text.title}" else Text.title @_resolveItemSlug() - display = @_modPack.findItemDisplay @model.item?.slug + display = @_modPack.findItemDisplay @model.item?.qualifiedSlug if display? @$craftingPlanLink.attr href:display.craftingUrl @$craftingPlanLink.fadeIn duration:Duration.normal @@ -143,6 +143,4 @@ module.exports = class ItemPageController extends BaseController @$similarContainer.fadeOut duration:Duration.normal _resolveItemSlug: -> - oldItem = @model.item @model.item = @_modPack.findItem @_itemSlug, includeDisabled:true - newItem = @model.item diff --git a/src/scripts/controllers/minimal_recipe_controller.coffee b/src/scripts/controllers/minimal_recipe_controller.coffee index 75d554f1c..3c9a6075f 100644 --- a/src/scripts/controllers/minimal_recipe_controller.coffee +++ b/src/scripts/controllers/minimal_recipe_controller.coffee @@ -72,6 +72,6 @@ module.exports = class MinimalRecipeController extends BaseController result = [] if @model? for stack in @model.tools - name = @_modPack.findName stack.slug - result.push name if name? + item = @_modPack.findItem stack.slug + result.push item.name if item? return result diff --git a/src/scripts/crafting_guide_router.coffee b/src/scripts/crafting_guide_router.coffee index c61f1aee4..9229cf402 100644 --- a/src/scripts/crafting_guide_router.coffee +++ b/src/scripts/crafting_guide_router.coffee @@ -57,10 +57,11 @@ module.exports = class CraftingGuideRouter extends Backbone.Router @_recordPageView() routes: - '': 'root' - 'item/:itemSlug': 'item' - 'crafting/(:text)': 'crafting' - 'mod/:modSlug': 'mod' + '': 'root' + 'item/:itemSlug': 'item' + 'crafting/(:text)': 'crafting' + 'mod/:modSlug': 'mod' + 'mod/:modSlug/:itemSlug': 'modItem' # Route Methods ################################################################################ @@ -69,7 +70,12 @@ module.exports = class CraftingGuideRouter extends Backbone.Router controller.model.params = inventoryText:text @_setPage 'crafting', controller - item: (slug)-> + item: (itemSlug)-> + controller = new ItemPageController _.extend {itemSlug:itemSlug}, @_defaultOptions + @_setPage 'item', controller + + modItem: (modSlug, itemSlug)-> + slug = _.composeSlugs modSlug, itemSlug controller = new ItemPageController _.extend {itemSlug:slug}, @_defaultOptions @_setPage 'item', controller @@ -84,9 +90,9 @@ module.exports = class CraftingGuideRouter extends Backbone.Router text = '' if params.recipeName? if params.count? - text = "#{params.count}.#{params.recipeName}" + text = "#{params.count}.#{_.slugify(params.recipeName)}" else - text = "#{params.recipeName}" + text = _.slugify params.recipeName @navigate Url.crafting(inventoryText:text), trigger:true diff --git a/src/scripts/main.coffee b/src/scripts/main.coffee index a7103ac29..bfedb339f 100644 --- a/src/scripts/main.coffee +++ b/src/scripts/main.coffee @@ -24,7 +24,7 @@ global.logger = new Logger switch window.location.hostname when 'localhost' global.env = 'development' - logger.level = Logger.INFO + logger.level = Logger.DEBUG when 'new.crafting-guide.com' global.env = 'staging' logger.level = Logger.VERBOSE diff --git a/src/scripts/models/base_model.coffee b/src/scripts/models/base_model.coffee index 777ad783a..825190cbb 100644 --- a/src/scripts/models/base_model.coffee +++ b/src/scripts/models/base_model.coffee @@ -25,10 +25,6 @@ module.exports = class BaseModel extends Backbone.Model @logEvents = options.logEvents or false @state = ModelState.unloaded - @on 'request', => @state = ModelState.loading - @on 'sync', => @state = ModelState.loaded - @on 'error', => @state = ModelState.error - @loading = null Object.defineProperties this, @@ -42,6 +38,8 @@ module.exports = class BaseModel extends Backbone.Model onLoadSucceeded: (text, status, xhr)-> try @set @parse text + + @state = ModelState.loaded @trigger Event.change, this @trigger Event.sync, this logger.info => "#{@constructor.name}.#{@cid} loaded successfully" @@ -50,6 +48,7 @@ module.exports = class BaseModel extends Backbone.Model @onLoadFailed e.message, 'parsing failed', xhr onLoadFailed: (error, status, xhr)-> + @state = ModelState.error logger.error => "#{@constructor.name}.#{@cid} failed to load: status:#{status}, message:#{error}" @trigger Event.error, this, error @@ -62,6 +61,7 @@ module.exports = class BaseModel extends Backbone.Model url = @url() logger.info => "#{@constructor.name}.#{@cid} reading from url: #{url}" + @state = ModelState.loading @trigger Event.request, this @loading = w.promise (resolve, reject)=> $.ajax diff --git a/src/scripts/models/crafting_page.coffee b/src/scripts/models/crafting_page.coffee index 2572e9fb1..1d304a896 100644 --- a/src/scripts/models/crafting_page.coffee +++ b/src/scripts/models/crafting_page.coffee @@ -14,7 +14,7 @@ ModPack = require './mod_pack' ######################################################################################################################## -module.exports = class extends BaseModel +module.exports = class CraftingPage extends BaseModel constructor: (attributes={}, options={})-> attributes.modPack ?= new ModPack @@ -40,7 +40,7 @@ module.exports = class extends BaseModel inventory = @_parser.parse @params.inventoryText inventory.each (stack)=> - item = @modPack.findItemByName stack.slug, enableAsNeeded:true + item = @modPack.findItem stack.slug, enableAsNeeded:true return unless item? and item.isCraftable @plan.want.add stack.slug, stack.quantity inventory.remove stack.slug diff --git a/src/scripts/models/crafting_plan.coffee b/src/scripts/models/crafting_plan.coffee index 4f2bc5f69..41cbd0d56 100644 --- a/src/scripts/models/crafting_plan.coffee +++ b/src/scripts/models/crafting_plan.coffee @@ -23,14 +23,14 @@ module.exports = class CraftingPlan extends BaseModel @need = new Inventory @result = new Inventory + recraft = _.debounce (=> @craft()), 100 + for inventory in [@have, @want] + inventory.on 'change', recraft + + @on Event.change + ':includingTools', recraft + @clear() - @have.on Event.change, => @craft() - @want.on Event.change, => @craft() - @modPack.on Event.change, => @craft() - - @on Event.change + ':includingTools', => @craft() - # Public Methods ############################################################################### clear: (options={})-> @@ -42,7 +42,12 @@ module.exports = class CraftingPlan extends BaseModel return this craft: -> + toolsMessage = if @includingTools then ' (including tools)' else '' + logger.info => "crafting #{@want}#{toolsMessage} starting with #{@have}" + @clear() + @have.localizeTo @modPack + @want.localizeTo @modPack @result.addInventory @have @@ -50,12 +55,14 @@ module.exports = class CraftingPlan extends BaseModel @_reservedSteps = {} @want.each (stack)=> @_findSteps stack.slug - @need.add stack.slug, stack.quantity + item = @modPack.findItem stack.slug + @need.add item.qualifiedSlug, stack.quantity @_reservedSteps = null @steps = _.values @steps @_resolveNeeds() @_removeExtraSteps() + @result.addInventory @want @need.trigger 'change', @need @@ -91,14 +98,13 @@ module.exports = class CraftingPlan extends BaseModel # Private Methods ############################################################################## _addStep: (recipe)-> - logger.verbose -> "adding step: #{recipe.slug}" - @steps[recipe.slug] = recipe:recipe + logger.verbose -> "adding step: #{recipe.item.qualifiedSlug}" + @steps[recipe.item.qualifiedSlug] = recipe:recipe _chooseRecipe: (item)-> return item.getPrimaryRecipe() _findSteps: (slug)-> - logger.debug -> "finding steps for #{slug}" item = @modPack.findItem slug return unless item? return unless item.isCraftable @@ -111,9 +117,8 @@ module.exports = class CraftingPlan extends BaseModel if not @_hasStep toolStack.slug @_findSteps toolStack.slug - return if @_hasStep item.slug - logger.debug -> "reserving: #{item.slug}" - @_reservedSteps[item.slug] = recipe + return if @_hasStep item.qualifiedSlug + @_reservedSteps[item.qualifiedSlug] = recipe for inputStack in recipe.input @_findSteps inputStack.slug @@ -125,6 +130,11 @@ module.exports = class CraftingPlan extends BaseModel return true if @_reservedSteps[slug]? return false + _qualifyItemSlug: (slug)-> + item = @modPack.findItem slug + return item.qualifiedSlug if item? + return slug + _removeExtraSteps: -> result = (step for step in @steps when step.multiplier > 0) @steps = result @@ -134,29 +144,31 @@ module.exports = class CraftingPlan extends BaseModel step = @steps[i] recipe = step.recipe - step.multiplier = Math.ceil(@need.quantityOf(recipe.slug) / step.recipe.output[0].quantity) + step.multiplier = Math.ceil(@need.quantityOf(recipe.slug) / recipe.output[0].quantity) if @includingTools for stack in recipe.tools - slug = stack.slug + slug = @_qualifyItemSlug stack.slug available = @result.quantityOf(slug) + @need.quantityOf(slug) needed = Math.max 0, stack.quantity - available - @need.add stack.slug, needed - @result.add stack.slug, needed + @need.add slug, needed + @result.add slug, needed for stack in recipe.input + slug = @_qualifyItemSlug stack.slug needed = step.multiplier * stack.quantity - consumed = Math.min needed, @result.quantityOf(stack.slug) + consumed = Math.min needed, @result.quantityOf slug remaining = needed - consumed - @result.remove stack.slug, consumed - @need.add stack.slug, remaining + @result.remove slug, consumed + @need.add slug, remaining for stack in recipe.output + slug = @_qualifyItemSlug stack.slug created = stack.quantity * step.multiplier - consumed = Math.min created, @need.quantityOf stack.slug + consumed = Math.min created, @need.quantityOf slug remaining = created - consumed - @result.add stack.slug, remaining - @need.remove stack.slug, consumed + @result.add slug, remaining + @need.remove slug, consumed diff --git a/src/scripts/models/inventory.coffee b/src/scripts/models/inventory.coffee index 9d65e0fc3..9da1d2dc5 100644 --- a/src/scripts/models/inventory.coffee +++ b/src/scripts/models/inventory.coffee @@ -5,9 +5,10 @@ Copyright (c) 2014-2015 by Redwood Labs All rights reserved. ### -BaseModel = require './base_model' -{Event} = require '../constants' -Stack = require './stack' +BaseModel = require './base_model' +{Event} = require '../constants' +{RequiredMods} = require '../constants' +Stack = require './stack' ######################################################################################################################## @@ -59,6 +60,22 @@ module.exports = class Inventory extends BaseModel return false unless stack? return stack.quantity >= quantity + localizeTo: (modPack)-> + newSlugs = [] + for slug in @_slugs + stack = @_stacks[slug] + qualifiedSlug = modPack.findItem(slug)?.qualifiedSlug + if qualifiedSlug? + delete @_stacks[slug] + newSlugs.push qualifiedSlug + @_stacks[qualifiedSlug] = stack + stack.slug = qualifiedSlug + else + throw new Error "could not find an item for: #{slug}" + + @_slugs = newSlugs + @_sort() + pop: -> slug = @_slugs.pop() return null unless slug? @@ -129,6 +146,22 @@ module.exports = class Inventory extends BaseModel stack = new Stack slug:slug, quantity:quantity @_stacks[slug] = stack @_slugs.push slug - @_slugs.sort() + @_sort() else stack.quantity += quantity + + _sort: -> + @_slugs.sort (a, b)-> + [modSlugA, itemSlugA] = _.decomposeSlug a + [modSlugB, itemSlugB] = _.decomposeSlug b + isRequiredA = modSlugA in RequiredMods + isRequiredB = modSlugB in RequiredMods + + if isRequiredA isnt isRequiredB + return -1 if isRequiredA + return +1 if isRequiredB + else if modSlugA isnt modSlugB + return if modSlugA < modSlugB then -1 else +1 + else if itemSlugA isnt itemSlugB + return if itemSlugA < itemSlugB then -1 else +1 + return 0 diff --git a/src/scripts/models/inventory_parser.coffee b/src/scripts/models/inventory_parser.coffee index 7119cdcf0..3521f722d 100644 --- a/src/scripts/models/inventory_parser.coffee +++ b/src/scripts/models/inventory_parser.coffee @@ -33,10 +33,10 @@ module.exports = class InventoryParser stackParts = stackText.split InventoryParser.ITEM_DELIMITER if stackParts.length is 2 quantity = parseInt stackParts[0] - slug = _.slugify stackParts[1] + slug = stackParts[1] else if stackParts.length is 1 quantity = 1 - slug = _.slugify stackParts[0] + slug = stackParts[0] else throw new Error "expected #{stackText} to have 0 or 1 parts" diff --git a/src/scripts/models/item.coffee b/src/scripts/models/item.coffee index 9cb620969..4480c3ace 100644 --- a/src/scripts/models/item.coffee +++ b/src/scripts/models/item.coffee @@ -21,28 +21,33 @@ module.exports = class Item extends BaseModel attributes.group ?= Item.Group.Other attributes.isGatherable ?= false + attributes.modVersion ?= null attributes.slug ?= _.slugify attributes.name + options.logEvents ?= false super attributes, options @_recipes = [] + Object.defineProperties this, isCraftable: { get:-> @_recipes.length > 0 } + qualifiedSlug: { get:@getQualifiedSlug } primaryRecipe: { get:@getPrimaryRecipe } + @on Event.change + ':modVersion', => @_qualifiedSlug = null + # Public Methods ############################################################################### addRecipe: (recipe)-> - if recipe.slug isnt @slug then throw new Error "cannot add a recipe for #{recipe.slug} to #{@slug}" + [modSlug, itemSlug] = _.decomposeSlug recipe.slug + if itemSlug isnt @slug then throw new Error "cannot add a recipe for #{recipe.slug} to #{@slug}" + recipe.item = this @_recipes.push recipe eachRecipe: (callback)-> for recipe in @_recipes callback recipe - getPrimaryRecipe: -> - return @_recipes[0] - compareTo: (that)-> if this.slug isnt that.slug return if this.slug < that.slug then -1 else +1 @@ -50,6 +55,19 @@ module.exports = class Item extends BaseModel return if this.name < that.name then -1 else +1 return 0 + # Property Methods ############################################################################# + + getQualifiedSlug: -> + return @slug if not @modVersion? + + if not @_qualifiedSlug? + @_qualifiedSlug = _.composeSlugs @modVersion.modSlug, @slug + + return @_qualifiedSlug + + getPrimaryRecipe: -> + return @_recipes[0] + # Object Overrides ############################################################################# toString: -> diff --git a/src/scripts/models/item_page.coffee b/src/scripts/models/item_page.coffee index cb905bd3a..746d142fa 100644 --- a/src/scripts/models/item_page.coffee +++ b/src/scripts/models/item_page.coffee @@ -31,14 +31,14 @@ module.exports = class ItemPage extends BaseModel findComponentInItems: -> return null unless @item? - itemSlug = @item.slug + itemSlug = @item.qualifiedSlug result = {} @modPack.eachMod (mod)-> mod.eachItem (item)-> item.eachRecipe (recipe)-> for stack in recipe.input if stack.slug is itemSlug - result[item.slug] = item + result[item.qualifiedSlug] = item result = _.values(result).sort (a, b)-> a.compareTo b return null unless result.length > 0 @@ -56,7 +56,7 @@ module.exports = class ItemPage extends BaseModel return result findRecipes: -> - result = @modPack.findRecipes @item?.slug + result = @modPack.findRecipes @item?.qualifiedSlug return null unless result.length > 0 return result @@ -66,7 +66,7 @@ module.exports = class ItemPage extends BaseModel @_plan.clear() if @item? - @_plan.want.add @item.slug + @_plan.want.add @item.qualifiedSlug @_plan.craft() if @_plan.steps.length > 0 diff --git a/src/scripts/models/mod.coffee b/src/scripts/models/mod.coffee index 9e9dc2ad4..45a07a74b 100644 --- a/src/scripts/models/mod.coffee +++ b/src/scripts/models/mod.coffee @@ -21,6 +21,7 @@ module.exports = class Mod extends BaseModel attributes.documentationUrl ?= null attributes.downloadUrl ?= null attributes.homePageUrl ?= null + attributes.modPack ?= null attributes.name ?= '' super attributes, options diff --git a/src/scripts/models/mod_pack.coffee b/src/scripts/models/mod_pack.coffee index cc8fc06bb..96c7b2601 100644 --- a/src/scripts/models/mod_pack.coffee +++ b/src/scripts/models/mod_pack.coffee @@ -25,9 +25,16 @@ module.exports = class ModPack extends BaseModel findItem: (slug, options={})-> options.includeDisabled ?= false + [modSlug, itemSlug] = _.decomposeSlug slug + if modSlug? + mod = @getMod modSlug + if mod? + item = mod.findItem itemSlug, options + return item if item? + for mod in @_mods continue unless mod.enabled or options.includeDisabled - item = mod.findItem slug, options + item = mod.findItem itemSlug, options return item if item? return null @@ -36,10 +43,9 @@ module.exports = class ModPack extends BaseModel options.enableAsNeeded ?= false options.includeDisabled = true if options.enableAsNeeded - slug = _.slugify name for mod in @_mods continue unless mod.enabled or options.includeDisabled - item = mod.findItem slug, options + item = mod.findItemByName name, options return item if item? return null @@ -74,6 +80,14 @@ module.exports = class ModPack extends BaseModel return null findRecipes: (slug, result=[])-> + [modSlug, itemSlug] = _.decomposeSlug slug + + if modSlug? + mod = @getMod modSlug + if mod? + mod.findRecipes slug, result + return result if result.length > 0 + for mod in @_mods continue unless mod.enabled mod.findRecipes slug, result @@ -88,12 +102,9 @@ module.exports = class ModPack extends BaseModel isValidName: (name)-> slug = _.slugify name - for mod in @_mods - continue unless mod.enabled - name = mod.findName slug - return true if name + existingName = @findName slug - return false + return name is existingName # Property Methods ############################################################################# @@ -101,6 +112,7 @@ module.exports = class ModPack extends BaseModel if not mod? then throw new Error 'mod is required' return if @_mods.indexOf(mod) isnt -1 + mod.modPack = this @_mods.push mod @listenTo mod, Event.change, => @trigger Event.change, this @trigger Event.add + ':mod', mod, this diff --git a/src/scripts/models/mod_version.coffee b/src/scripts/models/mod_version.coffee index 2dcfac51c..4d13d1652 100644 --- a/src/scripts/models/mod_version.coffee +++ b/src/scripts/models/mod_version.coffee @@ -19,6 +19,7 @@ module.exports = class ModVersion extends BaseModel constructor: (attributes={}, options={})-> if not attributes.modSlug? then throw new Error 'attributes.modSlug is required' if not attributes.version? then throw new Error 'attributes.version is required' + attributes.mod ?= null super attributes, options @_groups = {} diff --git a/src/scripts/models/name_finder.coffee b/src/scripts/models/name_finder.coffee index f745a6567..10f9a8fe6 100644 --- a/src/scripts/models/name_finder.coffee +++ b/src/scripts/models/name_finder.coffee @@ -67,7 +67,7 @@ module.exports = class NameFinder item = mod.findItem slug if not @includeGatherable - return unless item? and (not item.isGatherable) + return unless item? and item.isCraftable scanName = "#{mod.name} : #{name}" if nameHint? diff --git a/src/scripts/models/parser_versions/mod_version_parser_v1.coffee b/src/scripts/models/parser_versions/mod_version_parser_v1.coffee index f9cd8f076..d47b51f6f 100644 --- a/src/scripts/models/parser_versions/mod_version_parser_v1.coffee +++ b/src/scripts/models/parser_versions/mod_version_parser_v1.coffee @@ -61,8 +61,8 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase if not name.length > 0 then throw new Error 'the item name cannot be empty' @_itemData = name:name, line:@_lineNumber, group:@_rawData.group - @_rawData.items ?= [] - @_rawData.items.push @_itemData + @_rawData.items ?= {} + @_rawData.items[name] = @_itemData @_recipeData = null @@ -112,7 +112,7 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase _buildModVersion: (modVersionData, modVersion)-> modVersionData.items ?= [] - for itemData in modVersionData.items + for itemName, itemData of modVersionData.items @_handleErrors @_buildItem, modVersion, itemData return modVersion @@ -135,6 +135,12 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase if not recipeData.input? then throw new Error 'the "input" declaration is required' if not recipeData.pattern? then throw new Error 'the "pattern" declaration is required' + localizeSlug = (name, slug)=> + if @_rawData.items[name]? + return _.composeSlugs modVersion.modSlug, slug + else + return slug + recipeData.quantity ?= 1 recipeData.extras ?= [] recipeData.tools ?= [] @@ -143,6 +149,7 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase for name in recipeData.input slug = _.slugify name modVersion.registerSlug slug, name + slug = localizeSlug name, slug inputStacks.push new Stack slug:slug, quantity:0 for c in recipeData.pattern @@ -158,16 +165,18 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase name = modVersion.findName stack.slug throw new Error "#{name} is an input for this recipe, but it is not in the pattern" - outputStacks = [ new Stack slug:item.slug, quantity:recipeData.quantity ] + outputStacks = [ new Stack slug:item.qualifiedSlug, quantity:recipeData.quantity ] for extraData in recipeData.extras slug = _.slugify extraData.name modVersion.registerSlug slug, extraData.name + slug = localizeSlug extraData.name, slug outputStacks.push new Stack slug:slug, quantity:extraData.quantity toolStacks = [] for name in recipeData.tools slug = _.slugify name modVersion.registerSlug slug, name + slug = localizeSlug name, slug toolStacks.push new Stack slug:slug, quantity:1 attributes = diff --git a/src/scripts/models/recipe.coffee b/src/scripts/models/recipe.coffee index f7e617c3b..74b3a70f1 100644 --- a/src/scripts/models/recipe.coffee +++ b/src/scripts/models/recipe.coffee @@ -15,8 +15,7 @@ module.exports = class Recipe extends BaseModel constructor: (attributes={}, options={})-> if attributes.item? attributes.name = attributes.item.name - attributes.slug = attributes.item.slug - attributes.output ?= [new Stack slug:attributes.item.slug, quantity:1] + attributes.output ?= [new Stack slug:attributes.item.qualifiedSlug, quantity:1] if not attributes.name? then throw new Error 'attributes.name is required' if not attributes.input? then throw new Error 'attributes.input is required' @@ -25,11 +24,13 @@ module.exports = class Recipe extends BaseModel attributes.item ?= null attributes.output ?= [new Stack slug:_.slugify(attributes.name), quantity:1] attributes.pattern = @_parsePattern attributes.pattern - attributes.slug ?= attributes.output[0].slug attributes.tools ?= [] options.logEvents ?= false super attributes, options + Object.defineProperties this, + slug: {get:@getSlug} + # Public Methods ############################################################################### getItemSlugAt: (patternSlot)-> @@ -48,6 +49,12 @@ module.exports = class Recipe extends BaseModel return true if stack.slug is itemSlug return false + # Property Methods ############################################################################# + + getSlug: -> + return @item.qualifiedSlug if @item? + return @output[0].slug + # Object Overrides ############################################################################# toString: -> diff --git a/src/scripts/underscore_mixins.coffee b/src/scripts/underscore_mixins.coffee index 7974e66e1..707227cca 100644 --- a/src/scripts/underscore_mixins.coffee +++ b/src/scripts/underscore_mixins.coffee @@ -16,3 +16,15 @@ _.mixin result = result.replace /^_/, '' result = result.replace /_$/, '' return result + + composeSlugs: (part1, part2)-> + return "#{part1}__#{part2}" + + decomposeSlug: (slug)-> + return [null, null] unless slug? + + parts = slug.split '__' + if parts.length is 1 + parts = [ null, parts[0] ] + + return parts diff --git a/test/crafting_plan.test.coffee b/test/crafting_plan.test.coffee index 8bfb56297..5afe29cff 100644 --- a/test/crafting_plan.test.coffee +++ b/test/crafting_plan.test.coffee @@ -44,25 +44,29 @@ describe 'crafting_plan.coffee', -> plan.want.add 'oak_plank' plan.craft() plan.need.toList().should.eql ['oak_log'] - plan.result.toList().should.eql [[4, 'oak_plank']] + plan.result.toList().should.eql [[4, 'minecraft__oak_plank']] it 'can craft a multi-step recipe', -> plan.want.add 'crafting_table' plan.craft() plan.need.toList().should.eql ['oak_log'] - plan.result.toList().should.eql ['crafting_table'] + plan.result.toList().should.eql ['minecraft__crafting_table'] it 'can craft a multi-step recipe using tools', -> plan.want.add 'furnace' plan.craft() plan.need.toList().should.eql [[8, 'cobblestone']] - plan.result.toList().should.eql ['furnace'] + plan.result.toList().should.eql ['minecraft__furnace'] it 'can craft a multi-step recipe re-using tools', -> plan.want.add 'iron_sword' plan.craft() plan.need.toList().should.eql [[2, 'furnace_fuel'], [2, 'iron_ore'], 'oak_log'] - plan.result.toList().should.eql ['iron_sword', [2, 'oak_plank'], [3, 'stick']] + plan.result.toList().should.eql [ + 'minecraft__iron_sword', + [2, 'minecraft__oak_plank'], + [3, 'minecraft__stick'] + ] describe 'with building tools', -> @@ -71,7 +75,7 @@ describe 'crafting_plan.coffee', -> plan.want.add 'furnace' plan.craft() plan.need.toList().should.eql [[8, 'cobblestone'], 'oak_log'] - plan.result.toList().should.eql ['crafting_table', 'furnace'] + plan.result.toList().should.eql ['minecraft__crafting_table', 'minecraft__furnace'] it 'can craft a multi-step recipe re-using tools', -> plan.includingTools = true @@ -82,5 +86,9 @@ describe 'crafting_plan.coffee', -> [8, 'cobblestone'], [2, 'furnace_fuel'], [2, 'iron_ore'], [2, 'oak_log'] ] plan.result.toList().should.eql [ - 'crafting_table', 'furnace', 'iron_sword', [2, 'oak_plank'], [3, 'stick'] + 'minecraft__crafting_table', + 'minecraft__furnace', + 'minecraft__iron_sword', + [2, 'minecraft__oak_plank'], + [3, 'minecraft__stick'] ] diff --git a/test/inventory.test.coffee b/test/inventory.test.coffee index 1c1cd5e4d..de03a93f9 100644 --- a/test/inventory.test.coffee +++ b/test/inventory.test.coffee @@ -12,7 +12,7 @@ Item = require '../src/scripts/models/item' ######################################################################################################################## -inventory = null +inventory = modPack = null ######################################################################################################################## @@ -107,6 +107,39 @@ describe 'inventory.coffee', -> inventory.hasAtLeast('wool', 4).should.be.true inventory.hasAtLeast('wool', 5).should.be.false + describe 'localizeTo', -> + + before -> + modPack = + map: + wool: 'minecraft__wool' + string: 'minecraft__string' + boat: 'minecraft__boat' + stone_gear: 'buildcraft__stone_gear' + findItem: (slug)-> + [modSlug, itemSlug] = _.decomposeSlug slug + return slug:itemSlug, qualifiedSlug:@map[itemSlug] + + it 'replaces item slugs with qualified slugs', -> + inventory.add 'stone_gear' + inventory.localizeTo modPack + inventory.toList().should.eql [ + 'minecraft__boat', + [20, 'minecraft__string'], + [4, 'minecraft__wool'], + 'buildcraft__stone_gear' + ] + + it 'ignores qualified slugs', -> + inventory.add 'buildcraft__stone_gear' + inventory.localizeTo modPack + inventory.toList().should.eql [ + 'minecraft__boat', + [20, 'minecraft__string'], + [4, 'minecraft__wool'], + 'buildcraft__stone_gear' + ] + describe 'pop', -> it 'returns null for an empty inventory', -> diff --git a/test/inventory_parser.test.coffee b/test/inventory_parser.test.coffee index 6f81721d6..f78321175 100644 --- a/test/inventory_parser.test.coffee +++ b/test/inventory_parser.test.coffee @@ -24,7 +24,7 @@ describe 'inventory_parser.coffee', -> result.toList().should.eql [] it 'can parse a single item without quantity', -> - result = parser.parse 'Wool' + result = parser.parse 'wool' result.toList().should.eql ['wool'] it 'can parse a single item with quantity', -> @@ -32,11 +32,11 @@ describe 'inventory_parser.coffee', -> result.toList().should.eql [[4, 'wool']] it 'can parse multiple mixed-type items', -> - result = parser.parse '4.Wool:10.String:Boat' + result = parser.parse '4.wool:10.string:boat' result.toList().should.eql ['boat', [10, 'string'], [4, 'wool']] it 're-uses the given inventory object', -> inventory = new Inventory inventory.add 'string', 8 - result = parser.parse '4.Wool', inventory + result = parser.parse '4.wool', inventory result.toList().should.eql [[8, 'string'], [4, 'wool']] diff --git a/test/mod_pack.test.coffee b/test/mod_pack.test.coffee index 883ac49f8..aa469fb3a 100644 --- a/test/mod_pack.test.coffee +++ b/test/mod_pack.test.coffee @@ -19,22 +19,23 @@ buildcraft = industrialCraft = minecraft = modPack = null describe 'mod_pack.coffee', -> beforeEach -> - minecraft = new Mod slug:'minecraft' + minecraft = new Mod slug:'minecraft', name:'Minecraft' minecraft.addModVersion new ModVersion modSlug:minecraft.slug, version:'1.7.10' minecraft.activeModVersion.addItem new Item name:'Wool' minecraft.activeModVersion.addItem new Item name:'Bed', recipes:[''] minecraft.activeModVersion.registerSlug 'iron_chestplate', 'Iron Chestplate' - buildcraft = new Mod slug:'buildcraft' + buildcraft = new Mod slug:'buildcraft', name:'Buildcraft' buildcraft.addModVersion new ModVersion modSlug:buildcraft.slug, version:'6.2.6' buildcraft.activeModVersion.addItem new Item name:'Stone Gear', recipes:[''] - buildcraft.activeModVersion.addItem new Item name:'Bed', recipes:[''] + buildcraft.activeModVersion.addItem new Item name:'Wrench', recipes:[''] buildcraft.activeVersion = Mod.Version.None - industrialCraft = new Mod slug:'industrial_craft' + industrialCraft = new Mod slug:'industrial_craft', name:'Industrial Craft' industrialCraft.addModVersion new ModVersion modSlug:industrialCraft.slug, version:'2.0' industrialCraft.activeModVersion.addItem new Item name:'Resin' industrialCraft.activeModVersion.addItem new Item name:'Rubber' + industrialCraft.activeModVersion.addItem new Item name:'Wrench', recipes:[''] industrialCraft.activeVersion = Mod.Version.None modPack = new ModPack @@ -42,6 +43,30 @@ describe 'mod_pack.coffee', -> modPack.addMod buildcraft modPack.addMod industrialCraft + describe 'findItem', -> + + it 'can find an item by partial slug', -> + item = modPack.findItem 'wool' + item.qualifiedSlug.should.equal 'minecraft__wool' + + it 'can find an item by full slug', -> + item = modPack.findItem 'minecraft__wool' + item.name.should.equal 'Wool' + + it 'can find an ambiguous item by full slug', -> + buildcraft.activeVersion = Mod.Version.Latest + industrialCraft.activeVersion = Mod.Version.Latest + item = modPack.findItem 'industrial_craft__wrench' + item.name.should.equal 'Wrench' + item.modVersion.mod.name.should.equal 'Industrial Craft' + + it 'can find an ambiguous item by partial slug', -> + buildcraft.activeVersion = Mod.Version.Latest + industrialCraft.activeVersion = Mod.Version.Latest + item = modPack.findItem 'wrench' + item.name.should.equal 'Wrench' + item.modVersion.mod.name.should.equal 'Buildcraft' + describe 'findItemByName', -> it 'finds the requested item', -> @@ -57,7 +82,7 @@ describe 'mod_pack.coffee', -> it 'returns all data for a regular Minecraft item', -> display = modPack.findItemDisplay 'bed' display.iconUrl.should.equal '/data/minecraft/1.7.10/images/bed.png' - display.itemUrl.should.equal '/item/bed' + display.itemUrl.should.equal '/mod/minecraft/bed' display.itemName.should.equal 'Bed' display.modSlug.should.equal 'minecraft' @@ -65,13 +90,13 @@ describe 'mod_pack.coffee', -> buildcraft.activeVersion = '6.2.6' display = modPack.findItemDisplay 'stone_gear' display.iconUrl.should.equal '/data/buildcraft/6.2.6/images/stone_gear.png' - display.itemUrl.should.equal '/item/stone_gear' + display.itemUrl.should.equal '/mod/buildcraft/stone_gear' display.itemName.should.equal 'Stone Gear' display.modSlug.should.equal 'buildcraft' it 'assumes an unfound item is from Minecraft', -> display = modPack.findItemDisplay 'iron_chestplate' display.iconUrl.should.equal '/data/minecraft/1.7.10/images/iron_chestplate.png' - display.itemUrl.should.equal '/item/iron_chestplate' + display.itemUrl.should.equal '/mod/minecraft/iron_chestplate' display.itemName.should.equal 'Iron Chestplate' display.modSlug.should.equal 'minecraft' diff --git a/test/mod_version.test.coffee b/test/mod_version.test.coffee index 2b0ece99b..90bbcfcd8 100644 --- a/test/mod_version.test.coffee +++ b/test/mod_version.test.coffee @@ -91,5 +91,5 @@ describe 'mod_version.coffee', -> """ it 'finds all recipes which list item as output', -> - recipes = modVersion.findRecipes 'bucket' - (r.output[0].slug for r in recipes).sort().should.eql ['bucket', 'cake', 'cake'] + recipes = modVersion.findRecipes 'test__bucket' + (r.output[0].slug for r in recipes).sort().should.eql ['test__bucket', 'test__cake', 'test__cake'] diff --git a/test/parser_versions/mod_version_parser_v1.test.coffee b/test/parser_versions/mod_version_parser_v1.test.coffee index 6d045e7a8..c0317a529 100644 --- a/test/parser_versions/mod_version_parser_v1.test.coffee +++ b/test/parser_versions/mod_version_parser_v1.test.coffee @@ -68,7 +68,7 @@ describe 'mod_version_parser_v1.coffee', -> it 'adds "input" when present', -> modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo, Charlie; pattern: ... 012 ...' slugs = (s.slug for s in modVersion._items.charlie._recipes[0].input) - slugs.should.eql ['alpha', 'bravo', 'charlie'] + slugs.should.eql ['alpha', 'bravo', 'test__charlie'] it 'requires an "input" declaration', -> func = -> parser.parse baseText + 'recipe:; pattern: ... .0. ...' @@ -155,20 +155,20 @@ describe 'mod_version_parser_v1.coffee', -> describe 'output', -> beforeEach -> - baseText = 'item:Bravo; recipe:; input:Charlie; pattern:... .0. ...; ' + baseText = 'item: Delta; item:Bravo; recipe:; input:Charlie; pattern:... .0. ...; ' it 'adds a single item as the default output', -> modVersion = parser.parse baseText stack = modVersion._items.bravo._recipes[0].output[0] - stack.slug.should.equal 'bravo' + stack.slug.should.equal 'test__bravo' stack.quantity.should.equal 1 it 'can add multiple extras with quantities', -> modVersion = parser.parse baseText + 'extras:2 Delta, 4 Echo' output = modVersion._items.bravo._recipes[0].output - output[0].slug.should.equal 'bravo' + output[0].slug.should.equal 'test__bravo' output[0].quantity.should.equal 1 - output[1].slug.should.equal 'delta' + output[1].slug.should.equal 'test__delta' output[1].quantity.should.equal 2 output[2].slug.should.equal 'echo' output[2].quantity.should.equal 4 diff --git a/test/recipe.test.coffee b/test/recipe.test.coffee index c58751203..cd8b48177 100644 --- a/test/recipe.test.coffee +++ b/test/recipe.test.coffee @@ -5,6 +5,7 @@ Copyright (c) 2015 by Redwood Labs All rights reserved. ### +Item = require '../src/scripts/models/item' Recipe = require '../src/scripts/models/recipe' Stack = require '../src/scripts/models/stack' @@ -32,7 +33,8 @@ describe 'recipe.coffee', -> expect(-> new Recipe name:'Gold Gear', input:input).to.throw Error, 'attributes.pattern is required' it 'allows an item to provide required attributes', -> - recipe = new Recipe item:{name:'Gold Gear', slug:'gold_gear'}, input:input, pattern:pattern + item = new Item name:'Gold Gear' + recipe = new Recipe item:item, input:input, pattern:pattern recipe.name.should.equal 'Gold Gear' (o.slug for o in recipe.output).should.eql ['gold_gear']