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
This commit is contained in:
Andrew Miner
2015-02-18 10:30:53 -08:00
parent 100ce1bc70
commit e88821d449
33 changed files with 624 additions and 535 deletions
+2 -2
View File
@@ -55,8 +55,8 @@ Text.title = 'Crafting Guide for Minecraft | The Ultimate Step-by-Step Tutorial
exports.Url = Url = {} exports.Url = Url = {}
Url.crafting = _.template "/crafting/<%= inventoryText %>" Url.crafting = _.template "/crafting/<%= inventoryText %>"
Url.itemIcon = _.template "/data/<%= modSlug %>/<%= modVersion %>/images/<%= slug %>.png" Url.itemIcon = _.template "/data/<%= modSlug %>/<%= modVersion %>/images/<%= itemSlug %>.png"
Url.item = _.template "/mod/<%= modSlug %>/<%= slug %>" Url.item = _.template "/mod/<%= modSlug %>/<%= itemSlug %>"
Url.mod = _.template "/mod/<%= modSlug %>" Url.mod = _.template "/mod/<%= modSlug %>"
Url.modData = _.template "/data/<%= modSlug %>/mod.cg" Url.modData = _.template "/data/<%= modSlug %>/mod.cg"
Url.modVersion = _.template "/data/<%= modSlug %>/<%= modVersion %>/mod-version.cg" Url.modVersion = _.template "/data/<%= modSlug %>/<%= modVersion %>/mod-version.cg"
@@ -64,8 +64,8 @@ module.exports = class CraftingGridController extends BaseController
if slot >= @_slotCount then throw new Error "slot (#{slot}) must be less than #{@_slotCount}" if slot >= @_slotCount then throw new Error "slot (#{slot}) must be less than #{@_slotCount}"
return null unless @model? return null unless @model?
slug = @model.getItemSlugAt slot itemSlug = @model.getItemSlugAt slot
return null unless slug? return null unless itemSlug?
itemDisplay = @_modPack.findItemDisplay slug itemDisplay = @_modPack.findItemDisplay itemSlug
return itemDisplay return itemDisplay
@@ -10,7 +10,6 @@ CraftingTableController = require './crafting_table_controller'
{Event} = require '../constants' {Event} = require '../constants'
ImageLoader = require './image_loader' ImageLoader = require './image_loader'
InventoryController = require './inventory_controller' InventoryController = require './inventory_controller'
InventoryParser = require '../models/inventory_parser'
CraftingPage = require '../models/crafting_page' CraftingPage = require '../models/crafting_page'
ModPackController = require './mod_pack_controller' ModPackController = require './mod_pack_controller'
NameFinder = require '../models/name_finder' NameFinder = require '../models/name_finder'
@@ -32,7 +31,6 @@ module.exports = class CraftingPageController extends BaseController
super options super options
@_imageLoader = options.imageLoader @_imageLoader = options.imageLoader
@_parser = new InventoryParser options.modPack
@_storage = options.storage @_storage = options.storage
# Event Methods ################################################################################ # Event Methods ################################################################################
@@ -8,7 +8,6 @@ All rights reserved.
BaseController = require './base_controller' BaseController = require './base_controller'
{Duration} = require '../constants' {Duration} = require '../constants'
ImageLoader = require './image_loader' ImageLoader = require './image_loader'
InventoryParser = require '../models/inventory_parser'
MinimalRecipeController = require './minimal_recipe_controller' MinimalRecipeController = require './minimal_recipe_controller'
######################################################################################################################## ########################################################################################################################
@@ -34,7 +33,6 @@ module.exports = class CraftingTableController extends BaseController
@model.stepIndex -= 1 @model.stepIndex -= 1
onReportProblem: -> onReportProblem: ->
parser = new InventoryParser @_modPack
itemList = parser.unparse @model.plan.want itemList = parser.unparse @model.plan.want
toolsMessage = if @model.plan.includingTools then '(including tools)' else '' toolsMessage = if @model.plan.includingTools then '(including tools)' else ''
message = "When I was on step #{@model.stepIndex + 1} of making: message = "When I was on step #{@model.stepIndex + 1} of making:
@@ -68,7 +68,7 @@ module.exports = class FullRecipeController extends BaseController
result = [] result = []
if @model? if @model?
for stack in @model.tools for stack in @model.tools
item = @modPack.findItem stack.slug item = @modPack.findItem stack.itemSlug
result.push item.name if item? result.push item.name if item?
return result return result
@@ -78,7 +78,7 @@ module.exports = class FullRecipeController extends BaseController
if @model? if @model?
for stack in @model.input for stack in @model.input
inputs.add stack.slug, stack.quantity inputs.add stack.itemSlug, stack.quantity
_refreshOutputs: -> _refreshOutputs: ->
@@ -87,4 +87,4 @@ module.exports = class FullRecipeController extends BaseController
if @model? if @model?
for stack in @model.output for stack in @model.output
outputs.add stack.slug, stack.quantity outputs.add stack.itemSlug, stack.quantity
@@ -196,4 +196,4 @@ module.exports = class InventoryController extends BaseController
controller.$el.fadeOut duration:Duration.fast, complete:-> @remove() controller.$el.fadeOut duration:Duration.fast, complete:-> @remove()
_removeStack: (stack)-> _removeStack: (stack)->
@model.remove stack.slug, stack.quantity @model.remove stack.itemSlug, stack.quantity
@@ -7,8 +7,9 @@ All rights reserved.
BaseController = require './base_controller' BaseController = require './base_controller'
{Duration} = require '../constants' {Duration} = require '../constants'
{Key} = require '../constants'
ImageLoader = require './image_loader' ImageLoader = require './image_loader'
ItemSlug = require '../models/item_slug'
{Key} = require '../constants'
NameFinder = require '../models/name_finder' NameFinder = require '../models/name_finder'
StackController = require './stack_controller' StackController = require './stack_controller'
@@ -38,10 +39,10 @@ module.exports = class InventoryTableController extends BaseController
# Event Methods ################################################################################ # Event Methods ################################################################################
onAddButtonClicked: -> onAddButtonClicked: ->
name = @$nameField.val() name = @modpack.findItemByName @$nameField.val()
return unless @modPack.isValidName name return unless item?
@model.add _.slugify(name), parseInt(@$quantityField.val()) @model.add item.slug, parseInt(@$quantityField.val())
@$nameField.val '' @$nameField.val ''
@$quantityField.val '1' @$quantityField.val '1'
@@ -160,7 +161,7 @@ module.exports = class InventoryTableController extends BaseController
# Private Methods ############################################################################## # Private Methods ##############################################################################
_removeStack: (stack)-> _removeStack: (stack)->
@model.remove stack.slug, stack.quantity @model.remove stack.itemSlug, stack.quantity
_updateNameAutocomplete: -> _updateNameAutocomplete: ->
onChanged = => @onNameFieldChanged() onChanged = => @onNameFieldChanged()
@@ -48,7 +48,7 @@ module.exports = class MinimalRecipeController extends BaseController
if @model? if @model?
outputStack = @model.output[0] outputStack = @model.output[0]
if outputStack? if outputStack?
display = @_modPack.findItemDisplay outputStack.slug display = @_modPack.findItemDisplay outputStack.itemSlug
@$outputLink.attr 'href', display.itemUrl @$outputLink.attr 'href', display.itemUrl
@$outputLink.attr 'title', display.itemName @$outputLink.attr 'title', display.itemName
@$outputImg.attr 'alt', display.itemName @$outputImg.attr 'alt', display.itemName
@@ -72,6 +72,6 @@ module.exports = class MinimalRecipeController extends BaseController
result = [] result = []
if @model? if @model?
for stack in @model.tools for stack in @model.tools
item = @_modPack.findItem stack.slug item = @_modPack.findItem stack.itemSlug
result.push item.name if item? result.push item.name if item?
return result return result
@@ -49,7 +49,6 @@ module.exports = class StackController extends BaseController
@$nameLink.html display.itemName @$nameLink.html display.itemName
@$nameLink.attr 'href', display.itemUrl @$nameLink.attr 'href', display.itemUrl
@$quantityField.html @model.quantity @$quantityField.html @model.quantity
# @$removeButton.css opacity:(if @editable then 1 else 0)
@$action.css display:(if @editable then 'table-cell' else 'none') @$action.css display:(if @editable then 'table-cell' else 'none')
+7 -6
View File
@@ -12,6 +12,7 @@ CraftingPageController = require './controllers/crafting_page_controller'
{Event} = require './constants' {Event} = require './constants'
HeaderController = require './controllers/header_controller' HeaderController = require './controllers/header_controller'
ItemPageController = require './controllers/item_page_controller' ItemPageController = require './controllers/item_page_controller'
ItemSlug = require './models/item_slug'
Mod = require './models/mod' Mod = require './models/mod'
ModPack = require './models/mod_pack' ModPack = require './models/mod_pack'
ModPageController = require './controllers/mod_page_controller' ModPageController = require './controllers/mod_page_controller'
@@ -42,8 +43,8 @@ module.exports = class CraftingGuideRouter extends Backbone.Router
makeResponder = (m)-> return -> makeResponder = (m)-> return ->
m.activeModVersion.fetch() if m.activeModVersion? m.activeModVersion.fetch() if m.activeModVersion?
for slug in DefaultMods for modSlug in DefaultMods
mod = new Mod slug:slug mod = new Mod slug:modSlug
mod.on Event.change + ':activeModVersion', makeResponder mod mod.on Event.change + ':activeModVersion', makeResponder mod
@storage.register "mod:#{mod.slug}", mod, 'activeVersion' @storage.register "mod:#{mod.slug}", mod, 'activeVersion'
mod.fetch() mod.fetch()
@@ -75,13 +76,13 @@ module.exports = class CraftingGuideRouter extends Backbone.Router
@_setPage 'item', controller @_setPage 'item', controller
modItem: (modSlug, itemSlug)-> modItem: (modSlug, itemSlug)->
slug = _.composeSlugs modSlug, itemSlug slug = new ItemSlug modSlug, itemSlug
controller = new ItemPageController _.extend {itemSlug:slug}, @_defaultOptions controller = new ItemPageController _.extend {itemSlug:slug}, @_defaultOptions
@_setPage 'item', controller @_setPage 'item', controller
mod: (slug)-> mod: (modSlug)->
controller = new ModPageController _.extend {}, @_defaultOptions controller = new ModPageController _.extend {}, @_defaultOptions
controller.model = @modPack.getMod slug controller.model = @modPack.getMod modSlug
@_setPage 'mod', controller @_setPage 'mod', controller
root: -> root: ->
@@ -89,7 +90,7 @@ module.exports = class CraftingGuideRouter extends Backbone.Router
text = '' text = ''
if params.recipeName? if params.recipeName?
if params.count? if params.count? and params.count > 1
text = "#{params.count}.#{_.slugify(params.recipeName)}" text = "#{params.count}.#{_.slugify(params.recipeName)}"
else else
text = _.slugify params.recipeName text = _.slugify params.recipeName
-10
View File
@@ -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
+6 -7
View File
@@ -9,7 +9,7 @@ BaseModel = require './base_model'
CraftingPlan = require './crafting_plan' CraftingPlan = require './crafting_plan'
CraftingTable = require './crafting_table' CraftingTable = require './crafting_table'
{Event} = require '../constants' {Event} = require '../constants'
InventoryParser = require './inventory_parser' Inventory = require './inventory'
ModPack = require './mod_pack' ModPack = require './mod_pack'
######################################################################################################################## ########################################################################################################################
@@ -23,8 +23,6 @@ module.exports = class CraftingPage extends BaseModel
attributes.table ?= new CraftingTable plan:attributes.plan attributes.table ?= new CraftingTable plan:attributes.plan
super attributes, options super attributes, options
@_parser = new InventoryParser
@modPack.on Event.change, => @_consumeParams() @modPack.on Event.change, => @_consumeParams()
@on Event.change + ':params', => @_consumeParams() @on Event.change + ':params', => @_consumeParams()
@@ -37,12 +35,13 @@ module.exports = class CraftingPage extends BaseModel
if not @params.inventoryText? if not @params.inventoryText?
@params = null @params = null
else else
inventory = @_parser.parse @params.inventoryText inventory = new Inventory
inventory.parse @params.inventoryText
inventory.each (stack)=> inventory.each (stack)=>
item = @modPack.findItem stack.slug, enableAsNeeded:true item = @modPack.findItem stack.itemSlug, enableAsNeeded:true
return unless item? and item.isCraftable return unless item? and item.isCraftable
@plan.want.add stack.slug, stack.quantity @plan.want.add stack.itemSlug, stack.quantity
inventory.remove stack.slug inventory.remove stack.itemSlug
if inventory.isEmpty then @params = null if inventory.isEmpty then @params = null
+43 -43
View File
@@ -18,10 +18,10 @@ module.exports = class CraftingPlan extends BaseModel
attributes.includingTools ?= false attributes.includingTools ?= false
super attributes, options super attributes, options
@have = new Inventory @have = new Inventory modPack:@modPack
@want = new Inventory @want = new Inventory modPack:@modPack
@need = new Inventory @need = new Inventory modPack:@modPack
@result = new Inventory @result = new Inventory modPack:@modPack
recraft = _.debounce (=> @craft()), 100 recraft = _.debounce (=> @craft()), 100
for inventory in [@have, @want] for inventory in [@have, @want]
@@ -46,17 +46,17 @@ module.exports = class CraftingPlan extends BaseModel
logger.info => "crafting #{@want}#{toolsMessage} starting with #{@have}" logger.info => "crafting #{@want}#{toolsMessage} starting with #{@have}"
@clear() @clear()
@have.localizeTo @modPack @have.localize()
@want.localizeTo @modPack @want.localize()
@result.addInventory @have @result.addInventory @have
@steps = {} @steps = {}
@_reservedSteps = {} @_reservedSteps = {}
@want.each (stack)=> @want.each (stack)=>
@_findSteps stack.slug @_findSteps stack.itemSlug
item = @modPack.findItem stack.slug item = @modPack.findItem stack.itemSlug
@need.add item.qualifiedSlug, stack.quantity @need.add item.slug, stack.quantity
@_reservedSteps = null @_reservedSteps = null
@steps = _.values @steps @steps = _.values @steps
@@ -72,11 +72,11 @@ module.exports = class CraftingPlan extends BaseModel
removeUncraftableItems: -> removeUncraftableItems: ->
toRemove = [] toRemove = []
@want.each (stack)=> @want.each (stack)=>
item = @modPack.findItem stack.slug item = @modPack.findItem stack.itemSlug
if not item? then toRemove.push stack.slug if not item? then toRemove.push stack.itemSlug
for slug in toRemove for itemSlug in toRemove
@want.remove slug @want.remove itemSlug
# Event Methods ################################################################################ # Event Methods ################################################################################
@@ -98,16 +98,16 @@ module.exports = class CraftingPlan extends BaseModel
# Private Methods ############################################################################## # Private Methods ##############################################################################
_addStep: (recipe)-> _addStep: (recipe)->
logger.verbose -> "adding step: #{recipe.slug}" logger.verbose -> "adding step: #{recipe.itemSlug}"
@steps[recipe.slug] = recipe:recipe @steps[recipe.itemSlug] = recipe:recipe
_chooseRecipe: (item)-> _chooseRecipe: (item)->
recipes = @modPack.findRecipes item.qualifiedSlug recipes = @modPack.findRecipes item.slug
return null unless recipes? return null unless recipes?
return recipes[0] return recipes[0]
_findSteps: (slug)-> _findSteps: (itemSlug)->
item = @modPack.findItem slug item = @modPack.findItem itemSlug
return unless item? return unless item?
return unless item.isCraftable return unless item.isCraftable
return if item.isGatherable return if item.isGatherable
@@ -116,26 +116,26 @@ module.exports = class CraftingPlan extends BaseModel
if @includingTools if @includingTools
for toolStack in recipe.tools for toolStack in recipe.tools
if not @_hasStep toolStack.slug if not @_hasStep toolStack.itemSlug
@_findSteps toolStack.slug @_findSteps toolStack.itemSlug
return if @_hasStep item.qualifiedSlug return if @_hasStep item.slug
@_reservedSteps[item.qualifiedSlug] = recipe @_reservedSteps[item.slug] = recipe
for inputStack in recipe.input for inputStack in recipe.input
@_findSteps inputStack.slug @_findSteps inputStack.itemSlug
@_addStep recipe @_addStep recipe
_hasStep: (slug)-> _hasStep: (itemSlug)->
return true if @steps[slug]? return true if @steps[itemSlug]?
return true if @_reservedSteps[slug]? return true if @_reservedSteps[itemSlug]?
return false return false
_qualifyItemSlug: (slug)-> _qualifyItemSlug: (itemSlug)->
item = @modPack.findItem slug item = @modPack.findItem itemSlug
return item.qualifiedSlug if item? return item.slug if item?
return slug return itemSlug
_removeExtraSteps: -> _removeExtraSteps: ->
result = (step for step in @steps when step.multiplier > 0) result = (step for step in @steps when step.multiplier > 0)
@@ -146,31 +146,31 @@ module.exports = class CraftingPlan extends BaseModel
step = @steps[i] step = @steps[i]
recipe = step.recipe 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 if @includingTools
for stack in recipe.tools for stack in recipe.tools
slug = @_qualifyItemSlug stack.slug itemSlug = @_qualifyItemSlug stack.itemSlug
available = @result.quantityOf(slug) + @need.quantityOf(slug) available = @result.quantityOf(itemSlug) + @need.quantityOf(itemSlug)
needed = Math.max 0, stack.quantity - available needed = Math.max 0, stack.quantity - available
@need.add slug, needed @need.add itemSlug, needed
@result.add slug, needed @result.add itemSlug, needed
for stack in recipe.input for stack in recipe.input
slug = @_qualifyItemSlug stack.slug itemSlug = @_qualifyItemSlug stack.itemSlug
needed = step.multiplier * stack.quantity needed = step.multiplier * stack.quantity
consumed = Math.min needed, @result.quantityOf slug consumed = Math.min needed, @result.quantityOf itemSlug
remaining = needed - consumed remaining = needed - consumed
@result.remove slug, consumed @result.remove itemSlug, consumed
@need.add slug, remaining @need.add itemSlug, remaining
for stack in recipe.output for stack in recipe.output
slug = @_qualifyItemSlug stack.slug itemSlug = @_qualifyItemSlug stack.itemSlug
created = stack.quantity * step.multiplier created = stack.quantity * step.multiplier
consumed = Math.min created, @need.quantityOf slug consumed = Math.min created, @need.quantityOf itemSlug
remaining = created - consumed remaining = created - consumed
@result.add slug, remaining @result.add itemSlug, remaining
@need.remove slug, consumed @need.remove itemSlug, consumed
+87 -60
View File
@@ -7,6 +7,7 @@ All rights reserved.
BaseModel = require './base_model' BaseModel = require './base_model'
{Event} = require '../constants' {Event} = require '../constants'
ItemSlug = require './item_slug'
{RequiredMods} = require '../constants' {RequiredMods} = require '../constants'
Stack = require './stack' Stack = require './stack'
@@ -16,28 +17,35 @@ module.exports = class Inventory extends BaseModel
constructor: (attributes={}, options={})-> constructor: (attributes={}, options={})->
super attributes, options super attributes, options
attributes.modPack ?= null
@clear() @clear()
Object.defineProperties this, Object.defineProperties this,
isEmpty: { get:-> @_slugs.length is 0 } isEmpty: { get:-> @_itemSlugs.length is 0 }
# Class Methods ################################################################################
@Delimiters =
Item: '.'
Stack: ':'
# Public Methods ############################################################################### # Public Methods ###############################################################################
add: (slug, quantity=1)-> add: (itemSlug, quantity=1)->
@_add slug, quantity @_add itemSlug, quantity
@trigger Event.add, this, slug, quantity @trigger Event.add, this, itemSlug, quantity
@trigger Event.change, this @trigger Event.change, this
return this return this
addInventory: (inventory)-> addInventory: (inventory)->
inventory.each (stack)=> @_add stack.slug, stack.quantity inventory.each (stack)=> @_add stack.itemSlug, stack.quantity
@trigger Event.change, this @trigger Event.change, this
return this return this
clear: (options={})-> clear: (options={})->
@_stacks = {} @_stacks = {}
@_slugs = [] @_itemSlugs = []
@trigger Event.change, this @trigger Event.change, this
@@ -47,78 +55,110 @@ module.exports = class Inventory extends BaseModel
return inventory return inventory
each: (callback)-> each: (callback)->
for slug in @_slugs for itemSlug in @_itemSlugs
callback @_stacks[slug] callback @_stacks[itemSlug]
getSlugs: -> getSlugs: ->
return @_slugs[..] return @_itemSlugs[..]
hasAtLeast: (slug, quantity=1)-> hasAtLeast: (itemSlug, quantity=1)->
if quantity is 0 then return true if quantity is 0 then return true
stack = @_stacks[slug] stack = @_stacks[itemSlug]
return false unless stack? return false unless stack?
return stack.quantity >= quantity return stack.quantity >= quantity
localizeTo: (modPack)-> localize: ->
if not @modPack? then throw new Error 'localize requires @modPack'
newSlugs = [] newSlugs = []
for slug in @_slugs for itemSlug in @_itemSlugs
stack = @_stacks[slug] stack = @_stacks[itemSlug]
qualifiedSlug = modPack.findItem(slug)?.qualifiedSlug
qualifiedSlug = @modPack.findItem(itemSlug)?.slug
if qualifiedSlug? if qualifiedSlug?
delete @_stacks[slug] delete @_stacks[itemSlug]
newSlugs.push qualifiedSlug newSlugs.push qualifiedSlug
@_stacks[qualifiedSlug] = stack @_stacks[qualifiedSlug] = stack
stack.slug = qualifiedSlug stack.itemSlug = qualifiedSlug
else else
throw new Error "could not find an item for: #{slug}" newSlugs.push itemSlug
@_slugs = newSlugs @_itemSlugs = newSlugs
@_sort() @_sort()
pop: -> pop: ->
slug = @_slugs.pop() itemSlug = @_itemSlugs.pop()
return null unless slug? return null unless itemSlug?
stack = @_stacks[slug] stack = @_stacks[itemSlug]
delete @_stacks[slug] delete @_stacks[itemSlug]
@trigger Event.remove, this, stack.slug, stack.quantity @trigger Event.remove, this, stack.itemSlug, stack.quantity
@trigger Event.change, this @trigger Event.change, this
return stack return stack
quantityOf: (slug)-> quantityOf: (itemSlug)->
stack = @_stacks[slug] stack = @_stacks[itemSlug]
return 0 unless stack? return 0 unless stack?
return stack.quantity return stack.quantity
remove: (slug, quantity=null)-> remove: (itemSlug, quantity=null)->
return if quantity is 0 return if quantity is 0
stack = @_stacks[slug] stack = @_stacks[itemSlug]
if not stack? then throw new Error "cannot remove #{slug} since it is not in this inventory" if not stack? then throw new Error "cannot remove #{itemSlug} since it is not in this inventory"
quantity ?= stack.quantity quantity ?= stack.quantity
if stack.quantity < 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 stack.quantity -= quantity
if stack.quantity is 0 if stack.quantity is 0
delete @_stacks[slug] delete @_stacks[itemSlug]
@_slugs = _(@_slugs).without slug @_itemSlugs = _(@_itemSlugs).without itemSlug
@trigger Event.remove, this, slug, quantity @trigger Event.remove, this, itemSlug, quantity
@trigger Event.change, this @trigger Event.change, this
return this return this
toList: -> # Parsing Methods ##############################################################################
result = []
@each (stack)-> parse: (data)->
if stack.quantity > 1 return this if not data? or data.length is 0
result.push [stack.quantity, stack.slug]
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 else
result.push stack.slug throw new Error "expected #{stackText} to have 0 or 1 parts"
return result
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 ############################################################################# # Object Overrides #############################################################################
@@ -137,31 +177,18 @@ module.exports = class Inventory extends BaseModel
# Private Methods ############################################################################## # Private Methods ##############################################################################
_add: (slug, quantity=1)-> _add: (itemSlug, quantity=1)->
return unless slug? return unless itemSlug?
return if quantity is 0 return if quantity is 0
stack = @_stacks[slug] stack = @_stacks[itemSlug]
if not stack? if not stack?
stack = new Stack slug:slug, quantity:quantity stack = new Stack itemSlug:itemSlug, quantity:quantity
@_stacks[slug] = stack @_stacks[itemSlug] = stack
@_slugs.push slug @_itemSlugs.push itemSlug
@_sort() @_sort()
else else
stack.quantity += quantity stack.quantity += quantity
_sort: -> _sort: ->
@_slugs.sort (a, b)-> @_itemSlugs.sort (a, b)-> ItemSlug.compare 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
@@ -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
+7 -17
View File
@@ -5,9 +5,9 @@ Copyright (c) 2014-2015 by Redwood Labs
All rights reserved. All rights reserved.
### ###
BaseCollection = require './base_collection'
BaseModel = require './base_model' BaseModel = require './base_model'
{Event} = require '../constants' {Event} = require '../constants'
ItemSlug = require './item_slug'
Recipe = require './recipe' Recipe = require './recipe'
StringBuilder = require './string_builder' StringBuilder = require './string_builder'
@@ -23,16 +23,12 @@ module.exports = class Item extends BaseModel
attributes.group ?= Item.Group.Other attributes.group ?= Item.Group.Other
attributes.isGatherable ?= false attributes.isGatherable ?= false
attributes.modVersion ?= null attributes.modVersion ?= null
attributes.slug ?= _.slugify attributes.name attributes.slug ?= ItemSlug.slugify attributes.name
options.logEvents ?= false options.logEvents ?= false
super attributes, options super attributes, options
Object.defineProperties this, @on Event.change + ':modVersion', => @slug.mod = @modVersion?.modSlug
isCraftable: {get:@getIsCraftable}
qualifiedSlug: {get:@getQualifiedSlug}
@on Event.change + ':modVersion', => @_qualifiedSlug = null
# Public Methods ############################################################################### # Public Methods ###############################################################################
@@ -47,15 +43,10 @@ module.exports = class Item extends BaseModel
getIsCraftable: -> getIsCraftable: ->
return false unless @modVersion? return false unless @modVersion?
return @modVersion.hasRecipes @qualifiedSlug return @modVersion.hasRecipes @slug
getQualifiedSlug: -> Object.defineProperties @prototype,
return @slug if not @modVersion? isCraftable: {get:@prototype.getIsCraftable}
if not @_qualifiedSlug?
@_qualifiedSlug = _.composeSlugs @modVersion.modSlug, @slug
return @_qualifiedSlug
# Object Overrides ############################################################################# # Object Overrides #############################################################################
@@ -68,7 +59,6 @@ module.exports = class Item extends BaseModel
.push 'isGatherable:', @isGatherable, ', ' .push 'isGatherable:', @isGatherable, ', '
.onlyIf (@group isnt Item.Group.Other), (b)=> .onlyIf (@group isnt Item.Group.Other), (b)=>
b.push 'group:"', @group, '", ' b.push 'group:"', @group, '", '
.onlyIf (_.slugify(@name) isnt @slug), (b)=> .push 'slug:"', @slug, '", '
b.push 'slug:"', @slug, '", '
.push '}' .push '}'
.toString() .toString()
+3 -6
View File
@@ -31,14 +31,11 @@ module.exports = class ItemPage extends BaseModel
findComponentInItems: -> findComponentInItems: ->
return null unless @item? return null unless @item?
slugs = [@item.slug, @item.qualifiedSlug]
result = {} result = {}
@modPack.eachMod (mod)-> @modPack.eachMod (mod)->
mod.eachItem (item)-> mod.eachRecipe (recipe)->
item.eachRecipe (recipe)-> if recipe.produces @item.slug
for stack in recipe.input result[item.slug] = item
if stack.slug in slugs
result[item.qualifiedSlug] = item
result = _.values(result).sort (a, b)-> a.compareTo b result = _.values(result).sort (a, b)-> a.compareTo b
return null unless result.length > 0 return null unless result.length > 0
@@ -5,12 +5,15 @@ Copyright (c) 2015 by Redwood Labs
All rights reserved. All rights reserved.
### ###
{RequiredMods} = require '../constants'
######################################################################################################################## ########################################################################################################################
module.exports = class ItemSlug module.exports = class ItemSlug
constructor: -> constructor: ->
@_item = @_mod = null @_item = @_mod = null
if arguments.length is 1 if arguments.length is 1
@item = arguments[0] @item = arguments[0]
else if arguments.length is 2 else if arguments.length is 2
@@ -21,10 +24,14 @@ module.exports = class ItemSlug
# Class Methods ################################################################################ # Class Methods ################################################################################
DELIMITER = '__'
@compare: (a, b)-> @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 a.isQualified
return +1 if b.isQualified return +1 if b.isQualified
else if a.mod isnt b.mod else if a.mod isnt b.mod
@@ -34,18 +41,36 @@ module.exports = class ItemSlug
return 0 return 0
@equals: (a, b)-> @equal: (a, b)->
return false unless a.mod is b.mod return false unless a.mod is b.mod
return false unless a.item is b.item return false unless a.item is b.item
return true 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 ############################################################################# # Property Methods #############################################################################
Object.defineProperties @prototype, getIsQualified: ->
isQualified: { get:@prototype.isQualified } return @_mod?
mod: { get:@prototype.getMod, set:@prototype.setMod }
item: { get:@prototype.getItem, set:@prototype.setItem }
qualified: { get:@prototype.getQualified set:@prototype.setQualified }
getItem: -> getItem: ->
return @_item return @_item
@@ -53,17 +78,24 @@ module.exports = class ItemSlug
setItem: (item)-> setItem: (item)->
if not item? then throw new Error 'item is required' if not item? then throw new Error 'item is required'
@_item = item @_item = item
@mod = mod
@mod = @mod # reset @_qualified
getMod: -> getMod: ->
return @_mod return @_mod
setMod: (mod)-> setMod: (mod)->
@_qualified = if mod? then "#{@mod}#{ItemSlug.DELIMITER}#{@item}" else @item
@_mod = mod @_mod = mod
@_qualified = if @_mod? then _.composeSlugs(@_mod, @_item) else @_item
isQualified: -> getQualified: ->
return @_mod? 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 ############################################################################# # Object Overrides #############################################################################
+5 -5
View File
@@ -87,13 +87,13 @@ module.exports = class Mod extends BaseModel
return unless @_activeModVersion? return unless @_activeModVersion?
@_activeModVersion.findItemByName name @_activeModVersion.findItemByName name
findName: (slug)-> findName: (itemSlug)->
return unless @_activeModVersion? return unless @_activeModVersion?
@_activeModVersion.findName slug @_activeModVersion.findName itemSlug
findRecipes: (slug, result=[])-> findRecipes: (itemSlug, result=[])->
return result unless @_activeModVersion? return result unless @_activeModVersion?
@_activeModVersion.findRecipes slug, result @_activeModVersion.findRecipes itemSlug, result
# Property Methods ############################################################################# # Property Methods #############################################################################
@@ -123,6 +123,7 @@ module.exports = class Mod extends BaseModel
for modVersion in @_modVersions for modVersion in @_modVersions
return modVersion if modVersion.version is version return modVersion if modVersion.version is version
return null return null
getActiveVersion: -> getActiveVersion: ->
@@ -150,7 +151,6 @@ module.exports = class Mod extends BaseModel
@trigger Event.change + ':activeVersion', this, @_activeVersion @trigger Event.change + ':activeVersion', this, @_activeVersion
@trigger Event.change, this @trigger Event.change, this
# Backbone.View Overrides ###################################################################### # Backbone.View Overrides ######################################################################
parse: (text)-> parse: (text)->
+20 -34
View File
@@ -22,12 +22,11 @@ module.exports = class ModPack extends BaseModel
# Public Methods ############################################################################### # Public Methods ###############################################################################
findItem: (slug, options={})-> findItem: (itemSlug, options={})->
options.includeDisabled ?= false options.includeDisabled ?= false
[modSlug, itemSlug] = _.decomposeSlug slug if itemSlug.isQualified
if modSlug? mod = @getMod itemSlug.mod
mod = @getMod modSlug
if mod? if mod?
item = mod.findItem itemSlug, options item = mod.findItem itemSlug, options
return item if item? return item if item?
@@ -50,63 +49,50 @@ module.exports = class ModPack extends BaseModel
return null return null
findItemDisplay: (slug)-> findItemDisplay: (itemSlug)->
if not slug? then return null if not itemSlug? then return null
result = {} result = {}
item = @findItem slug, includeDisabled:true item = @findItem itemSlug, includeDisabled:true
if item? if item?
result.modSlug = item.modVersion.modSlug result.modSlug = item.slug.mod
result.modVersion = item.modVersion.version result.modVersion = item.modVersion.version
result.slug = item.slug result.itemSlug = item.slug.item
result.itemName = item.name result.itemName = item.name
else else
result.modSlug = @_mods[0].slug result.modSlug = @_mods[0].slug
result.modVersion = @_mods[0].activeVersion result.modVersion = @_mods[0].activeVersion
result.slug = slug result.itemSlug = itemSlug.item
result.itemName = @findName slug, includeDisabled:true 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.iconUrl = Url.itemIcon result
result.itemUrl = Url.item result result.itemUrl = Url.item result
return result return result
findName: (slug)-> findName: (slug, options={})->
options.includeDisabled ?= false
for mod in @_mods for mod in @_mods
continue unless mod.enabled continue unless mod.enabled or options.includeDisabled
name = mod.findName slug name = mod.findName slug
return name if name return name if name
return null return null
findRecipes: (slug, result=[])-> findRecipes: (itemSlug, result=[])->
[modSlug, itemSlug] = _.decomposeSlug slug if itemSlug.isQualified
mod = @getMod itemSlug.mod
if modSlug?
mod = @getMod modSlug
if mod? if mod?
mod.findRecipes slug, result mod.findRecipes itemSlug, result
return result if result.length > 0 return result if result.length > 0
for mod in @_mods for mod in @_mods
continue unless mod.enabled continue unless mod.enabled
mod.findRecipes slug, result mod.findRecipes itemSlug, result
return if result.length > 0 then result else null 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 ############################################################################# # Property Methods #############################################################################
addMod: (mod)-> addMod: (mod)->
+39 -38
View File
@@ -5,10 +5,10 @@ Copyright (c) 2014-2015 by Redwood Labs
All rights reserved. All rights reserved.
### ###
BaseCollection = require './base_collection'
BaseModel = require './base_model' BaseModel = require './base_model'
{Event} = require '../constants' {Event} = require '../constants'
Item = require './item' Item = require './item'
ItemSlug = require './item_slug'
{RequiredMods} = require '../constants' {RequiredMods} = require '../constants'
{Url} = require '../constants' {Url} = require '../constants'
@@ -39,16 +39,19 @@ module.exports = class ModVersion extends BaseModel
return 0 return 0
sort: ->
@_slugs.sort (a, b)-> ItemSlug.compare a, b
# Item Methods ################################################################################# # Item Methods #################################################################################
addItem: (item)-> addItem: (item)->
if @_items[item.slug]? then throw new Error "duplicate item for #{item.name}" if @findItem(item.slug)? then throw new Error "duplicate item for #{item.name}"
@_items[item.slug] = item
@_groups[item.group] ?= {}
@_groups[item.group][item.slug] = item
item.modVersion = this item.modVersion = this
@_items[item.slug.item] = item
@_groups[item.group] ?= {}
@_groups[item.group][item.slug.item] = item
@registerName item.slug, item.name @registerName item.slug, item.name
return this return this
@@ -60,20 +63,21 @@ module.exports = class ModVersion extends BaseModel
eachItem: (callback)-> eachItem: (callback)->
for slug in @_slugs for slug in @_slugs
item = @_items[slug] item = @findItem slug
continue unless item? continue unless item?
callback @_items[slug], slug callback item
return this return this
eachItemInGroup: (group, callback)-> eachItemInGroup: (group, callback)->
group = @_groups[group] itemMap = @_groups[group]
return unless group? return unless itemMap?
for slug in _.keys(group).sort() items = _.values(itemMap).sort (a, b)-> a.compareTo b
callback group[slug] for item in items
callback item
findItem: (itemSlug)-> findItem: (itemSlug)->
return @_items[itemSlug] return @_items[itemSlug.item]
findItemByName: (name)-> findItemByName: (name)->
for itemSlug, item of @_items for itemSlug, item of @_items
@@ -97,22 +101,16 @@ module.exports = class ModVersion extends BaseModel
eachName: (callback)-> eachName: (callback)->
for slug in @_slugs for slug in @_slugs
callback @_names[slug], slug callback @_names[slug.item]
return this return this
findName: (slug)-> findName: (itemSlug)->
[modSlug, itemSlug] = _.decomposeSlug slug return @_names[itemSlug.item]
return @_names[itemSlug]
registerName: (slug, name)->
hasSlug = @_names[slug]?
@_names[slug] = name
if not hasSlug
@_slugs.push slug
@_slugs.sort()
@_slugs = _.uniq @_slugs, true
registerName: (itemSlug, name)->
return if @_names[itemSlug.item]
@_names[itemSlug.item] = name
@_slugs.push itemSlug
return this return this
# Recipe Methods ############################################################################### # Recipe Methods ###############################################################################
@@ -121,15 +119,15 @@ module.exports = class ModVersion extends BaseModel
recipe.modVersion = this recipe.modVersion = this
for stack in recipe.output for stack in recipe.output
recipeList = @_recipes[stack.slug] recipeList = @_recipes[stack.itemSlug.item]
if not recipeList? if not recipeList?
@_recipes[stack.slug] = recipeList = [] recipeList = @_recipes[stack.itemSlug.item] = []
recipeList.push recipe recipeList.push recipe
return this return this
findRecipes: (slug, result=[])-> findRecipes: (itemSlug, result=[])->
recipeList = @_recipes[slug] recipeList = @_recipes[itemSlug.item]
if recipeList? if recipeList?
for recipe in recipeList for recipe in recipeList
result.push recipe result.push recipe
@@ -138,16 +136,19 @@ module.exports = class ModVersion extends BaseModel
findExternalRecipes: -> findExternalRecipes: ->
result = {} result = {}
for slug, recipeList of @_recipes for itemSlug in @_slugs
[modSlug, itemSlug] = _.decomposeSlug slug continue if itemSlug.isQualified
logger.debug "checking: #{modSlug}, #{itemSlug}" recipes = @_recipes[itemSlug.item]
continue if @_items[itemSlug]? continue unless recipes? and recipes.length > 0
logger.debug "external: #{recipeList}"
result[itemSlug] = recipeList[..] resultList = result[itemSlug] = []
for recipe in recipes
resultList.push recipe
return result return result
hasRecipes: (itemSlug)-> hasRecipes: (itemSlug)->
recipeList = @_recipes[itemSlug] recipeList = @_recipes[itemSlug.item]
return true if recipeList? and recipeList.length > 0 return true if recipeList? and recipeList.length > 0
return false return false
@@ -167,5 +168,5 @@ module.exports = class ModVersion extends BaseModel
toString: -> toString: ->
return "ModVersion (#{@cid}) { return "ModVersion (#{@cid}) {
modSlug:#{@modSlug}, version:#{@version}, items:#{_.keys(@_items).length} items modSlug:#{@modSlug}, version:#{@version}, items:«#{@_slugs.length} items»
}" }"
@@ -7,6 +7,7 @@ All rights reserved.
CommandParserVersionBase = require './command_parser_version_base' CommandParserVersionBase = require './command_parser_version_base'
Item = require '../item' Item = require '../item'
ItemSlug = require '../item_slug'
ModVersion = require '../mod_version' ModVersion = require '../mod_version'
Recipe = require '../recipe' Recipe = require '../recipe'
Stack = require '../stack' Stack = require '../stack'
@@ -124,6 +125,7 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase
for itemName, itemData of modVersionData.items for itemName, itemData of modVersionData.items
@_handleErrors @_buildItem, modVersion, itemData @_handleErrors @_buildItem, modVersion, itemData
modVersion.sort()
return modVersion return modVersion
_buildItem: (modVersion, itemData)-> _buildItem: (modVersion, itemData)->
@@ -134,6 +136,10 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase
if itemData.type is 'new' if itemData.type is 'new'
item = new Item name:itemData.name, isGatherable:itemData.gatherable, group:itemData.group item = new Item name:itemData.name, isGatherable:itemData.gatherable, group:itemData.group
modVersion.addItem item 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 for recipeData in itemData.recipes
@_handleErrors @_buildRecipe, modVersion, itemData, recipeData @_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.input? then throw new Error 'the "input" declaration is required'
if not recipeData.pattern? then throw new Error 'the "pattern" declaration is required' if not recipeData.pattern? then throw new Error 'the "pattern" declaration is required'
itemSlug = _.slugify itemData.name createSlug = (name)=>
modVersion.registerName itemSlug, itemData.name item = @_rawData.items[name]
if itemData.type is 'new' then itemSlug = _.composeSlugs modVersion.modSlug, itemSlug if item?
slug = new ItemSlug modVersion.modSlug, _.slugify name
qualifySlug = (name, slug)=> else
return slug unless itemData.type is 'new' slug = new ItemSlug _.slugify name
return slug unless @_rawData.items[name]? modVersion.registerName slug, name
return _.composeSlugs modVersion.modSlug, slug return slug
recipeData.quantity ?= 1 recipeData.quantity ?= 1
recipeData.extras ?= [] recipeData.extras ?= []
@@ -160,10 +166,8 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase
inputStacks = [] inputStacks = []
for name in recipeData.input for name in recipeData.input
slug = _.slugify name inputSlug = createSlug name
modVersion.registerName slug, name inputStacks.push new Stack itemSlug:inputSlug, quantity:0
slug = qualifySlug name, slug
inputStacks.push new Stack slug:slug, quantity:0
for c in recipeData.pattern for c in recipeData.pattern
continue if c is '.' continue if c is '.'
@@ -175,23 +179,18 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase
for i in [0...inputStacks.length] for i in [0...inputStacks.length]
stack = inputStacks[i] stack = inputStacks[i]
if stack.quantity is 0 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" throw new Error "#{name} is an input for this recipe, but it is not in the pattern"
outputStacks = [ new Stack itemSlug:itemData.slug, quantity:recipeData.quantity ]
outputStacks = [ new Stack slug:itemSlug, quantity:recipeData.quantity ]
for extraData in recipeData.extras for extraData in recipeData.extras
slug = _.slugify extraData.name outputSlug = createSlug extraData.name
modVersion.registerName slug, extraData.name outputStacks.push new Stack itemSlug:outputSlug, quantity:extraData.quantity
slug = qualifySlug extraData.name, slug
outputStacks.push new Stack slug:slug, quantity:extraData.quantity
toolStacks = [] toolStacks = []
for name in recipeData.tools for name in recipeData.tools
slug = _.slugify name toolSlug = createSlug name
modVersion.registerName slug, name toolStacks.push new Stack itemSlug:toolSlug, quantity:1
slug = qualifySlug name, slug
toolStacks.push new Stack slug:slug, quantity:1
recipe = new Recipe recipe = new Recipe
input: inputStacks input: inputStacks
@@ -200,7 +199,6 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase
tools: toolStacks tools: toolStacks
modVersion.addRecipe recipe modVersion.addRecipe recipe
logger.debug "adding recipe: #{recipe}"
return recipe return recipe
# Un-parsing Methods ########################################################################### # Un-parsing Methods ###########################################################################
@@ -213,9 +211,9 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase
modVersion.eachGroup (group)=> modVersion.eachGroup (group)=>
@_unparseGroup builder, modVersion, group @_unparseGroup builder, modVersion, group
for itemSlug, recipeList of modVersion.findExternalRecipes() for itemSlugText, recipeList of modVersion.findExternalRecipes()
builder builder
.line 'update: ', modVersion.findName itemSlug .line 'update: ', modVersion.findName ItemSlug.slugify(itemSlugText)
.indent() .indent()
.loop(recipeList, delimiter:'\n\n', onEach:(b, r)=> @_unparseRecipe(b, r)) .loop(recipeList, delimiter:'\n\n', onEach:(b, r)=> @_unparseRecipe(b, r))
.outdent() .outdent()
@@ -235,7 +233,7 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase
builder.outdent() builder.outdent()
_unparseItem: (builder, modVersion, item)-> _unparseItem: (builder, modVersion, item)->
recipes = modVersion.findRecipes item.qualifiedSlug recipes = modVersion.findRecipes item.slug
builder builder
.line 'item: ', item.name .line 'item: ', item.name
@@ -246,13 +244,13 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase
.outdent() .outdent()
_unparseRecipe: (builder, recipe)-> _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() inputNames.sort()
patternMap = {'.', '.'} patternMap = {'.', '.'}
for i in [0...recipe.input.length] for i in [0...recipe.input.length]
stack = recipe.input[i] 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 pattern = recipe.pattern or recipe.defaultPattern
newPattern = [] newPattern = []
@@ -288,9 +286,9 @@ module.exports = class ModVersionParserV1 extends CommandParserVersionBase
_unparseStackList: (builder, stackList)-> _unparseStackList: (builder, stackList)->
if stackList.length is 1 and stackList[0].quantity is 1 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 else
builder.loop stackList, onEach:(b, stack)=> builder.loop stackList, onEach:(b, stack)=>
builder builder
.onlyIf stack.quantity > 1, => builder.push stack.quantity, ' ' .onlyIf stack.quantity > 1, => builder.push stack.quantity, ' '
.push builder.context.findName stack.slug .push builder.context.findName stack.itemSlug
+13 -6
View File
@@ -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.input? then throw new Error 'attributes.input is required'
if not attributes.pattern? then throw new Error 'attributes.pattern is required' if not attributes.pattern? then throw new Error 'attributes.pattern is required'
if attributes.slug? and not attributes.output? if attributes.itemSlug? and not attributes.output?
attributes.output = [new Stack slug:attributes.slug, quantity:1] attributes.output = [new Stack itemSlug:attributes.itemSlug, quantity:1]
else if attributes.output? and not attributes.slug? else if attributes.output? and not attributes.itemSlug?
if attributes.output.length is 0 then throw new Error 'attributes.output cannot be empty' 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 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 attributes.pattern = @_parsePattern attributes.pattern
@@ -42,7 +42,14 @@ module.exports = class Recipe extends BaseModel
stack = @input[parseInt(patternDigit)] stack = @input[parseInt(patternDigit)]
return null unless stack? 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 ############################################################################# # Object Overrides #############################################################################
+2 -2
View File
@@ -12,7 +12,7 @@ BaseModel = require './base_model'
module.exports = class Stack extends BaseModel module.exports = class Stack extends BaseModel
constructor: (attributes={}, options={})-> 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 attributes.quantity ?= 1
options.logEvents ?= false options.logEvents ?= false
super attributes, options super attributes, options
@@ -20,4 +20,4 @@ module.exports = class Stack extends BaseModel
# Object Overrides ############################################################################# # Object Overrides #############################################################################
toString: -> toString: ->
return "#{@quantity} #{@slug}" return "#{@quantity} #{@itemSlug}"
+20 -30
View File
@@ -6,6 +6,7 @@ All rights reserved.
### ###
CraftingPlan = require '../src/scripts/models/crafting_plan' CraftingPlan = require '../src/scripts/models/crafting_plan'
ItemSlug = require '../src/scripts/models/item_slug'
Mod = require '../src/scripts/models/mod' Mod = require '../src/scripts/models/mod'
ModPack = require '../src/scripts/models/mod_pack' ModPack = require '../src/scripts/models/mod_pack'
ModVersion = require '../src/scripts/models/mod_version' ModVersion = require '../src/scripts/models/mod_version'
@@ -41,54 +42,43 @@ describe 'crafting_plan.coffee', ->
describe 'under the simplest conditions', -> describe 'under the simplest conditions', ->
it 'can craft a single step recipe', -> it 'can craft a single step recipe', ->
plan.want.add 'oak_plank' plan.want.add ItemSlug.slugify 'oak_plank'
plan.craft() plan.craft()
plan.need.toList().should.eql ['oak_log'] plan.need.unparse().should.equal 'oak_log'
plan.result.toList().should.eql [[4, 'minecraft__oak_plank']] plan.result.unparse().should.equal '4.minecraft__oak_plank'
it 'can craft a multi-step recipe', -> it 'can craft a multi-step recipe', ->
plan.want.add 'crafting_table' plan.want.add ItemSlug.slugify 'crafting_table'
plan.craft() plan.craft()
plan.need.toList().should.eql ['oak_log'] plan.need.unparse().should.equal 'oak_log'
plan.result.toList().should.eql ['minecraft__crafting_table'] plan.result.unparse().should.equal 'minecraft__crafting_table'
it 'can craft a multi-step recipe using tools', -> it 'can craft a multi-step recipe using tools', ->
plan.want.add 'furnace' plan.want.add ItemSlug.slugify 'furnace'
plan.craft() plan.craft()
plan.need.toList().should.eql [[8, 'cobblestone']] plan.need.unparse().should.equal '8.cobblestone'
plan.result.toList().should.eql ['minecraft__furnace'] plan.result.unparse().should.equal 'minecraft__furnace'
it 'can craft a multi-step recipe re-using tools', -> 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.craft()
plan.need.toList().should.eql [[2, 'furnace_fuel'], [2, 'iron_ore'], 'oak_log'] plan.need.unparse().should.equal '2.furnace_fuel:2.iron_ore:oak_log'
plan.result.toList().should.eql [ plan.result.unparse().should.equal 'minecraft__iron_sword:2.minecraft__oak_plank:3.minecraft__stick'
'minecraft__iron_sword',
[2, 'minecraft__oak_plank'],
[3, 'minecraft__stick']
]
describe 'with building tools', -> describe 'with building tools', ->
it 'can craft a multi-step recipe using tools', -> it 'can craft a multi-step recipe using tools', ->
plan.includingTools = true plan.includingTools = true
plan.want.add 'furnace' plan.want.add ItemSlug.slugify 'furnace'
plan.craft() plan.craft()
plan.need.toList().should.eql [[8, 'cobblestone'], 'oak_log'] plan.need.unparse().should.equal '8.cobblestone:oak_log'
plan.result.toList().should.eql ['minecraft__crafting_table', 'minecraft__furnace'] plan.result.unparse().should.equal 'minecraft__crafting_table:minecraft__furnace'
it 'can craft a multi-step recipe re-using tools', -> it 'can craft a multi-step recipe re-using tools', ->
plan.includingTools = true plan.includingTools = true
plan.want.add 'iron_sword' plan.want.add ItemSlug.slugify 'iron_sword'
plan.craft() plan.craft()
plan.need.toList().should.eql [ plan.need.unparse().should.eql '8.cobblestone:2.furnace_fuel:2.iron_ore:2.oak_log'
[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'
plan.result.toList().should.eql [
'minecraft__crafting_table',
'minecraft__furnace',
'minecraft__iron_sword',
[2, 'minecraft__oak_plank'],
[3, 'minecraft__stick']
]
+71 -42
View File
@@ -9,6 +9,7 @@
EventRecorder = require './event_recorder' EventRecorder = require './event_recorder'
Inventory = require '../src/scripts/models/inventory' Inventory = require '../src/scripts/models/inventory'
Item = require '../src/scripts/models/item' Item = require '../src/scripts/models/item'
ItemSlug = require '../src/scripts/models/item_slug'
######################################################################################################################## ########################################################################################################################
@@ -20,30 +21,30 @@ describe 'inventory.coffee', ->
beforeEach -> beforeEach ->
inventory = new Inventory {}, silent:false inventory = new Inventory {}, silent:false
inventory.add 'wool', 4 inventory.add ItemSlug.slugify('wool'), 4
inventory.add 'string', 20 inventory.add ItemSlug.slugify('string'), 20
inventory.add 'boat' inventory.add ItemSlug.slugify('boat')
describe 'add', -> describe 'add', ->
it 'can add to an empty inventory', -> 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 = inventory._stacks['iron_ingot']
stack.constructor.name.should.equal 'Stack' stack.constructor.name.should.equal 'Stack'
stack.slug.should.equal 'iron_ingot' stack.itemSlug.qualified.should.equal 'iron_ingot'
stack.quantity.should.equal 4 stack.quantity.should.equal 4
it 'can augment quantity of existing items', -> it 'can augment quantity of existing items', ->
inventory.add 'wool', 2 inventory.add ItemSlug.slugify('wool'), 2
inventory.toList().should.eql ['boat', [20, 'string'], [6, 'wool']] inventory.unparse().should.equal 'boat:20.string:6.wool'
it 'can add zero quantity', -> it 'can add zero quantity', ->
inventory.add 'wool', 0 inventory.add ItemSlug.slugify('wool'), 0
inventory.toList().should.eql ['boat', [20, 'string'], [4, 'wool']] inventory.unparse().should.equal 'boat:20.string:4.wool'
it 'emits the proper events', -> it 'emits the proper events', ->
events = new EventRecorder inventory events = new EventRecorder inventory
inventory.add 'iron_ingot', 10 inventory.add ItemSlug.slugify('iron_ingot'), 10
events.names.should.eql [Event.add, Event.change] events.names.should.eql [Event.add, Event.change]
describe 'addInventory', -> describe 'addInventory', ->
@@ -51,24 +52,24 @@ describe 'inventory.coffee', ->
it 'can add to an empty inventory', -> it 'can add to an empty inventory', ->
newInventory = new Inventory newInventory = new Inventory
newInventory.addInventory 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', -> it 'can add a mix of new and existing items', ->
newInventory = new Inventory newInventory = new Inventory
newInventory.add 'string', 2 newInventory.add ItemSlug.slugify('string'), 2
newInventory.addInventory inventory newInventory.addInventory inventory
newInventory.toList().should.eql ['boat', [22, 'string'], [4, 'wool']] newInventory.unparse().should.equal 'boat:22.string:4.wool'
describe 'clone', -> describe 'clone', ->
it 'creates an empty inventory from an empty inventory', -> it 'creates an empty inventory from an empty inventory', ->
a = new Inventory a = new Inventory
b = a.clone() b = a.clone()
b._slugs.should.eql [] b._itemSlugs.should.eql []
it 'faithfully copies an existing inventory', -> it 'faithfully copies an existing inventory', ->
copy = inventory.clone() copy = inventory.clone()
copy.toList().should.eql ['boat', [20, 'string'], [4, 'wool']] copy.unparse().should.equal 'boat:20.string:4.wool'
describe 'each', -> describe 'each', ->
@@ -80,16 +81,16 @@ describe 'inventory.coffee', ->
it 'works when items have only been added', -> it 'works when items have only been added', ->
result = [] result = []
inventory.each (stack)-> result.push stack.slug inventory.each (stack)-> result.push stack.itemSlug.qualified
result.should.eql ['boat', 'string', 'wool'] result.should.eql ['boat', 'string', 'wool']
it 'works when items have been augmented', -> it 'works when items have been augmented', ->
inventory.add 'iron_ingot' inventory.add ItemSlug.slugify 'iron_ingot'
inventory.add 'boat' inventory.add ItemSlug.slugify 'boat'
inventory.add 'wool', 2 inventory.add ItemSlug.slugify('wool'), 2
result = [] result = []
inventory.each (stack)-> result.push stack.slug inventory.each (stack)-> result.push stack.itemSlug.qualified
result.should.eql ['boat', 'iron_ingot', 'string', 'wool'] result.should.eql ['boat', 'iron_ingot', 'string', 'wool']
describe 'hasAtLeast', -> describe 'hasAtLeast', ->
@@ -107,39 +108,67 @@ describe 'inventory.coffee', ->
inventory.hasAtLeast('wool', 4).should.be.true inventory.hasAtLeast('wool', 4).should.be.true
inventory.hasAtLeast('wool', 5).should.be.false inventory.hasAtLeast('wool', 5).should.be.false
describe 'localizeTo', -> describe 'localize', ->
before -> before ->
modPack = modPack =
map: modSlug:
wool: 'minecraft__wool' wool: 'minecraft'
string: 'minecraft__string' string: 'minecraft'
boat: 'minecraft__boat' boat: 'minecraft'
stone_gear: 'buildcraft__stone_gear' stone_gear: 'buildcraft'
findItem: (slug)-> findItem: (slug)->
[modSlug, itemSlug] = _.decomposeSlug slug return slug:new ItemSlug @modSlug[slug.item], slug.item
return slug:itemSlug, qualifiedSlug:@map[itemSlug]
it 'replaces item slugs with qualified slugs', -> it 'replaces item slugs with qualified slugs', ->
inventory.add 'stone_gear' inventory.add ItemSlug.slugify 'stone_gear'
inventory.localizeTo modPack inventory.modPack = modPack
inventory.toList().should.eql [ inventory.localize()
'minecraft__boat',
[20, 'minecraft__string'], slugs = []
[4, 'minecraft__wool'], inventory.each (stack)-> slugs.push stack.itemSlug.qualified
slugs.should.eql [
'minecraft__boat'
'minecraft__string'
'minecraft__wool'
'buildcraft__stone_gear' 'buildcraft__stone_gear'
] ]
it 'ignores qualified slugs', -> it 'ignores qualified slugs', ->
inventory.add 'buildcraft__stone_gear' inventory.add ItemSlug.slugify 'buildcraft__stone_gear'
inventory.localizeTo modPack inventory.modPack = modPack
inventory.toList().should.eql [ inventory.localize()
'minecraft__boat',
[20, 'minecraft__string'], slugs = []
[4, 'minecraft__wool'], inventory.each (stack)-> slugs.push stack.itemSlug.qualified
slugs.should.eql [
'minecraft__boat'
'minecraft__string'
'minecraft__wool'
'buildcraft__stone_gear' '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', -> describe 'pop', ->
it 'returns null for an empty inventory', -> it 'returns null for an empty inventory', ->
@@ -149,9 +178,9 @@ describe 'inventory.coffee', ->
it 'completely removes the last item', -> it 'completely removes the last item', ->
stack = inventory.pop() stack = inventory.pop()
stack.slug.should.equal 'wool' stack.itemSlug.qualified.should.equal 'wool'
stack.quantity.should.equal 4 stack.quantity.should.equal 4
inventory.toList().should.eql ['boat', [20, 'string']] inventory.unparse().should.equal 'boat:20.string'
it 'triggers the right events', -> it 'triggers the right events', ->
events = new EventRecorder inventory events = new EventRecorder inventory
-42
View File
@@ -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']]
+136
View File
@@ -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'
+10 -9
View File
@@ -6,6 +6,7 @@ All rights reserved.
### ###
Item = require '../src/scripts/models/item' Item = require '../src/scripts/models/item'
ItemSlug = require '../src/scripts/models/item_slug'
Mod = require '../src/scripts/models/mod' Mod = require '../src/scripts/models/mod'
ModPack = require '../src/scripts/models/mod_pack' ModPack = require '../src/scripts/models/mod_pack'
ModVersion = require '../src/scripts/models/mod_version' 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.addModVersion new ModVersion modSlug:minecraft.slug, version:'1.7.10'
minecraft.activeModVersion.addItem new Item name:'Wool' minecraft.activeModVersion.addItem new Item name:'Wool'
minecraft.activeModVersion.addItem new Item name:'Bed', recipes:[''] 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 = new Mod slug:'buildcraft', name:'Buildcraft'
buildcraft.addModVersion new ModVersion modSlug:buildcraft.slug, version:'6.2.6' buildcraft.addModVersion new ModVersion modSlug:buildcraft.slug, version:'6.2.6'
@@ -46,24 +47,24 @@ describe 'mod_pack.coffee', ->
describe 'findItem', -> describe 'findItem', ->
it 'can find an item by partial slug', -> it 'can find an item by partial slug', ->
item = modPack.findItem 'wool' item = modPack.findItem ItemSlug.slugify 'wool'
item.qualifiedSlug.should.equal 'minecraft__wool' item.slug.qualified.should.equal 'minecraft__wool'
it 'can find an item by full slug', -> 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' item.name.should.equal 'Wool'
it 'can find an ambiguous item by full slug', -> it 'can find an ambiguous item by full slug', ->
buildcraft.activeVersion = Mod.Version.Latest buildcraft.activeVersion = Mod.Version.Latest
industrialCraft.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.name.should.equal 'Wrench'
item.modVersion.mod.name.should.equal 'Industrial Craft' item.modVersion.mod.name.should.equal 'Industrial Craft'
it 'can find an ambiguous item by partial slug', -> it 'can find an ambiguous item by partial slug', ->
buildcraft.activeVersion = Mod.Version.Latest buildcraft.activeVersion = Mod.Version.Latest
industrialCraft.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.name.should.equal 'Wrench'
item.modVersion.mod.name.should.equal 'Buildcraft' item.modVersion.mod.name.should.equal 'Buildcraft'
@@ -80,7 +81,7 @@ describe 'mod_pack.coffee', ->
describe 'findItemDisplay', -> describe 'findItemDisplay', ->
it 'returns all data for a regular Minecraft item', -> 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.iconUrl.should.equal '/data/minecraft/1.7.10/images/bed.png'
display.itemUrl.should.equal '/mod/minecraft/bed' display.itemUrl.should.equal '/mod/minecraft/bed'
display.itemName.should.equal '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', -> it 'returns all data for an item in an enabled mod', ->
buildcraft.activeVersion = '6.2.6' 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.iconUrl.should.equal '/data/buildcraft/6.2.6/images/stone_gear.png'
display.itemUrl.should.equal '/mod/buildcraft/stone_gear' display.itemUrl.should.equal '/mod/buildcraft/stone_gear'
display.itemName.should.equal 'Stone Gear' display.itemName.should.equal 'Stone Gear'
display.modSlug.should.equal 'buildcraft' display.modSlug.should.equal 'buildcraft'
it 'assumes an unfound item is from Minecraft', -> 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.iconUrl.should.equal '/data/minecraft/1.7.10/images/iron_chestplate.png'
display.itemUrl.should.equal '/mod/minecraft/iron_chestplate' display.itemUrl.should.equal '/mod/minecraft/iron_chestplate'
display.itemName.should.equal 'Iron Chestplate' display.itemName.should.equal 'Iron Chestplate'
+9 -8
View File
@@ -6,6 +6,7 @@ All rights reserved.
### ###
Item = require '../src/scripts/models/item' Item = require '../src/scripts/models/item'
ItemSlug = require '../src/scripts/models/item_slug'
ModVersion = require '../src/scripts/models/mod_version' ModVersion = require '../src/scripts/models/mod_version'
######################################################################################################################## ########################################################################################################################
@@ -57,24 +58,24 @@ describe 'mod_version.coffee', ->
it 'returns immediately for unknown group', -> it 'returns immediately for unknown group', ->
slugs = [] slugs = []
modVersion.eachItemInGroup 'foobar', (item)-> slugs.push item.slug modVersion.eachItemInGroup 'foobar', (item)-> slugs.push item.slug.qualified
slugs.should.eql [] slugs.should.eql []
it 'calls callback for exactly the items in a group in order', -> it 'calls callback for exactly the items in a group in order', ->
slugs = [] slugs = []
modVersion.eachItemInGroup 'letter', (item)-> slugs.push item.slug modVersion.eachItemInGroup 'letter', (item)-> slugs.push item.slug.qualified
slugs.should.eql ['alpha', 'bravo'] slugs.should.eql ['test__alpha', 'test__bravo']
slugs = [] slugs = []
modVersion.eachItemInGroup 'number', (item)-> slugs.push item.slug modVersion.eachItemInGroup 'number', (item)-> slugs.push item.slug.qualified
slugs.should.eql ['one', 'two'] slugs.should.eql ['test__one', 'test__two']
describe 'findItemByName', -> describe 'findItemByName', ->
it 'locates items by slugified name', -> it 'locates items by slugified name', ->
modVersion.addItem new Item name:'Crafting Table' 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', -> describe 'findRecipes', ->
@@ -91,5 +92,5 @@ describe 'mod_version.coffee', ->
""" """
it 'finds all recipes which list item as output', -> it 'finds all recipes which list item as output', ->
recipes = modVersion.findRecipes 'test__bucket' recipes = modVersion.findRecipes ItemSlug.slugify('Bucket')
(r.output[0].slug for r in recipes).sort().should.eql ['test__bucket', 'test__cake', 'test__cake'] (r.output[0].itemSlug.item for r in recipes).sort().should.eql ['bucket', 'cake', 'cake']
@@ -6,6 +6,7 @@ All rights reserved.
### ###
CommandParserVersionBase = require '../../src/scripts/models/parser_versions/command_parser_version_base' 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' ModVersion = require '../../src/scripts/models/mod_version'
ModVersionParserV1 = require '../../src/scripts/models/parser_versions/mod_version_parser_v1' 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:Alpha; pattern:... .0. ...;
recipe:; input:Bravo; pattern:... 0.0 ...;" recipe:; input:Bravo; pattern:... 0.0 ...;"
modVersion = parser.parse recipes modVersion = parser.parse recipes
recipes = modVersion.findRecipes 'test__charlie' recipes = modVersion.findRecipes ItemSlug.slugify 'charlie'
recipes[0].input[0].slug.should.equal 'alpha' recipes[0].input[0].itemSlug.qualified.should.equal 'alpha'
recipes[1].input[0].slug.should.equal 'bravo' recipes[1].input[0].itemSlug.qualified.should.equal 'bravo'
describe 'name', -> describe 'name', ->
@@ -68,8 +69,8 @@ describe 'mod_version_parser_v1.coffee', ->
it 'adds "input" when present', -> it 'adds "input" when present', ->
modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo, Charlie; pattern: ... 012 ...' modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo, Charlie; pattern: ... 012 ...'
slugs = (s.slug for s in modVersion.findRecipes('test__charlie')[0].input) slugs = (s.itemSlug.item for s in modVersion.findRecipes(ItemSlug.slugify('charlie'))[0].input)
slugs.should.eql ['alpha', 'bravo', 'test__charlie'] slugs.should.eql ['alpha', 'bravo', 'charlie']
it 'requires an "input" declaration', -> it 'requires an "input" declaration', ->
func = -> parser.parse baseText + 'recipe:; pattern: ... .0. ...' func = -> parser.parse baseText + 'recipe:; pattern: ... .0. ...'
@@ -85,13 +86,13 @@ describe 'mod_version_parser_v1.coffee', ->
it 'registers slugs for each input name', -> it 'registers slugs for each input name', ->
modVersion = parser.parse baseText + 'recipe:; input:Delta, Echo, Foxtrot; pattern:...012...' 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', -> describe 'pattern', ->
it 'adds "pattern" when present', -> it 'adds "pattern" when present', ->
modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern:... .0. .1.' 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', -> it 'requires a "pattern" declaration', ->
func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo' 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', -> it 'computes the input stack sizes from the pattern', ->
modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo, Charlie; pattern:111 .0. 2.2' 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[0].quantity.should.equal 1
recipe.input[1].quantity.should.equal 3 recipe.input[1].quantity.should.equal 3
recipe.input[2].quantity.should.equal 2 recipe.input[2].quantity.should.equal 2
@@ -135,7 +136,7 @@ describe 'mod_version_parser_v1.coffee', ->
it 'adds "quantity" when present', -> it 'adds "quantity" when present', ->
modVersion = parser.parse baseText + 'quantity: 2' 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', -> it 'does not allow a duplicate "quantity" declaration', ->
func = -> parser.parse baseText + 'quantity:1; quantity:2' 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', -> it 'assumes a quantity of 1 by default', ->
modVersion = parser.parse baseText 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', -> it 'does not allow "quantity" before recipe', ->
func = -> parser.parse 'item:Bravo; quantity:12; 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', -> it 'adds a single item as the default output', ->
modVersion = parser.parse baseText modVersion = parser.parse baseText
stack = modVersion.findRecipes('test__bravo')[0].output[0] stack = modVersion.findRecipes(ItemSlug.slugify('bravo'))[0].output[0]
stack.slug.should.equal 'test__bravo' stack.itemSlug.qualified.should.equal 'test__bravo'
stack.quantity.should.equal 1 stack.quantity.should.equal 1
it 'can add multiple extras with quantities', -> it 'can add multiple extras with quantities', ->
modVersion = parser.parse baseText + 'extras:2 Delta, 4 Echo' modVersion = parser.parse baseText + 'extras:2 Delta, 4 Echo'
output = modVersion.findRecipes('test__bravo')[0].output output = modVersion.findRecipes(ItemSlug.slugify('bravo'))[0].output
output[0].slug.should.equal 'test__bravo' output[0].itemSlug.qualified.should.equal 'test__bravo'
output[0].quantity.should.equal 1 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[1].quantity.should.equal 2
output[2].slug.should.equal 'echo' output[2].itemSlug.qualified.should.equal 'echo'
output[2].quantity.should.equal 4 output[2].quantity.should.equal 4
it 'does not allow "extras" before "recipe"', -> it 'does not allow "extras" before "recipe"', ->
@@ -180,7 +181,9 @@ describe 'mod_version_parser_v1.coffee', ->
it 'registers slugs for each output name', -> it 'registers slugs for each output name', ->
modVersion = parser.parse baseText + 'extras:Delta, Echo' 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', -> it 'does not allow a duplicate "extras" declaration', ->
func = -> parser.parse baseText + 'extras:Echo; extras:Delta' func = -> parser.parse baseText + 'extras:Echo; extras:Delta'
@@ -193,17 +196,17 @@ describe 'mod_version_parser_v1.coffee', ->
it 'can add a single tool', -> it 'can add a single tool', ->
modVersion = parser.parse baseText + 'tools: Furnace' 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', -> it 'can add multiple tools', ->
modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace' modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace'
tools = modVersion.findRecipes('test__bravo')[0].tools tools = modVersion.findRecipes(ItemSlug.slugify('bravo'))[0].tools
tools[0].slug.should.equal 'crafting_table' tools[0].itemSlug.item.should.equal 'crafting_table'
tools[1].slug.should.equal 'furnace' tools[1].itemSlug.item.should.equal 'furnace'
it 'registers slugs for each tool name', -> it 'registers slugs for each tool name', ->
modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace' 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', -> it 'does not allow a duplicate "tools" declaration', ->
func = -> parser.parse baseText + 'tools:Crafting Table; tools:Furnace' func = -> parser.parse baseText + 'tools:Crafting Table; tools:Furnace'
@@ -245,6 +248,4 @@ describe 'mod_version_parser_v1.coffee', ->
actual = CommandParserVersionBase.simplify text actual = CommandParserVersionBase.simplify text
expected = CommandParserVersionBase.simplify baseText expected = CommandParserVersionBase.simplify baseText
logger.debug "actual:\n>>>#{actual}<<<\n\n\n"
logger.debug "expected:\n>>>#{expected}<<<"
actual.should.equal expected actual.should.equal expected
+24 -14
View File
@@ -5,9 +5,10 @@ Copyright (c) 2015 by Redwood Labs
All rights reserved. All rights reserved.
### ###
Item = require '../src/scripts/models/item' Item = require '../src/scripts/models/item'
Recipe = require '../src/scripts/models/recipe' ItemSlug = require '../src/scripts/models/item_slug'
Stack = require '../src/scripts/models/stack' Recipe = require '../src/scripts/models/recipe'
Stack = require '../src/scripts/models/stack'
######################################################################################################################## ########################################################################################################################
@@ -20,7 +21,10 @@ describe 'recipe.coffee', ->
describe 'constructor', -> describe 'constructor', ->
beforeEach -> 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.' pattern = '.1. 101 .1.'
it 'requires input', -> it 'requires input', ->
@@ -31,29 +35,32 @@ describe 'recipe.coffee', ->
it 'requires either outputs or a slug', -> it 'requires either outputs or a slug', ->
f = -> new Recipe input:input, pattern:pattern 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', -> 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.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 recipe.output[0].quantity.should.equal 1
it 'assigns a default slug', -> it 'assigns a default slug', ->
recipe = new Recipe input:input, pattern:pattern, output:[new Stack slug:'gold_gear'] recipe = new Recipe input:input, pattern:pattern, output:[new Stack itemSlug:ItemSlug.slugify('gold_gear')]
recipe.slug.should.equal 'gold_gear' recipe.itemSlug.qualified.should.equal 'gold_gear'
describe 'getItemSlugAt', -> describe 'getItemSlugAt', ->
beforeEach -> beforeEach ->
input = [ new Stack(slug:'iron_gear'), new Stack(slug:'gold_ingot', quantity:4) ] input = [
recipe = new Recipe slug:'gold_gear', input:input, pattern:'.1. 101 .1.' 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', -> 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', -> 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', -> it 'returns null for an invalid slot', ->
expect(recipe.getItemSlugAt(12)).to.be.null expect(recipe.getItemSlugAt(12)).to.be.null
@@ -61,7 +68,10 @@ describe 'recipe.coffee', ->
describe '_parsePattern', -> describe '_parsePattern', ->
beforeEach -> 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', -> it 'normalizes invalid characters', ->
recipe._parsePattern('$$0 #() 010').should.equal '..0 ... 010' recipe._parsePattern('$$0 #() 010').should.equal '..0 ... 010'
+6 -5
View File
@@ -28,15 +28,16 @@ require '../src/scripts/underscore_mixins'
mocha.setup 'bdd' 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.test'
require './inventory_parser.test' require './recipe.test'
require './mod_version.test'
require './mod.test' require './mod.test'
require './mod_pack.test' require './mod_pack.test'
require './mod_version.test'
require './parser_versions/mod_version_parser_v1.test' require './parser_versions/mod_version_parser_v1.test'
require './recipe.test' require './crafting_plan.test'
require './string_builder.test'
mocha.checkLeaks() mocha.checkLeaks()
mocha.globals ['LiveReload'] mocha.globals ['LiveReload']