From e88821d449e08440248e41fda501539bec927ac2 Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Wed, 18 Feb 2015 10:30:53 -0800 Subject: [PATCH] Convert codebase to using ItemSlug * Flesh out the formerly incomplete ItemSlug class * Convert all uses of text item slugs to use ItemSlug * Move the ItemSlug class under models where it properly belongs * Avoid the use of `slug` as a local variable or item attribute wherever possible to avoid confusion between various kinds of slugs * Merge the InventoryParser class into the Inventory class as it is unlikely to need to support multiple versions * Simplify the ModPack/Mod/ModVersion interfaces by replacing the use of various "helper" methods with equivalent use of more generic methods * Update the various methods of ModPack/Mod/ModVersion to be able to work with qualified or unqualified ItemSlugs * Remove the unused BaseCollection class * Add tests for ItemSlug and update all other tests to reflect all the other changes --- src/scripts/constants.coffee | 4 +- .../crafting_grid_controller.coffee | 6 +- .../crafting_page_controller.coffee | 2 - .../crafting_table_controller.coffee | 2 - .../controllers/full_recipe_controller.coffee | 6 +- .../controllers/inventory_controller.coffee | 2 +- .../inventory_table_controller.coffee | 11 +- .../minimal_recipe_controller.coffee | 4 +- .../controllers/stack_controller.coffee | 1 - src/scripts/crafting_guide_router.coffee | 13 +- src/scripts/models/base_collection.coffee | 10 -- src/scripts/models/crafting_page.coffee | 13 +- src/scripts/models/crafting_plan.coffee | 86 +++++----- src/scripts/models/inventory.coffee | 147 +++++++++++------- src/scripts/models/inventory_parser.coffee | 62 -------- src/scripts/models/item.coffee | 24 +-- src/scripts/models/item_page.coffee | 9 +- src/scripts/{ => models}/item_slug.coffee | 58 +++++-- src/scripts/models/mod.coffee | 10 +- src/scripts/models/mod_pack.coffee | 54 +++---- src/scripts/models/mod_version.coffee | 77 ++++----- .../mod_version_parser_v1.coffee | 60 ++++--- src/scripts/models/recipe.coffee | 19 ++- src/scripts/models/stack.coffee | 4 +- test/crafting_plan.test.coffee | 50 +++--- test/inventory.test.coffee | 113 +++++++++----- test/inventory_parser.test.coffee | 42 ----- test/item_slug.test.coffee | 136 ++++++++++++++++ test/mod_pack.test.coffee | 19 +-- test/mod_version.test.coffee | 17 +- .../mod_version_parser_v1.test.coffee | 49 +++--- test/recipe.test.coffee | 38 +++-- test/test.coffee | 11 +- 33 files changed, 624 insertions(+), 535 deletions(-) delete mode 100644 src/scripts/models/base_collection.coffee delete mode 100644 src/scripts/models/inventory_parser.coffee rename src/scripts/{ => models}/item_slug.coffee (53%) delete mode 100644 test/inventory_parser.test.coffee create mode 100644 test/item_slug.test.coffee diff --git a/src/scripts/constants.coffee b/src/scripts/constants.coffee index 4ed133b7e..85e021ec1 100644 --- a/src/scripts/constants.coffee +++ b/src/scripts/constants.coffee @@ -55,8 +55,8 @@ 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 "/mod/<%= modSlug %>/<%= slug %>" +Url.itemIcon = _.template "/data/<%= modSlug %>/<%= modVersion %>/images/<%= itemSlug %>.png" +Url.item = _.template "/mod/<%= modSlug %>/<%= itemSlug %>" 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/crafting_grid_controller.coffee b/src/scripts/controllers/crafting_grid_controller.coffee index a4f535da4..e021f4c4d 100644 --- a/src/scripts/controllers/crafting_grid_controller.coffee +++ b/src/scripts/controllers/crafting_grid_controller.coffee @@ -64,8 +64,8 @@ module.exports = class CraftingGridController extends BaseController if slot >= @_slotCount then throw new Error "slot (#{slot}) must be less than #{@_slotCount}" return null unless @model? - slug = @model.getItemSlugAt slot - return null unless slug? + itemSlug = @model.getItemSlugAt slot + return null unless itemSlug? - itemDisplay = @_modPack.findItemDisplay slug + itemDisplay = @_modPack.findItemDisplay itemSlug return itemDisplay diff --git a/src/scripts/controllers/crafting_page_controller.coffee b/src/scripts/controllers/crafting_page_controller.coffee index cf62aa1fd..5ec331506 100644 --- a/src/scripts/controllers/crafting_page_controller.coffee +++ b/src/scripts/controllers/crafting_page_controller.coffee @@ -10,7 +10,6 @@ CraftingTableController = require './crafting_table_controller' {Event} = require '../constants' ImageLoader = require './image_loader' InventoryController = require './inventory_controller' -InventoryParser = require '../models/inventory_parser' CraftingPage = require '../models/crafting_page' ModPackController = require './mod_pack_controller' NameFinder = require '../models/name_finder' @@ -32,7 +31,6 @@ module.exports = class CraftingPageController extends BaseController super options @_imageLoader = options.imageLoader - @_parser = new InventoryParser options.modPack @_storage = options.storage # Event Methods ################################################################################ diff --git a/src/scripts/controllers/crafting_table_controller.coffee b/src/scripts/controllers/crafting_table_controller.coffee index 844193ca0..9878b65e1 100644 --- a/src/scripts/controllers/crafting_table_controller.coffee +++ b/src/scripts/controllers/crafting_table_controller.coffee @@ -8,7 +8,6 @@ All rights reserved. BaseController = require './base_controller' {Duration} = require '../constants' ImageLoader = require './image_loader' -InventoryParser = require '../models/inventory_parser' MinimalRecipeController = require './minimal_recipe_controller' ######################################################################################################################## @@ -34,7 +33,6 @@ module.exports = class CraftingTableController extends BaseController @model.stepIndex -= 1 onReportProblem: -> - parser = new InventoryParser @_modPack itemList = parser.unparse @model.plan.want toolsMessage = if @model.plan.includingTools then '(including tools)' else '' message = "When I was on step #{@model.stepIndex + 1} of making: diff --git a/src/scripts/controllers/full_recipe_controller.coffee b/src/scripts/controllers/full_recipe_controller.coffee index 2fcf6d6d7..c87d8c236 100644 --- a/src/scripts/controllers/full_recipe_controller.coffee +++ b/src/scripts/controllers/full_recipe_controller.coffee @@ -68,7 +68,7 @@ module.exports = class FullRecipeController extends BaseController result = [] if @model? for stack in @model.tools - item = @modPack.findItem stack.slug + item = @modPack.findItem stack.itemSlug result.push item.name if item? return result @@ -78,7 +78,7 @@ module.exports = class FullRecipeController extends BaseController if @model? for stack in @model.input - inputs.add stack.slug, stack.quantity + inputs.add stack.itemSlug, stack.quantity _refreshOutputs: -> @@ -87,4 +87,4 @@ module.exports = class FullRecipeController extends BaseController if @model? for stack in @model.output - outputs.add stack.slug, stack.quantity + outputs.add stack.itemSlug, stack.quantity diff --git a/src/scripts/controllers/inventory_controller.coffee b/src/scripts/controllers/inventory_controller.coffee index df8ffe18a..1844b69a8 100644 --- a/src/scripts/controllers/inventory_controller.coffee +++ b/src/scripts/controllers/inventory_controller.coffee @@ -196,4 +196,4 @@ module.exports = class InventoryController extends BaseController controller.$el.fadeOut duration:Duration.fast, complete:-> @remove() _removeStack: (stack)-> - @model.remove stack.slug, stack.quantity + @model.remove stack.itemSlug, stack.quantity diff --git a/src/scripts/controllers/inventory_table_controller.coffee b/src/scripts/controllers/inventory_table_controller.coffee index 4e1d7de00..46cdbd1e7 100644 --- a/src/scripts/controllers/inventory_table_controller.coffee +++ b/src/scripts/controllers/inventory_table_controller.coffee @@ -7,8 +7,9 @@ All rights reserved. BaseController = require './base_controller' {Duration} = require '../constants' -{Key} = require '../constants' ImageLoader = require './image_loader' +ItemSlug = require '../models/item_slug' +{Key} = require '../constants' NameFinder = require '../models/name_finder' StackController = require './stack_controller' @@ -38,10 +39,10 @@ module.exports = class InventoryTableController extends BaseController # Event Methods ################################################################################ onAddButtonClicked: -> - name = @$nameField.val() - return unless @modPack.isValidName name + name = @modpack.findItemByName @$nameField.val() + return unless item? - @model.add _.slugify(name), parseInt(@$quantityField.val()) + @model.add item.slug, parseInt(@$quantityField.val()) @$nameField.val '' @$quantityField.val '1' @@ -160,7 +161,7 @@ module.exports = class InventoryTableController extends BaseController # Private Methods ############################################################################## _removeStack: (stack)-> - @model.remove stack.slug, stack.quantity + @model.remove stack.itemSlug, stack.quantity _updateNameAutocomplete: -> onChanged = => @onNameFieldChanged() diff --git a/src/scripts/controllers/minimal_recipe_controller.coffee b/src/scripts/controllers/minimal_recipe_controller.coffee index 3c9a6075f..4773a43f6 100644 --- a/src/scripts/controllers/minimal_recipe_controller.coffee +++ b/src/scripts/controllers/minimal_recipe_controller.coffee @@ -48,7 +48,7 @@ module.exports = class MinimalRecipeController extends BaseController if @model? outputStack = @model.output[0] if outputStack? - display = @_modPack.findItemDisplay outputStack.slug + display = @_modPack.findItemDisplay outputStack.itemSlug @$outputLink.attr 'href', display.itemUrl @$outputLink.attr 'title', display.itemName @$outputImg.attr 'alt', display.itemName @@ -72,6 +72,6 @@ module.exports = class MinimalRecipeController extends BaseController result = [] if @model? for stack in @model.tools - item = @_modPack.findItem stack.slug + item = @_modPack.findItem stack.itemSlug result.push item.name if item? return result diff --git a/src/scripts/controllers/stack_controller.coffee b/src/scripts/controllers/stack_controller.coffee index e03f08eed..2a15c9237 100644 --- a/src/scripts/controllers/stack_controller.coffee +++ b/src/scripts/controllers/stack_controller.coffee @@ -49,7 +49,6 @@ module.exports = class StackController extends BaseController @$nameLink.html display.itemName @$nameLink.attr 'href', display.itemUrl @$quantityField.html @model.quantity - # @$removeButton.css opacity:(if @editable then 1 else 0) @$action.css display:(if @editable then 'table-cell' else 'none') diff --git a/src/scripts/crafting_guide_router.coffee b/src/scripts/crafting_guide_router.coffee index 9229cf402..e4e500f0a 100644 --- a/src/scripts/crafting_guide_router.coffee +++ b/src/scripts/crafting_guide_router.coffee @@ -12,6 +12,7 @@ CraftingPageController = require './controllers/crafting_page_controller' {Event} = require './constants' HeaderController = require './controllers/header_controller' ItemPageController = require './controllers/item_page_controller' +ItemSlug = require './models/item_slug' Mod = require './models/mod' ModPack = require './models/mod_pack' ModPageController = require './controllers/mod_page_controller' @@ -42,8 +43,8 @@ module.exports = class CraftingGuideRouter extends Backbone.Router makeResponder = (m)-> return -> m.activeModVersion.fetch() if m.activeModVersion? - for slug in DefaultMods - mod = new Mod slug:slug + for modSlug in DefaultMods + mod = new Mod slug:modSlug mod.on Event.change + ':activeModVersion', makeResponder mod @storage.register "mod:#{mod.slug}", mod, 'activeVersion' mod.fetch() @@ -75,13 +76,13 @@ module.exports = class CraftingGuideRouter extends Backbone.Router @_setPage 'item', controller modItem: (modSlug, itemSlug)-> - slug = _.composeSlugs modSlug, itemSlug + slug = new ItemSlug modSlug, itemSlug controller = new ItemPageController _.extend {itemSlug:slug}, @_defaultOptions @_setPage 'item', controller - mod: (slug)-> + mod: (modSlug)-> controller = new ModPageController _.extend {}, @_defaultOptions - controller.model = @modPack.getMod slug + controller.model = @modPack.getMod modSlug @_setPage 'mod', controller root: -> @@ -89,7 +90,7 @@ module.exports = class CraftingGuideRouter extends Backbone.Router text = '' if params.recipeName? - if params.count? + if params.count? and params.count > 1 text = "#{params.count}.#{_.slugify(params.recipeName)}" else text = _.slugify params.recipeName diff --git a/src/scripts/models/base_collection.coffee b/src/scripts/models/base_collection.coffee deleted file mode 100644 index 393e1e490..000000000 --- a/src/scripts/models/base_collection.coffee +++ /dev/null @@ -1,10 +0,0 @@ -### -Crafting Guide - base_collection.coffee - -Copyright (c) 2015 by Redwood Labs -All rights reserved. -### - -######################################################################################################################## - -module.exports = class BaseCollection extends Backbone.Collection diff --git a/src/scripts/models/crafting_page.coffee b/src/scripts/models/crafting_page.coffee index 1d304a896..7065beb13 100644 --- a/src/scripts/models/crafting_page.coffee +++ b/src/scripts/models/crafting_page.coffee @@ -9,7 +9,7 @@ BaseModel = require './base_model' CraftingPlan = require './crafting_plan' CraftingTable = require './crafting_table' {Event} = require '../constants' -InventoryParser = require './inventory_parser' +Inventory = require './inventory' ModPack = require './mod_pack' ######################################################################################################################## @@ -23,8 +23,6 @@ module.exports = class CraftingPage extends BaseModel attributes.table ?= new CraftingTable plan:attributes.plan super attributes, options - @_parser = new InventoryParser - @modPack.on Event.change, => @_consumeParams() @on Event.change + ':params', => @_consumeParams() @@ -37,12 +35,13 @@ module.exports = class CraftingPage extends BaseModel if not @params.inventoryText? @params = null else - inventory = @_parser.parse @params.inventoryText + inventory = new Inventory + inventory.parse @params.inventoryText inventory.each (stack)=> - item = @modPack.findItem stack.slug, enableAsNeeded:true + item = @modPack.findItem stack.itemSlug, enableAsNeeded:true return unless item? and item.isCraftable - @plan.want.add stack.slug, stack.quantity - inventory.remove stack.slug + @plan.want.add stack.itemSlug, stack.quantity + inventory.remove stack.itemSlug if inventory.isEmpty then @params = null diff --git a/src/scripts/models/crafting_plan.coffee b/src/scripts/models/crafting_plan.coffee index eafd955b8..8442d718d 100644 --- a/src/scripts/models/crafting_plan.coffee +++ b/src/scripts/models/crafting_plan.coffee @@ -18,10 +18,10 @@ module.exports = class CraftingPlan extends BaseModel attributes.includingTools ?= false super attributes, options - @have = new Inventory - @want = new Inventory - @need = new Inventory - @result = new Inventory + @have = new Inventory modPack:@modPack + @want = new Inventory modPack:@modPack + @need = new Inventory modPack:@modPack + @result = new Inventory modPack:@modPack recraft = _.debounce (=> @craft()), 100 for inventory in [@have, @want] @@ -46,17 +46,17 @@ module.exports = class CraftingPlan extends BaseModel logger.info => "crafting #{@want}#{toolsMessage} starting with #{@have}" @clear() - @have.localizeTo @modPack - @want.localizeTo @modPack + @have.localize() + @want.localize() @result.addInventory @have @steps = {} @_reservedSteps = {} @want.each (stack)=> - @_findSteps stack.slug - item = @modPack.findItem stack.slug - @need.add item.qualifiedSlug, stack.quantity + @_findSteps stack.itemSlug + item = @modPack.findItem stack.itemSlug + @need.add item.slug, stack.quantity @_reservedSteps = null @steps = _.values @steps @@ -72,11 +72,11 @@ module.exports = class CraftingPlan extends BaseModel removeUncraftableItems: -> toRemove = [] @want.each (stack)=> - item = @modPack.findItem stack.slug - if not item? then toRemove.push stack.slug + item = @modPack.findItem stack.itemSlug + if not item? then toRemove.push stack.itemSlug - for slug in toRemove - @want.remove slug + for itemSlug in toRemove + @want.remove itemSlug # Event Methods ################################################################################ @@ -98,16 +98,16 @@ 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.itemSlug}" + @steps[recipe.itemSlug] = recipe:recipe _chooseRecipe: (item)-> - recipes = @modPack.findRecipes item.qualifiedSlug + recipes = @modPack.findRecipes item.slug return null unless recipes? return recipes[0] - _findSteps: (slug)-> - item = @modPack.findItem slug + _findSteps: (itemSlug)-> + item = @modPack.findItem itemSlug return unless item? return unless item.isCraftable return if item.isGatherable @@ -116,26 +116,26 @@ module.exports = class CraftingPlan extends BaseModel if @includingTools for toolStack in recipe.tools - if not @_hasStep toolStack.slug - @_findSteps toolStack.slug + if not @_hasStep toolStack.itemSlug + @_findSteps toolStack.itemSlug - return if @_hasStep item.qualifiedSlug - @_reservedSteps[item.qualifiedSlug] = recipe + return if @_hasStep item.slug + @_reservedSteps[item.slug] = recipe for inputStack in recipe.input - @_findSteps inputStack.slug + @_findSteps inputStack.itemSlug @_addStep recipe - _hasStep: (slug)-> - return true if @steps[slug]? - return true if @_reservedSteps[slug]? + _hasStep: (itemSlug)-> + return true if @steps[itemSlug]? + return true if @_reservedSteps[itemSlug]? return false - _qualifyItemSlug: (slug)-> - item = @modPack.findItem slug - return item.qualifiedSlug if item? - return slug + _qualifyItemSlug: (itemSlug)-> + item = @modPack.findItem itemSlug + return item.slug if item? + return itemSlug _removeExtraSteps: -> result = (step for step in @steps when step.multiplier > 0) @@ -146,31 +146,31 @@ module.exports = class CraftingPlan extends BaseModel step = @steps[i] recipe = step.recipe - step.multiplier = Math.ceil(@need.quantityOf(recipe.slug) / recipe.output[0].quantity) + step.multiplier = Math.ceil(@need.quantityOf(recipe.itemSlug) / recipe.output[0].quantity) if @includingTools for stack in recipe.tools - slug = @_qualifyItemSlug stack.slug - available = @result.quantityOf(slug) + @need.quantityOf(slug) + itemSlug = @_qualifyItemSlug stack.itemSlug + available = @result.quantityOf(itemSlug) + @need.quantityOf(itemSlug) needed = Math.max 0, stack.quantity - available - @need.add slug, needed - @result.add slug, needed + @need.add itemSlug, needed + @result.add itemSlug, needed for stack in recipe.input - slug = @_qualifyItemSlug stack.slug + itemSlug = @_qualifyItemSlug stack.itemSlug needed = step.multiplier * stack.quantity - consumed = Math.min needed, @result.quantityOf slug + consumed = Math.min needed, @result.quantityOf itemSlug remaining = needed - consumed - @result.remove slug, consumed - @need.add slug, remaining + @result.remove itemSlug, consumed + @need.add itemSlug, remaining for stack in recipe.output - slug = @_qualifyItemSlug stack.slug + itemSlug = @_qualifyItemSlug stack.itemSlug created = stack.quantity * step.multiplier - consumed = Math.min created, @need.quantityOf slug + consumed = Math.min created, @need.quantityOf itemSlug remaining = created - consumed - @result.add slug, remaining - @need.remove slug, consumed + @result.add itemSlug, remaining + @need.remove itemSlug, consumed diff --git a/src/scripts/models/inventory.coffee b/src/scripts/models/inventory.coffee index 9da1d2dc5..824109866 100644 --- a/src/scripts/models/inventory.coffee +++ b/src/scripts/models/inventory.coffee @@ -7,6 +7,7 @@ All rights reserved. BaseModel = require './base_model' {Event} = require '../constants' +ItemSlug = require './item_slug' {RequiredMods} = require '../constants' Stack = require './stack' @@ -16,28 +17,35 @@ module.exports = class Inventory extends BaseModel constructor: (attributes={}, options={})-> super attributes, options + attributes.modPack ?= null @clear() Object.defineProperties this, - isEmpty: { get:-> @_slugs.length is 0 } + isEmpty: { get:-> @_itemSlugs.length is 0 } + + # Class Methods ################################################################################ + + @Delimiters = + Item: '.' + Stack: ':' # Public Methods ############################################################################### - add: (slug, quantity=1)-> - @_add slug, quantity - @trigger Event.add, this, slug, quantity + add: (itemSlug, quantity=1)-> + @_add itemSlug, quantity + @trigger Event.add, this, itemSlug, quantity @trigger Event.change, this return this addInventory: (inventory)-> - inventory.each (stack)=> @_add stack.slug, stack.quantity + inventory.each (stack)=> @_add stack.itemSlug, stack.quantity @trigger Event.change, this return this clear: (options={})-> @_stacks = {} - @_slugs = [] + @_itemSlugs = [] @trigger Event.change, this @@ -47,78 +55,110 @@ module.exports = class Inventory extends BaseModel return inventory each: (callback)-> - for slug in @_slugs - callback @_stacks[slug] + for itemSlug in @_itemSlugs + callback @_stacks[itemSlug] getSlugs: -> - return @_slugs[..] + return @_itemSlugs[..] - hasAtLeast: (slug, quantity=1)-> + hasAtLeast: (itemSlug, quantity=1)-> if quantity is 0 then return true - stack = @_stacks[slug] + stack = @_stacks[itemSlug] return false unless stack? return stack.quantity >= quantity - localizeTo: (modPack)-> + localize: -> + if not @modPack? then throw new Error 'localize requires @modPack' + newSlugs = [] - for slug in @_slugs - stack = @_stacks[slug] - qualifiedSlug = modPack.findItem(slug)?.qualifiedSlug + for itemSlug in @_itemSlugs + stack = @_stacks[itemSlug] + + qualifiedSlug = @modPack.findItem(itemSlug)?.slug if qualifiedSlug? - delete @_stacks[slug] + delete @_stacks[itemSlug] newSlugs.push qualifiedSlug @_stacks[qualifiedSlug] = stack - stack.slug = qualifiedSlug + stack.itemSlug = qualifiedSlug else - throw new Error "could not find an item for: #{slug}" + newSlugs.push itemSlug - @_slugs = newSlugs + @_itemSlugs = newSlugs @_sort() pop: -> - slug = @_slugs.pop() - return null unless slug? + itemSlug = @_itemSlugs.pop() + return null unless itemSlug? - stack = @_stacks[slug] - delete @_stacks[slug] + stack = @_stacks[itemSlug] + delete @_stacks[itemSlug] - @trigger Event.remove, this, stack.slug, stack.quantity + @trigger Event.remove, this, stack.itemSlug, stack.quantity @trigger Event.change, this return stack - quantityOf: (slug)-> - stack = @_stacks[slug] + quantityOf: (itemSlug)-> + stack = @_stacks[itemSlug] return 0 unless stack? return stack.quantity - remove: (slug, quantity=null)-> + remove: (itemSlug, quantity=null)-> return if quantity is 0 - stack = @_stacks[slug] - if not stack? then throw new Error "cannot remove #{slug} since it is not in this inventory" + stack = @_stacks[itemSlug] + if not stack? then throw new Error "cannot remove #{itemSlug} since it is not in this inventory" quantity ?= stack.quantity if stack.quantity < quantity - throw new Error "cannot remove #{quantity}: only #{stack.quantity} #{slug} in this inventory" + throw new Error "cannot remove #{quantity}: only #{stack.quantity} #{itemSlug} in this inventory" stack.quantity -= quantity if stack.quantity is 0 - delete @_stacks[slug] - @_slugs = _(@_slugs).without slug + delete @_stacks[itemSlug] + @_itemSlugs = _(@_itemSlugs).without itemSlug - @trigger Event.remove, this, slug, quantity + @trigger Event.remove, this, itemSlug, quantity @trigger Event.change, this return this - toList: -> - result = [] - @each (stack)-> - if stack.quantity > 1 - result.push [stack.quantity, stack.slug] + # Parsing Methods ############################################################################## + + parse: (data)-> + return this if not data? or data.length is 0 + + stacks = data.split Inventory.Delimiters.Stack + for stackText in stacks + stackParts = stackText.split Inventory.Delimiters.Item + if stackParts.length is 2 + quantity = parseInt stackParts[0], 10 + itemSlug = ItemSlug.slugify stackParts[1] + else if stackParts.length is 1 + quantity = 1 + itemSlug = ItemSlug.slugify stackParts[0] else - result.push stack.slug - return result + throw new Error "expected #{stackText} to have 0 or 1 parts" + + if itemSlug.qualified.length > 0 + @add itemSlug, quantity + + return this + + unparse: -> + parts = [] + @each (stack)=> + slugText = stack.itemSlug.qualified + if @modPack? + item = @modPack.findItem stack.itemSlug.item + if item? + slugText = if item.slug isnt stack.itemSlug then item.slug.qualified + + if stack.quantity is 1 + parts.push slugText + else + parts.push "#{stack.quantity}#{Inventory.Delimiters.Item}#{slugText}" + + return parts.join Inventory.Delimiters.Stack # Object Overrides ############################################################################# @@ -137,31 +177,18 @@ module.exports = class Inventory extends BaseModel # Private Methods ############################################################################## - _add: (slug, quantity=1)-> - return unless slug? + _add: (itemSlug, quantity=1)-> + return unless itemSlug? return if quantity is 0 - stack = @_stacks[slug] + stack = @_stacks[itemSlug] if not stack? - stack = new Stack slug:slug, quantity:quantity - @_stacks[slug] = stack - @_slugs.push slug + stack = new Stack itemSlug:itemSlug, quantity:quantity + @_stacks[itemSlug] = stack + @_itemSlugs.push itemSlug @_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 + @_itemSlugs.sort (a, b)-> ItemSlug.compare a, b diff --git a/src/scripts/models/inventory_parser.coffee b/src/scripts/models/inventory_parser.coffee deleted file mode 100644 index 2df7c7453..000000000 --- a/src/scripts/models/inventory_parser.coffee +++ /dev/null @@ -1,62 +0,0 @@ -### -Crafting Guide - inventory_parser.coffee - -Copyright (c) 2014-2015 by Redwood Labs -All rights reserved. -### - -Inventory = require './inventory' -Item = require './item' -StringBuilder = require './string_builder' - -######################################################################################################################## - -module.exports = class InventoryParser - - constructor: (modPack=null)-> - @modPack = modPack - - # Class Methods ################################################################################ - - @STACK_DELIMITER = ':' - - @ITEM_DELIMITER = '.' - - # Public Methods ############################################################################### - - parse: (data, inventory=null)-> - inventory ?= new Inventory - return inventory if not data? or data.length is 0 - - stacks = data.split InventoryParser.STACK_DELIMITER - for stackText in stacks - stackParts = stackText.split InventoryParser.ITEM_DELIMITER - if stackParts.length is 2 - quantity = parseInt stackParts[0] - slug = stackParts[1] - else if stackParts.length is 1 - quantity = 1 - slug = stackParts[0] - else - throw new Error "expected #{stackText} to have 0 or 1 parts" - - if slug.length > 0 - inventory.add slug, quantity - - return inventory - - unparse: (inventory)-> - if not @modPack? then throw new Error 'this.modPack is needed to unparse' - - parts = [] - inventory.each (stack)=> - [modSlug, itemSlug] = _.decomposeSlug stack.slug - item = @modPack.findItem itemSlug - slug = if item.qualifiedSlug is stack.slug then item.slug else item.qualifiedSlug - - if stack.quantity is 1 - parts.push slug - else - parts.push "#{stack.quantity}#{InventoryParser.ITEM_DELIMITER}#{slug}" - - return parts.join InventoryParser.STACK_DELIMITER diff --git a/src/scripts/models/item.coffee b/src/scripts/models/item.coffee index 55db52aec..26afd5f0f 100644 --- a/src/scripts/models/item.coffee +++ b/src/scripts/models/item.coffee @@ -5,9 +5,9 @@ Copyright (c) 2014-2015 by Redwood Labs All rights reserved. ### -BaseCollection = require './base_collection' BaseModel = require './base_model' {Event} = require '../constants' +ItemSlug = require './item_slug' Recipe = require './recipe' StringBuilder = require './string_builder' @@ -23,16 +23,12 @@ module.exports = class Item extends BaseModel attributes.group ?= Item.Group.Other attributes.isGatherable ?= false attributes.modVersion ?= null - attributes.slug ?= _.slugify attributes.name + attributes.slug ?= ItemSlug.slugify attributes.name options.logEvents ?= false super attributes, options - Object.defineProperties this, - isCraftable: {get:@getIsCraftable} - qualifiedSlug: {get:@getQualifiedSlug} - - @on Event.change + ':modVersion', => @_qualifiedSlug = null + @on Event.change + ':modVersion', => @slug.mod = @modVersion?.modSlug # Public Methods ############################################################################### @@ -47,15 +43,10 @@ module.exports = class Item extends BaseModel getIsCraftable: -> return false unless @modVersion? - return @modVersion.hasRecipes @qualifiedSlug + return @modVersion.hasRecipes @slug - getQualifiedSlug: -> - return @slug if not @modVersion? - - if not @_qualifiedSlug? - @_qualifiedSlug = _.composeSlugs @modVersion.modSlug, @slug - - return @_qualifiedSlug + Object.defineProperties @prototype, + isCraftable: {get:@prototype.getIsCraftable} # Object Overrides ############################################################################# @@ -68,7 +59,6 @@ module.exports = class Item extends BaseModel .push 'isGatherable:', @isGatherable, ', ' .onlyIf (@group isnt Item.Group.Other), (b)=> b.push 'group:"', @group, '", ' - .onlyIf (_.slugify(@name) isnt @slug), (b)=> - b.push 'slug:"', @slug, '", ' + .push 'slug:"', @slug, '", ' .push '}' .toString() diff --git a/src/scripts/models/item_page.coffee b/src/scripts/models/item_page.coffee index 3b8266a13..dc52848ab 100644 --- a/src/scripts/models/item_page.coffee +++ b/src/scripts/models/item_page.coffee @@ -31,14 +31,11 @@ module.exports = class ItemPage extends BaseModel findComponentInItems: -> return null unless @item? - slugs = [@item.slug, @item.qualifiedSlug] result = {} @modPack.eachMod (mod)-> - mod.eachItem (item)-> - item.eachRecipe (recipe)-> - for stack in recipe.input - if stack.slug in slugs - result[item.qualifiedSlug] = item + mod.eachRecipe (recipe)-> + if recipe.produces @item.slug + result[item.slug] = item result = _.values(result).sort (a, b)-> a.compareTo b return null unless result.length > 0 diff --git a/src/scripts/item_slug.coffee b/src/scripts/models/item_slug.coffee similarity index 53% rename from src/scripts/item_slug.coffee rename to src/scripts/models/item_slug.coffee index 31bf2ba5f..838718c0b 100644 --- a/src/scripts/item_slug.coffee +++ b/src/scripts/models/item_slug.coffee @@ -5,12 +5,15 @@ Copyright (c) 2015 by Redwood Labs All rights reserved. ### +{RequiredMods} = require '../constants' + ######################################################################################################################## module.exports = class ItemSlug constructor: -> @_item = @_mod = null + if arguments.length is 1 @item = arguments[0] else if arguments.length is 2 @@ -21,10 +24,14 @@ module.exports = class ItemSlug # Class Methods ################################################################################ - DELIMITER = '__' - @compare: (a, b)-> - if a.isQualified isnt b.isQualified + aIsRequired = a.mod in RequiredMods + bIsRequired = b.mod in RequiredMods + + if aIsRequired isnt bIsRequired + return -1 if aIsRequired + return +1 if bIsRequired + else if a.isQualified isnt b.isQualified return -1 if a.isQualified return +1 if b.isQualified else if a.mod isnt b.mod @@ -34,18 +41,36 @@ module.exports = class ItemSlug return 0 - @equals: (a, b)-> + @equal: (a, b)-> return false unless a.mod is b.mod return false unless a.item is b.item return true + @slugify: (arg)-> + return arg if arg?.constructor?.name is 'ItemSlug' + + [modSlug, itemSlug] = _.decomposeSlug arg + itemSlug = _.slugify itemSlug + + if modSlug? + return new ItemSlug modSlug, itemSlug + else + return new ItemSlug itemSlug + + # Public Methods ############################################################################### + + matches: (slug, options={exact:false})-> + return false unless slug?.constructor?.name is 'ItemSlug' + + if slug.isQualified and this.isQualified + return slug.qualified is this.qualified + else + return slug.item is this.item + # Property Methods ############################################################################# - Object.defineProperties @prototype, - isQualified: { get:@prototype.isQualified } - mod: { get:@prototype.getMod, set:@prototype.setMod } - item: { get:@prototype.getItem, set:@prototype.setItem } - qualified: { get:@prototype.getQualified set:@prototype.setQualified } + getIsQualified: -> + return @_mod? getItem: -> return @_item @@ -53,17 +78,24 @@ module.exports = class ItemSlug setItem: (item)-> if not item? then throw new Error 'item is required' @_item = item - @mod = mod + + @mod = @mod # reset @_qualified getMod: -> return @_mod setMod: (mod)-> - @_qualified = if mod? then "#{@mod}#{ItemSlug.DELIMITER}#{@item}" else @item @_mod = mod + @_qualified = if @_mod? then _.composeSlugs(@_mod, @_item) else @_item - isQualified: -> - return @_mod? + getQualified: -> + return @_qualified + + Object.defineProperties @prototype, + isQualified: { get:@prototype.getIsQualified } + mod: { get:@prototype.getMod, set:@prototype.setMod } + item: { get:@prototype.getItem, set:@prototype.setItem } + qualified: { get:@prototype.getQualified } # Object Overrides ############################################################################# diff --git a/src/scripts/models/mod.coffee b/src/scripts/models/mod.coffee index 45a07a74b..f9466e11a 100644 --- a/src/scripts/models/mod.coffee +++ b/src/scripts/models/mod.coffee @@ -87,13 +87,13 @@ module.exports = class Mod extends BaseModel return unless @_activeModVersion? @_activeModVersion.findItemByName name - findName: (slug)-> + findName: (itemSlug)-> return unless @_activeModVersion? - @_activeModVersion.findName slug + @_activeModVersion.findName itemSlug - findRecipes: (slug, result=[])-> + findRecipes: (itemSlug, result=[])-> return result unless @_activeModVersion? - @_activeModVersion.findRecipes slug, result + @_activeModVersion.findRecipes itemSlug, result # Property Methods ############################################################################# @@ -123,6 +123,7 @@ module.exports = class Mod extends BaseModel for modVersion in @_modVersions return modVersion if modVersion.version is version + return null getActiveVersion: -> @@ -150,7 +151,6 @@ module.exports = class Mod extends BaseModel @trigger Event.change + ':activeVersion', this, @_activeVersion @trigger Event.change, this - # Backbone.View Overrides ###################################################################### parse: (text)-> diff --git a/src/scripts/models/mod_pack.coffee b/src/scripts/models/mod_pack.coffee index f78fa0d42..e23618a7d 100644 --- a/src/scripts/models/mod_pack.coffee +++ b/src/scripts/models/mod_pack.coffee @@ -22,12 +22,11 @@ module.exports = class ModPack extends BaseModel # Public Methods ############################################################################### - findItem: (slug, options={})-> + findItem: (itemSlug, options={})-> options.includeDisabled ?= false - [modSlug, itemSlug] = _.decomposeSlug slug - if modSlug? - mod = @getMod modSlug + if itemSlug.isQualified + mod = @getMod itemSlug.mod if mod? item = mod.findItem itemSlug, options return item if item? @@ -50,63 +49,50 @@ module.exports = class ModPack extends BaseModel return null - findItemDisplay: (slug)-> - if not slug? then return null + findItemDisplay: (itemSlug)-> + if not itemSlug? then return null result = {} - item = @findItem slug, includeDisabled:true + item = @findItem itemSlug, includeDisabled:true if item? - result.modSlug = item.modVersion.modSlug + result.modSlug = item.slug.mod result.modVersion = item.modVersion.version - result.slug = item.slug + result.itemSlug = item.slug.item result.itemName = item.name else result.modSlug = @_mods[0].slug result.modVersion = @_mods[0].activeVersion - result.slug = slug - result.itemName = @findName slug, includeDisabled:true + result.itemSlug = itemSlug.item + result.itemName = @findName itemSlug, includeDisabled:true - result.craftingUrl = Url.crafting inventoryText:slug + result.craftingUrl = Url.crafting inventoryText:itemSlug.item result.iconUrl = Url.itemIcon result result.itemUrl = Url.item result return result - findName: (slug)-> + findName: (slug, options={})-> + options.includeDisabled ?= false + for mod in @_mods - continue unless mod.enabled + continue unless mod.enabled or options.includeDisabled name = mod.findName slug return name if name return null - findRecipes: (slug, result=[])-> - [modSlug, itemSlug] = _.decomposeSlug slug - - if modSlug? - mod = @getMod modSlug + findRecipes: (itemSlug, result=[])-> + if itemSlug.isQualified + mod = @getMod itemSlug.mod if mod? - mod.findRecipes slug, result + mod.findRecipes itemSlug, result return result if result.length > 0 for mod in @_mods continue unless mod.enabled - mod.findRecipes slug, result + mod.findRecipes itemSlug, result return if result.length > 0 then result else null - isGatherable: (slug)-> - item = @findItem slug - return true if not item? - return true if item.isGatherable - return false if item.isCraftable - return true - - isValidName: (name)-> - slug = _.slugify name - existingName = @findName slug - - return name is existingName - # Property Methods ############################################################################# addMod: (mod)-> diff --git a/src/scripts/models/mod_version.coffee b/src/scripts/models/mod_version.coffee index 1d1bab95a..dde95adbb 100644 --- a/src/scripts/models/mod_version.coffee +++ b/src/scripts/models/mod_version.coffee @@ -5,10 +5,10 @@ Copyright (c) 2014-2015 by Redwood Labs All rights reserved. ### -BaseCollection = require './base_collection' BaseModel = require './base_model' {Event} = require '../constants' Item = require './item' +ItemSlug = require './item_slug' {RequiredMods} = require '../constants' {Url} = require '../constants' @@ -39,16 +39,19 @@ module.exports = class ModVersion extends BaseModel return 0 + sort: -> + @_slugs.sort (a, b)-> ItemSlug.compare a, b + # Item Methods ################################################################################# addItem: (item)-> - if @_items[item.slug]? then throw new Error "duplicate item for #{item.name}" - - @_items[item.slug] = item - @_groups[item.group] ?= {} - @_groups[item.group][item.slug] = item + if @findItem(item.slug)? then throw new Error "duplicate item for #{item.name}" item.modVersion = this + @_items[item.slug.item] = item + @_groups[item.group] ?= {} + @_groups[item.group][item.slug.item] = item + @registerName item.slug, item.name return this @@ -60,20 +63,21 @@ module.exports = class ModVersion extends BaseModel eachItem: (callback)-> for slug in @_slugs - item = @_items[slug] + item = @findItem slug continue unless item? - callback @_items[slug], slug + callback item return this eachItemInGroup: (group, callback)-> - group = @_groups[group] - return unless group? + itemMap = @_groups[group] + return unless itemMap? - for slug in _.keys(group).sort() - callback group[slug] + items = _.values(itemMap).sort (a, b)-> a.compareTo b + for item in items + callback item findItem: (itemSlug)-> - return @_items[itemSlug] + return @_items[itemSlug.item] findItemByName: (name)-> for itemSlug, item of @_items @@ -97,22 +101,16 @@ module.exports = class ModVersion extends BaseModel eachName: (callback)-> for slug in @_slugs - callback @_names[slug], slug + callback @_names[slug.item] return this - findName: (slug)-> - [modSlug, itemSlug] = _.decomposeSlug slug - return @_names[itemSlug] - - registerName: (slug, name)-> - hasSlug = @_names[slug]? - @_names[slug] = name - - if not hasSlug - @_slugs.push slug - @_slugs.sort() - @_slugs = _.uniq @_slugs, true + findName: (itemSlug)-> + return @_names[itemSlug.item] + registerName: (itemSlug, name)-> + return if @_names[itemSlug.item] + @_names[itemSlug.item] = name + @_slugs.push itemSlug return this # Recipe Methods ############################################################################### @@ -121,15 +119,15 @@ module.exports = class ModVersion extends BaseModel recipe.modVersion = this for stack in recipe.output - recipeList = @_recipes[stack.slug] + recipeList = @_recipes[stack.itemSlug.item] if not recipeList? - @_recipes[stack.slug] = recipeList = [] + recipeList = @_recipes[stack.itemSlug.item] = [] recipeList.push recipe return this - findRecipes: (slug, result=[])-> - recipeList = @_recipes[slug] + findRecipes: (itemSlug, result=[])-> + recipeList = @_recipes[itemSlug.item] if recipeList? for recipe in recipeList result.push recipe @@ -138,16 +136,19 @@ module.exports = class ModVersion extends BaseModel findExternalRecipes: -> result = {} - for slug, recipeList of @_recipes - [modSlug, itemSlug] = _.decomposeSlug slug - logger.debug "checking: #{modSlug}, #{itemSlug}" - continue if @_items[itemSlug]? - logger.debug "external: #{recipeList}" - result[itemSlug] = recipeList[..] + for itemSlug in @_slugs + continue if itemSlug.isQualified + recipes = @_recipes[itemSlug.item] + continue unless recipes? and recipes.length > 0 + + resultList = result[itemSlug] = [] + for recipe in recipes + resultList.push recipe + return result hasRecipes: (itemSlug)-> - recipeList = @_recipes[itemSlug] + recipeList = @_recipes[itemSlug.item] return true if recipeList? and recipeList.length > 0 return false @@ -167,5 +168,5 @@ module.exports = class ModVersion extends BaseModel toString: -> return "ModVersion (#{@cid}) { - modSlug:#{@modSlug}, version:#{@version}, items:#{_.keys(@_items).length} items + modSlug:#{@modSlug}, version:#{@version}, items:«#{@_slugs.length} items» }" 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 f6c4ca12f..5c079038f 100644 --- a/src/scripts/models/parser_versions/mod_version_parser_v1.coffee +++ b/src/scripts/models/parser_versions/mod_version_parser_v1.coffee @@ -7,6 +7,7 @@ All rights reserved. CommandParserVersionBase = require './command_parser_version_base' Item = require '../item' +ItemSlug = require '../item_slug' ModVersion = require '../mod_version' Recipe = require '../recipe' Stack = require '../stack' @@ -124,6 +125,7 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase for itemName, itemData of modVersionData.items @_handleErrors @_buildItem, modVersion, itemData + modVersion.sort() return modVersion _buildItem: (modVersion, itemData)-> @@ -134,6 +136,10 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase if itemData.type is 'new' item = new Item name:itemData.name, isGatherable:itemData.gatherable, group:itemData.group modVersion.addItem item + itemData.slug = item.slug + else + itemData.slug = ItemSlug.slugify itemData.name + modVersion.registerName itemData.slug, itemData.name for recipeData in itemData.recipes @_handleErrors @_buildRecipe, modVersion, itemData, recipeData @@ -145,14 +151,14 @@ 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' - itemSlug = _.slugify itemData.name - modVersion.registerName itemSlug, itemData.name - if itemData.type is 'new' then itemSlug = _.composeSlugs modVersion.modSlug, itemSlug - - qualifySlug = (name, slug)=> - return slug unless itemData.type is 'new' - return slug unless @_rawData.items[name]? - return _.composeSlugs modVersion.modSlug, slug + createSlug = (name)=> + item = @_rawData.items[name] + if item? + slug = new ItemSlug modVersion.modSlug, _.slugify name + else + slug = new ItemSlug _.slugify name + modVersion.registerName slug, name + return slug recipeData.quantity ?= 1 recipeData.extras ?= [] @@ -160,10 +166,8 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase inputStacks = [] for name in recipeData.input - slug = _.slugify name - modVersion.registerName slug, name - slug = qualifySlug name, slug - inputStacks.push new Stack slug:slug, quantity:0 + inputSlug = createSlug name + inputStacks.push new Stack itemSlug:inputSlug, quantity:0 for c in recipeData.pattern continue if c is '.' @@ -175,23 +179,18 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase for i in [0...inputStacks.length] stack = inputStacks[i] if stack.quantity is 0 - name = modVersion.findName stack.slug + name = modVersion.findName stack.itemSlug throw new Error "#{name} is an input for this recipe, but it is not in the pattern" - - outputStacks = [ new Stack slug:itemSlug, quantity:recipeData.quantity ] + outputStacks = [ new Stack itemSlug:itemData.slug, quantity:recipeData.quantity ] for extraData in recipeData.extras - slug = _.slugify extraData.name - modVersion.registerName slug, extraData.name - slug = qualifySlug extraData.name, slug - outputStacks.push new Stack slug:slug, quantity:extraData.quantity + outputSlug = createSlug extraData.name + outputStacks.push new Stack itemSlug:outputSlug, quantity:extraData.quantity toolStacks = [] for name in recipeData.tools - slug = _.slugify name - modVersion.registerName slug, name - slug = qualifySlug name, slug - toolStacks.push new Stack slug:slug, quantity:1 + toolSlug = createSlug name + toolStacks.push new Stack itemSlug:toolSlug, quantity:1 recipe = new Recipe input: inputStacks @@ -200,7 +199,6 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase tools: toolStacks modVersion.addRecipe recipe - logger.debug "adding recipe: #{recipe}" return recipe # Un-parsing Methods ########################################################################### @@ -213,9 +211,9 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase modVersion.eachGroup (group)=> @_unparseGroup builder, modVersion, group - for itemSlug, recipeList of modVersion.findExternalRecipes() + for itemSlugText, recipeList of modVersion.findExternalRecipes() builder - .line 'update: ', modVersion.findName itemSlug + .line 'update: ', modVersion.findName ItemSlug.slugify(itemSlugText) .indent() .loop(recipeList, delimiter:'\n\n', onEach:(b, r)=> @_unparseRecipe(b, r)) .outdent() @@ -235,7 +233,7 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase builder.outdent() _unparseItem: (builder, modVersion, item)-> - recipes = modVersion.findRecipes item.qualifiedSlug + recipes = modVersion.findRecipes item.slug builder .line 'item: ', item.name @@ -246,13 +244,13 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase .outdent() _unparseRecipe: (builder, recipe)-> - inputNames = (builder.context.findName(stack.slug) for stack in recipe.input) + inputNames = (builder.context.findName(stack.itemSlug) for stack in recipe.input) inputNames.sort() patternMap = {'.', '.'} for i in [0...recipe.input.length] stack = recipe.input[i] - patternMap["#{i}"] = "#{inputNames.indexOf builder.context.findName(stack.slug)}" + patternMap["#{i}"] = "#{inputNames.indexOf builder.context.findName(stack.itemSlug)}" pattern = recipe.pattern or recipe.defaultPattern newPattern = [] @@ -288,9 +286,9 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase _unparseStackList: (builder, stackList)-> if stackList.length is 1 and stackList[0].quantity is 1 - builder.push builder.context.findName(stackList[0].slug) + builder.push builder.context.findName(stackList[0].itemSlug) else builder.loop stackList, onEach:(b, stack)=> builder .onlyIf stack.quantity > 1, => builder.push stack.quantity, ' ' - .push builder.context.findName stack.slug + .push builder.context.findName stack.itemSlug diff --git a/src/scripts/models/recipe.coffee b/src/scripts/models/recipe.coffee index 7f93bdca1..3d294c49a 100644 --- a/src/scripts/models/recipe.coffee +++ b/src/scripts/models/recipe.coffee @@ -16,13 +16,13 @@ module.exports = class Recipe extends BaseModel if not attributes.input? then throw new Error 'attributes.input is required' if not attributes.pattern? then throw new Error 'attributes.pattern is required' - if attributes.slug? and not attributes.output? - attributes.output = [new Stack slug:attributes.slug, quantity:1] - else if attributes.output? and not attributes.slug? + if attributes.itemSlug? and not attributes.output? + attributes.output = [new Stack itemSlug:attributes.itemSlug, quantity:1] + else if attributes.output? and not attributes.itemSlug? if attributes.output.length is 0 then throw new Error 'attributes.output cannot be empty' - attributes.slug = attributes.output[0].slug + attributes.itemSlug = attributes.output[0].itemSlug else - throw new Error 'attributes.slug or attributes.output is required' + throw new Error 'attributes.itemSlug or attributes.output is required' attributes.pattern = @_parsePattern attributes.pattern @@ -42,7 +42,14 @@ module.exports = class Recipe extends BaseModel stack = @input[parseInt(patternDigit)] return null unless stack? - return stack.slug + return stack.itemSlug + + produces: (itemSlug)-> + for stack in @output + if stack.itemSlug.matches itemSlug + return true + + return false # Object Overrides ############################################################################# diff --git a/src/scripts/models/stack.coffee b/src/scripts/models/stack.coffee index 3ca29e519..161988839 100644 --- a/src/scripts/models/stack.coffee +++ b/src/scripts/models/stack.coffee @@ -12,7 +12,7 @@ BaseModel = require './base_model' module.exports = class Stack extends BaseModel constructor: (attributes={}, options={})-> - if not attributes.slug? then throw new Error 'attributes.slug is required' + if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required' attributes.quantity ?= 1 options.logEvents ?= false super attributes, options @@ -20,4 +20,4 @@ module.exports = class Stack extends BaseModel # Object Overrides ############################################################################# toString: -> - return "#{@quantity} #{@slug}" + return "#{@quantity} #{@itemSlug}" diff --git a/test/crafting_plan.test.coffee b/test/crafting_plan.test.coffee index 5afe29cff..d71bdf502 100644 --- a/test/crafting_plan.test.coffee +++ b/test/crafting_plan.test.coffee @@ -6,6 +6,7 @@ All rights reserved. ### CraftingPlan = require '../src/scripts/models/crafting_plan' +ItemSlug = require '../src/scripts/models/item_slug' Mod = require '../src/scripts/models/mod' ModPack = require '../src/scripts/models/mod_pack' ModVersion = require '../src/scripts/models/mod_version' @@ -41,54 +42,43 @@ describe 'crafting_plan.coffee', -> describe 'under the simplest conditions', -> it 'can craft a single step recipe', -> - plan.want.add 'oak_plank' + plan.want.add ItemSlug.slugify 'oak_plank' plan.craft() - plan.need.toList().should.eql ['oak_log'] - plan.result.toList().should.eql [[4, 'minecraft__oak_plank']] + plan.need.unparse().should.equal 'oak_log' + plan.result.unparse().should.equal '4.minecraft__oak_plank' it 'can craft a multi-step recipe', -> - plan.want.add 'crafting_table' + plan.want.add ItemSlug.slugify 'crafting_table' plan.craft() - plan.need.toList().should.eql ['oak_log'] - plan.result.toList().should.eql ['minecraft__crafting_table'] + plan.need.unparse().should.equal 'oak_log' + plan.result.unparse().should.equal 'minecraft__crafting_table' it 'can craft a multi-step recipe using tools', -> - plan.want.add 'furnace' + plan.want.add ItemSlug.slugify 'furnace' plan.craft() - plan.need.toList().should.eql [[8, 'cobblestone']] - plan.result.toList().should.eql ['minecraft__furnace'] + plan.need.unparse().should.equal '8.cobblestone' + plan.result.unparse().should.equal 'minecraft__furnace' it 'can craft a multi-step recipe re-using tools', -> - plan.want.add 'iron_sword' + plan.want.add ItemSlug.slugify 'iron_sword' plan.craft() - plan.need.toList().should.eql [[2, 'furnace_fuel'], [2, 'iron_ore'], 'oak_log'] - plan.result.toList().should.eql [ - 'minecraft__iron_sword', - [2, 'minecraft__oak_plank'], - [3, 'minecraft__stick'] - ] + plan.need.unparse().should.equal '2.furnace_fuel:2.iron_ore:oak_log' + plan.result.unparse().should.equal 'minecraft__iron_sword:2.minecraft__oak_plank:3.minecraft__stick' describe 'with building tools', -> it 'can craft a multi-step recipe using tools', -> plan.includingTools = true - plan.want.add 'furnace' + plan.want.add ItemSlug.slugify 'furnace' plan.craft() - plan.need.toList().should.eql [[8, 'cobblestone'], 'oak_log'] - plan.result.toList().should.eql ['minecraft__crafting_table', 'minecraft__furnace'] + plan.need.unparse().should.equal '8.cobblestone:oak_log' + plan.result.unparse().should.equal 'minecraft__crafting_table:minecraft__furnace' it 'can craft a multi-step recipe re-using tools', -> plan.includingTools = true - plan.want.add 'iron_sword' + plan.want.add ItemSlug.slugify 'iron_sword' plan.craft() - plan.need.toList().should.eql [ - [8, 'cobblestone'], [2, 'furnace_fuel'], [2, 'iron_ore'], [2, 'oak_log'] - ] - plan.result.toList().should.eql [ - 'minecraft__crafting_table', - 'minecraft__furnace', - 'minecraft__iron_sword', - [2, 'minecraft__oak_plank'], - [3, 'minecraft__stick'] - ] + plan.need.unparse().should.eql '8.cobblestone:2.furnace_fuel:2.iron_ore:2.oak_log' + plan.result.unparse().should.equal '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 de03a93f9..f05196eac 100644 --- a/test/inventory.test.coffee +++ b/test/inventory.test.coffee @@ -9,6 +9,7 @@ EventRecorder = require './event_recorder' Inventory = require '../src/scripts/models/inventory' Item = require '../src/scripts/models/item' +ItemSlug = require '../src/scripts/models/item_slug' ######################################################################################################################## @@ -20,30 +21,30 @@ describe 'inventory.coffee', -> beforeEach -> inventory = new Inventory {}, silent:false - inventory.add 'wool', 4 - inventory.add 'string', 20 - inventory.add 'boat' + inventory.add ItemSlug.slugify('wool'), 4 + inventory.add ItemSlug.slugify('string'), 20 + inventory.add ItemSlug.slugify('boat') describe 'add', -> it 'can add to an empty inventory', -> - inventory.add 'iron_ingot', 4 + inventory.add ItemSlug.slugify('iron_ingot'), 4 stack = inventory._stacks['iron_ingot'] stack.constructor.name.should.equal 'Stack' - stack.slug.should.equal 'iron_ingot' + stack.itemSlug.qualified.should.equal 'iron_ingot' stack.quantity.should.equal 4 it 'can augment quantity of existing items', -> - inventory.add 'wool', 2 - inventory.toList().should.eql ['boat', [20, 'string'], [6, 'wool']] + inventory.add ItemSlug.slugify('wool'), 2 + inventory.unparse().should.equal 'boat:20.string:6.wool' it 'can add zero quantity', -> - inventory.add 'wool', 0 - inventory.toList().should.eql ['boat', [20, 'string'], [4, 'wool']] + inventory.add ItemSlug.slugify('wool'), 0 + inventory.unparse().should.equal 'boat:20.string:4.wool' it 'emits the proper events', -> events = new EventRecorder inventory - inventory.add 'iron_ingot', 10 + inventory.add ItemSlug.slugify('iron_ingot'), 10 events.names.should.eql [Event.add, Event.change] describe 'addInventory', -> @@ -51,24 +52,24 @@ describe 'inventory.coffee', -> it 'can add to an empty inventory', -> newInventory = new Inventory newInventory.addInventory inventory - newInventory._slugs.should.eql ['boat', 'string', 'wool'] + newInventory.unparse().should.equal 'boat:20.string:4.wool' it 'can add a mix of new and existing items', -> newInventory = new Inventory - newInventory.add 'string', 2 + newInventory.add ItemSlug.slugify('string'), 2 newInventory.addInventory inventory - newInventory.toList().should.eql ['boat', [22, 'string'], [4, 'wool']] + newInventory.unparse().should.equal 'boat:22.string:4.wool' describe 'clone', -> it 'creates an empty inventory from an empty inventory', -> a = new Inventory b = a.clone() - b._slugs.should.eql [] + b._itemSlugs.should.eql [] it 'faithfully copies an existing inventory', -> copy = inventory.clone() - copy.toList().should.eql ['boat', [20, 'string'], [4, 'wool']] + copy.unparse().should.equal 'boat:20.string:4.wool' describe 'each', -> @@ -80,16 +81,16 @@ describe 'inventory.coffee', -> it 'works when items have only been added', -> result = [] - inventory.each (stack)-> result.push stack.slug + inventory.each (stack)-> result.push stack.itemSlug.qualified result.should.eql ['boat', 'string', 'wool'] it 'works when items have been augmented', -> - inventory.add 'iron_ingot' - inventory.add 'boat' - inventory.add 'wool', 2 + inventory.add ItemSlug.slugify 'iron_ingot' + inventory.add ItemSlug.slugify 'boat' + inventory.add ItemSlug.slugify('wool'), 2 result = [] - inventory.each (stack)-> result.push stack.slug + inventory.each (stack)-> result.push stack.itemSlug.qualified result.should.eql ['boat', 'iron_ingot', 'string', 'wool'] describe 'hasAtLeast', -> @@ -107,39 +108,67 @@ describe 'inventory.coffee', -> inventory.hasAtLeast('wool', 4).should.be.true inventory.hasAtLeast('wool', 5).should.be.false - describe 'localizeTo', -> + describe 'localize', -> before -> modPack = - map: - wool: 'minecraft__wool' - string: 'minecraft__string' - boat: 'minecraft__boat' - stone_gear: 'buildcraft__stone_gear' + modSlug: + wool: 'minecraft' + string: 'minecraft' + boat: 'minecraft' + stone_gear: 'buildcraft' findItem: (slug)-> - [modSlug, itemSlug] = _.decomposeSlug slug - return slug:itemSlug, qualifiedSlug:@map[itemSlug] + return slug:new ItemSlug @modSlug[slug.item], slug.item 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'], + inventory.add ItemSlug.slugify 'stone_gear' + inventory.modPack = modPack + inventory.localize() + + slugs = [] + inventory.each (stack)-> slugs.push stack.itemSlug.qualified + slugs.should.eql [ + 'minecraft__boat' + 'minecraft__string' + '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'], + inventory.add ItemSlug.slugify 'buildcraft__stone_gear' + inventory.modPack = modPack + inventory.localize() + + slugs = [] + inventory.each (stack)-> slugs.push stack.itemSlug.qualified + slugs.should.eql [ + 'minecraft__boat' + 'minecraft__string' + 'minecraft__wool' 'buildcraft__stone_gear' ] + describe 'parse', -> + + beforeEach -> + inventory = new Inventory {}, silent:false + + it 'ignores an empty string', -> + result = inventory.parse '' + result.unparse().should.eql '' + + it 'can parse a single item without quantity', -> + result = inventory.parse 'wool' + result.unparse().should.equal 'wool' + + it 'can parse a single item with quantity', -> + result = inventory.parse '4.wool' + result.unparse().should.equal '4.wool' + + it 'can parse multiple mixed-type items', -> + result = inventory.parse '4.wool:10.string:boat' + result.unparse().should.equal 'boat:10.string:4.wool' + describe 'pop', -> it 'returns null for an empty inventory', -> @@ -149,9 +178,9 @@ describe 'inventory.coffee', -> it 'completely removes the last item', -> stack = inventory.pop() - stack.slug.should.equal 'wool' + stack.itemSlug.qualified.should.equal 'wool' stack.quantity.should.equal 4 - inventory.toList().should.eql ['boat', [20, 'string']] + inventory.unparse().should.equal 'boat:20.string' it 'triggers the right events', -> events = new EventRecorder inventory diff --git a/test/inventory_parser.test.coffee b/test/inventory_parser.test.coffee deleted file mode 100644 index f78321175..000000000 --- a/test/inventory_parser.test.coffee +++ /dev/null @@ -1,42 +0,0 @@ -### -# Crafting Guide - inventory_parser.test.coffee -# -# Copyright (c) 2014-2015 by Redwood Labs -# All rights reserved. -### - -Inventory = require '../src/scripts/models/inventory' -InventoryParser = require '../src/scripts/models/inventory_parser' -Item = require '../src/scripts/models/item' - -######################################################################################################################## - -parser = null - -######################################################################################################################## - -describe 'inventory_parser.coffee', -> - - beforeEach -> parser = new InventoryParser - - it 'returns an empty Inventory for an empty string', -> - result = parser.parse '' - result.toList().should.eql [] - - it 'can parse a single item without quantity', -> - result = parser.parse 'wool' - result.toList().should.eql ['wool'] - - it 'can parse a single item with quantity', -> - result = parser.parse '4.wool' - result.toList().should.eql [[4, 'wool']] - - it 'can parse multiple mixed-type items', -> - 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.toList().should.eql [[8, 'string'], [4, 'wool']] diff --git a/test/item_slug.test.coffee b/test/item_slug.test.coffee new file mode 100644 index 000000000..1fbc4fa68 --- /dev/null +++ b/test/item_slug.test.coffee @@ -0,0 +1,136 @@ +### +Crafting Guide - item_slug.coffee + +Copyright (c) 2015 by Redwood Labs +All rights reserved. +### + +ItemSlug = require '../src/scripts/models/item_slug' + +######################################################################################################################## + +describe 'item_slug.coffee', -> + + describe 'constructor', -> + + it 'can handle one argument', -> + slug = new ItemSlug 'alpha' + slug.item.should.equal 'alpha' + expect(slug.mod).to.be.null + slug.qualified.should.equal 'alpha' + + it 'can handle two arguments', -> + slug = new ItemSlug 'alpha', 'bravo' + slug.mod.should.equal 'alpha' + slug.item.should.equal 'bravo' + slug.qualified.should.equal 'alpha__bravo' + + it 'throws with zero arguments', -> + f = -> new ItemSlug + expect(f).to.throw Error, 'expected arguments to be "modSlug, itemSlug" or just "itemSlug"' + + it 'throws with more arguments', -> + f = -> new ItemSlug 'alpha', 'bravo', 'charlie' + expect(f).to.throw Error, 'expected arguments to be "modSlug, itemSlug" or just "itemSlug"' + + describe 'ItemSlug.compare', -> + + it 'sorts qualified slugs first', -> + a = new ItemSlug 'alpha' + b = new ItemSlug 'bravo', 'charlie' + + ItemSlug.compare(a, b).should.equal +1 + ItemSlug.compare(b, a).should.equal -1 + + it 'sorts by mod when both are qualified', -> + a = new ItemSlug 'alpha', 'bravo' + b = new ItemSlug 'charlie', 'delta' + + ItemSlug.compare(a, b).should.equal -1 + ItemSlug.compare(b, a).should.equal +1 + + it 'sorts by item when both are qualified in the same mod', -> + a = new ItemSlug 'alpha', 'bravo' + b = new ItemSlug 'charlie', 'bravo' + + ItemSlug.compare(a, b).should.equal -1 + ItemSlug.compare(b, a).should.equal +1 + + it 'sorts by item when not qualified', -> + a = new ItemSlug 'alpha' + b = new ItemSlug 'bravo' + + ItemSlug.compare(a, b).should.equal -1 + ItemSlug.compare(b, a).should.equal +1 + + describe 'ItemSlug.equal', -> + + it 'requires both to have the same mod', -> + a = new ItemSlug 'alpha', 'bravo' + b = new ItemSlug 'alpha', 'charlie' + c = new ItemSlug 'alpha', 'bravo' + + ItemSlug.equal(a, b).should.be.false + ItemSlug.equal(a, c).should.be.true + + it 'requires both to have the same item', -> + a = new ItemSlug 'alpha', 'bravo' + b = new ItemSlug 'alpha', 'charlie' + c = new ItemSlug 'alpha', 'bravo' + + ItemSlug.equal(a, b).should.be.false + ItemSlug.equal(a, c).should.be.true + + describe 'ItemSlug.slugify', -> + + it 'can slugify a pure name', -> + slug = ItemSlug.slugify 'Alpha Bravo (Charlie)' + slug.item.should.equal 'alpha_bravo_charlie' + expect(slug.mod).to.be.null + + it 'can slugify a simple item slug', -> + slug = ItemSlug.slugify 'alpha_bravo_charlie' + slug.item.should.equal 'alpha_bravo_charlie' + expect(slug.mod).to.be.null + + it 'can slugify a fully-qualified slug', -> + slug = ItemSlug.slugify 'alpha_bravo__charlie_delta' + slug.mod.should.equal 'alpha_bravo' + slug.item.should.equal 'charlie_delta' + + describe 'matches', -> + + it 'ignores mod when either is unqualified', -> + a = new ItemSlug 'alpha', 'bravo' + b = new ItemSlug 'bravo' + c = new ItemSlug 'charlie' + + a.matches(b).should.be.true + a.matches(c).should.be.false + + it 'observes differences in mod when all are qualified', -> + a = new ItemSlug 'alpha', 'bravo' + b = new ItemSlug 'charlie', 'bravo' + c = new ItemSlug 'delta', 'echo' + d = new ItemSlug 'alpha', 'bravo' + + a.matches(b).should.be.false + a.matches(c).should.be.false + a.matches(d).should.be.true + + describe 'isQualified', -> + + it 'returns true only when the mod slug is set', -> + a = new ItemSlug 'alpha', 'bravo' + b = new ItemSlug 'charlie' + a.isQualified.should.be.true + b.isQualified.should.be.false + + describe '[]', -> + + it 'allows slugs as a key', -> + slug = new ItemSlug 'alpha', 'bravo' + data = {} + data[slug] = 'foo' + data['alpha__bravo'].should.equal 'foo' + data[slug].should.equal 'foo' diff --git a/test/mod_pack.test.coffee b/test/mod_pack.test.coffee index 8cec5be88..3c580170a 100644 --- a/test/mod_pack.test.coffee +++ b/test/mod_pack.test.coffee @@ -6,6 +6,7 @@ All rights reserved. ### Item = require '../src/scripts/models/item' +ItemSlug = require '../src/scripts/models/item_slug' Mod = require '../src/scripts/models/mod' ModPack = require '../src/scripts/models/mod_pack' ModVersion = require '../src/scripts/models/mod_version' @@ -23,7 +24,7 @@ describe 'mod_pack.coffee', -> 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.registerName 'iron_chestplate', 'Iron Chestplate' + minecraft.activeModVersion.registerName ItemSlug.slugify('iron_chestplate'), 'Iron Chestplate' buildcraft = new Mod slug:'buildcraft', name:'Buildcraft' buildcraft.addModVersion new ModVersion modSlug:buildcraft.slug, version:'6.2.6' @@ -46,24 +47,24 @@ describe 'mod_pack.coffee', -> describe 'findItem', -> it 'can find an item by partial slug', -> - item = modPack.findItem 'wool' - item.qualifiedSlug.should.equal 'minecraft__wool' + item = modPack.findItem ItemSlug.slugify 'wool' + item.slug.qualified.should.equal 'minecraft__wool' it 'can find an item by full slug', -> - item = modPack.findItem 'minecraft__wool' + item = modPack.findItem ItemSlug.slugify '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 = modPack.findItem ItemSlug.slugify '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 = modPack.findItem ItemSlug.slugify 'wrench' item.name.should.equal 'Wrench' item.modVersion.mod.name.should.equal 'Buildcraft' @@ -80,7 +81,7 @@ describe 'mod_pack.coffee', -> describe 'findItemDisplay', -> it 'returns all data for a regular Minecraft item', -> - display = modPack.findItemDisplay 'bed' + display = modPack.findItemDisplay ItemSlug.slugify 'bed' display.iconUrl.should.equal '/data/minecraft/1.7.10/images/bed.png' display.itemUrl.should.equal '/mod/minecraft/bed' display.itemName.should.equal 'Bed' @@ -88,14 +89,14 @@ describe 'mod_pack.coffee', -> it 'returns all data for an item in an enabled mod', -> buildcraft.activeVersion = '6.2.6' - display = modPack.findItemDisplay 'stone_gear' + display = modPack.findItemDisplay ItemSlug.slugify 'stone_gear' display.iconUrl.should.equal '/data/buildcraft/6.2.6/images/stone_gear.png' 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 = modPack.findItemDisplay ItemSlug.slugify 'iron_chestplate' display.iconUrl.should.equal '/data/minecraft/1.7.10/images/iron_chestplate.png' display.itemUrl.should.equal '/mod/minecraft/iron_chestplate' display.itemName.should.equal 'Iron Chestplate' diff --git a/test/mod_version.test.coffee b/test/mod_version.test.coffee index 90bbcfcd8..4720a1c02 100644 --- a/test/mod_version.test.coffee +++ b/test/mod_version.test.coffee @@ -6,6 +6,7 @@ All rights reserved. ### Item = require '../src/scripts/models/item' +ItemSlug = require '../src/scripts/models/item_slug' ModVersion = require '../src/scripts/models/mod_version' ######################################################################################################################## @@ -57,24 +58,24 @@ describe 'mod_version.coffee', -> it 'returns immediately for unknown group', -> slugs = [] - modVersion.eachItemInGroup 'foobar', (item)-> slugs.push item.slug + modVersion.eachItemInGroup 'foobar', (item)-> slugs.push item.slug.qualified slugs.should.eql [] it 'calls callback for exactly the items in a group in order', -> slugs = [] - modVersion.eachItemInGroup 'letter', (item)-> slugs.push item.slug - slugs.should.eql ['alpha', 'bravo'] + modVersion.eachItemInGroup 'letter', (item)-> slugs.push item.slug.qualified + slugs.should.eql ['test__alpha', 'test__bravo'] slugs = [] - modVersion.eachItemInGroup 'number', (item)-> slugs.push item.slug - slugs.should.eql ['one', 'two'] + modVersion.eachItemInGroup 'number', (item)-> slugs.push item.slug.qualified + slugs.should.eql ['test__one', 'test__two'] describe 'findItemByName', -> it 'locates items by slugified name', -> modVersion.addItem new Item name:'Crafting Table' - modVersion.findItemByName('Crafting Table').slug.should.equal 'crafting_table' + modVersion.findItemByName('Crafting Table').slug.qualified.should.equal 'test__crafting_table' describe 'findRecipes', -> @@ -91,5 +92,5 @@ describe 'mod_version.coffee', -> """ it 'finds all recipes which list item as output', -> - recipes = modVersion.findRecipes 'test__bucket' - (r.output[0].slug for r in recipes).sort().should.eql ['test__bucket', 'test__cake', 'test__cake'] + recipes = modVersion.findRecipes ItemSlug.slugify('Bucket') + (r.output[0].itemSlug.item for r in recipes).sort().should.eql ['bucket', 'cake', 'cake'] diff --git a/test/parser_versions/mod_version_parser_v1.test.coffee b/test/parser_versions/mod_version_parser_v1.test.coffee index 3c40589b8..e45575ad6 100644 --- a/test/parser_versions/mod_version_parser_v1.test.coffee +++ b/test/parser_versions/mod_version_parser_v1.test.coffee @@ -6,6 +6,7 @@ All rights reserved. ### CommandParserVersionBase = require '../../src/scripts/models/parser_versions/command_parser_version_base' +ItemSlug = require '../../src/scripts/models/item_slug' ModVersion = require '../../src/scripts/models/mod_version' ModVersionParserV1 = require '../../src/scripts/models/parser_versions/mod_version_parser_v1' @@ -28,9 +29,9 @@ describe 'mod_version_parser_v1.coffee', -> recipe:; input:Alpha; pattern:... .0. ...; recipe:; input:Bravo; pattern:... 0.0 ...;" modVersion = parser.parse recipes - recipes = modVersion.findRecipes 'test__charlie' - recipes[0].input[0].slug.should.equal 'alpha' - recipes[1].input[0].slug.should.equal 'bravo' + recipes = modVersion.findRecipes ItemSlug.slugify 'charlie' + recipes[0].input[0].itemSlug.qualified.should.equal 'alpha' + recipes[1].input[0].itemSlug.qualified.should.equal 'bravo' describe 'name', -> @@ -68,8 +69,8 @@ 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.findRecipes('test__charlie')[0].input) - slugs.should.eql ['alpha', 'bravo', 'test__charlie'] + slugs = (s.itemSlug.item for s in modVersion.findRecipes(ItemSlug.slugify('charlie'))[0].input) + slugs.should.eql ['alpha', 'bravo', 'charlie'] it 'requires an "input" declaration', -> func = -> parser.parse baseText + 'recipe:; pattern: ... .0. ...' @@ -85,13 +86,13 @@ describe 'mod_version_parser_v1.coffee', -> it 'registers slugs for each input name', -> modVersion = parser.parse baseText + 'recipe:; input:Delta, Echo, Foxtrot; pattern:...012...' - modVersion._slugs.should.eql ['charlie', 'delta', 'echo', 'foxtrot'] + (s.item for s in modVersion._slugs).should.eql ['charlie', 'delta', 'echo', 'foxtrot'] describe 'pattern', -> it 'adds "pattern" when present', -> modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern:... .0. .1.' - modVersion.findRecipes('test__charlie')[0].pattern.should.equal '... .0. .1.' + modVersion.findRecipes(ItemSlug.slugify('charlie'))[0].pattern.should.equal '... .0. .1.' it 'requires a "pattern" declaration', -> func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo' @@ -119,7 +120,7 @@ describe 'mod_version_parser_v1.coffee', -> it 'computes the input stack sizes from the pattern', -> modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo, Charlie; pattern:111 .0. 2.2' - recipe = modVersion.findRecipes('test__charlie')[0] + recipe = modVersion.findRecipes(ItemSlug.slugify('charlie'))[0] recipe.input[0].quantity.should.equal 1 recipe.input[1].quantity.should.equal 3 recipe.input[2].quantity.should.equal 2 @@ -135,7 +136,7 @@ describe 'mod_version_parser_v1.coffee', -> it 'adds "quantity" when present', -> modVersion = parser.parse baseText + 'quantity: 2' - modVersion.findRecipes('test__charlie')[0].output[0].quantity.should.equal 2 + modVersion.findRecipes(ItemSlug.slugify('charlie'))[0].output[0].quantity.should.equal 2 it 'does not allow a duplicate "quantity" declaration', -> func = -> parser.parse baseText + 'quantity:1; quantity:2' @@ -147,7 +148,7 @@ describe 'mod_version_parser_v1.coffee', -> it 'assumes a quantity of 1 by default', -> modVersion = parser.parse baseText - modVersion.findRecipes('test__charlie')[0].output[0].quantity.should.equal 1 + modVersion.findRecipes(ItemSlug.slugify('charlie'))[0].output[0].quantity.should.equal 1 it 'does not allow "quantity" before recipe', -> func = -> parser.parse 'item:Bravo; quantity:12; recipe:;' @@ -160,18 +161,18 @@ describe 'mod_version_parser_v1.coffee', -> it 'adds a single item as the default output', -> modVersion = parser.parse baseText - stack = modVersion.findRecipes('test__bravo')[0].output[0] - stack.slug.should.equal 'test__bravo' + stack = modVersion.findRecipes(ItemSlug.slugify('bravo'))[0].output[0] + stack.itemSlug.qualified.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.findRecipes('test__bravo')[0].output - output[0].slug.should.equal 'test__bravo' + output = modVersion.findRecipes(ItemSlug.slugify('bravo'))[0].output + output[0].itemSlug.qualified.should.equal 'test__bravo' output[0].quantity.should.equal 1 - output[1].slug.should.equal 'test__delta' + output[1].itemSlug.qualified.should.equal 'test__delta' output[1].quantity.should.equal 2 - output[2].slug.should.equal 'echo' + output[2].itemSlug.qualified.should.equal 'echo' output[2].quantity.should.equal 4 it 'does not allow "extras" before "recipe"', -> @@ -180,7 +181,9 @@ describe 'mod_version_parser_v1.coffee', -> it 'registers slugs for each output name', -> modVersion = parser.parse baseText + 'extras:Delta, Echo' - modVersion._slugs.should.eql ['bravo', 'charlie', 'delta', 'echo'] + (s.qualified for s in modVersion._slugs).should.eql [ + 'test__bravo', 'test__delta', 'charlie', 'echo' + ] it 'does not allow a duplicate "extras" declaration', -> func = -> parser.parse baseText + 'extras:Echo; extras:Delta' @@ -193,17 +196,17 @@ describe 'mod_version_parser_v1.coffee', -> it 'can add a single tool', -> modVersion = parser.parse baseText + 'tools: Furnace' - modVersion.findRecipes('test__bravo')[0].tools[0].slug.should.equal 'furnace' + modVersion.findRecipes(ItemSlug.slugify('bravo'))[0].tools[0].itemSlug.item.should.equal 'furnace' it 'can add multiple tools', -> modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace' - tools = modVersion.findRecipes('test__bravo')[0].tools - tools[0].slug.should.equal 'crafting_table' - tools[1].slug.should.equal 'furnace' + tools = modVersion.findRecipes(ItemSlug.slugify('bravo'))[0].tools + tools[0].itemSlug.item.should.equal 'crafting_table' + tools[1].itemSlug.item.should.equal 'furnace' it 'registers slugs for each tool name', -> modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace' - modVersion._slugs.should.eql ['bravo', 'charlie', 'crafting_table', 'furnace'] + (s.item for s in modVersion._slugs).should.eql ['bravo', 'charlie', 'crafting_table', 'furnace'] it 'does not allow a duplicate "tools" declaration', -> func = -> parser.parse baseText + 'tools:Crafting Table; tools:Furnace' @@ -245,6 +248,4 @@ describe 'mod_version_parser_v1.coffee', -> actual = CommandParserVersionBase.simplify text expected = CommandParserVersionBase.simplify baseText - logger.debug "actual:\n>>>#{actual}<<<\n\n\n" - logger.debug "expected:\n>>>#{expected}<<<" actual.should.equal expected diff --git a/test/recipe.test.coffee b/test/recipe.test.coffee index 4cf2409cf..3426b43b4 100644 --- a/test/recipe.test.coffee +++ b/test/recipe.test.coffee @@ -5,9 +5,10 @@ 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' +Item = require '../src/scripts/models/item' +ItemSlug = require '../src/scripts/models/item_slug' +Recipe = require '../src/scripts/models/recipe' +Stack = require '../src/scripts/models/stack' ######################################################################################################################## @@ -20,7 +21,10 @@ describe 'recipe.coffee', -> describe 'constructor', -> beforeEach -> - input = [ new Stack(slug:'iron_gear'), new Stack(slug:'gold_ingot', quantity:4) ] + input = [ + new Stack(itemSlug:new ItemSlug('iron_gear')), + new Stack(itemSlug:new ItemSlug('gold_ingot'), quantity:4) + ] pattern = '.1. 101 .1.' it 'requires input', -> @@ -31,29 +35,32 @@ describe 'recipe.coffee', -> it 'requires either outputs or a slug', -> f = -> new Recipe input:input, pattern:pattern - expect(f).to.throw 'attributes.slug or attributes.output is required' + expect(f).to.throw 'attributes.itemSlug or attributes.output is required' it 'creates default output', -> - recipe = new Recipe slug:'gold_gear', input:input, pattern:pattern + recipe = new Recipe itemSlug:ItemSlug.slugify('gold_gear'), input:input, pattern:pattern recipe.output.length.should.equal 1 - recipe.output[0].slug.should.equal 'gold_gear' + recipe.output[0].itemSlug.qualified.should.equal 'gold_gear' recipe.output[0].quantity.should.equal 1 it 'assigns a default slug', -> - recipe = new Recipe input:input, pattern:pattern, output:[new Stack slug:'gold_gear'] - recipe.slug.should.equal 'gold_gear' + recipe = new Recipe input:input, pattern:pattern, output:[new Stack itemSlug:ItemSlug.slugify('gold_gear')] + recipe.itemSlug.qualified.should.equal 'gold_gear' describe 'getItemSlugAt', -> beforeEach -> - input = [ new Stack(slug:'iron_gear'), new Stack(slug:'gold_ingot', quantity:4) ] - recipe = new Recipe slug:'gold_gear', input:input, pattern:'.1. 101 .1.' + input = [ + new Stack itemSlug:ItemSlug.slugify('iron_gear') + new Stack itemSlug:ItemSlug.slugify('gold_ingot'), quantity:4 + ] + recipe = new Recipe itemSlug:'gold_gear', input:input, pattern:'.1. 101 .1.' it 'returns the proper item for an early slot', -> - recipe.getItemSlugAt(1).should.equal 'gold_ingot' + recipe.getItemSlugAt(1).qualified.should.equal 'gold_ingot' it 'returns the proper item for a late slot', -> - recipe.getItemSlugAt(4).should.equal 'iron_gear' + recipe.getItemSlugAt(4).qualified.should.equal 'iron_gear' it 'returns null for an invalid slot', -> expect(recipe.getItemSlugAt(12)).to.be.null @@ -61,7 +68,10 @@ describe 'recipe.coffee', -> describe '_parsePattern', -> beforeEach -> - recipe = new Recipe slug:'oak_wood_planks', input:[new Stack slug:'oak_wood'], pattern:'... .0. ...' + recipe = new Recipe + itemSlug: 'oak_wood_planks', + input: [new Stack itemSlug:new ItemSlug('oak_wood')], + pattern:'... .0. ...' it 'normalizes invalid characters', -> recipe._parsePattern('$$0 #() 010').should.equal '..0 ... 010' diff --git a/test/test.coffee b/test/test.coffee index e93179ed9..c5f3e423e 100644 --- a/test/test.coffee +++ b/test/test.coffee @@ -28,15 +28,16 @@ require '../src/scripts/underscore_mixins' mocha.setup 'bdd' -require './crafting_plan.test' +# tests are roughly in order of how errors should be tackled +require './string_builder.test' +require './item_slug.test' require './inventory.test' -require './inventory_parser.test' +require './recipe.test' +require './mod_version.test' require './mod.test' require './mod_pack.test' -require './mod_version.test' require './parser_versions/mod_version_parser_v1.test' -require './recipe.test' -require './string_builder.test' +require './crafting_plan.test' mocha.checkLeaks() mocha.globals ['LiveReload']