Complete refactoring to move Recipes into Items

* Changed "version" to "dataVersion" in data format
* Changed the "modName" and "modVersion" fields on the ModVersion
  class to remove the "mod" prefix and changed the data format to
  match
* Change the Stack class to use the itemSlug instead of the item
  itself
* Update the ModVersion and ModPack classes to include methods for
  recording a correspondence between slugs and names. Update code
  which displays items from stacks to use these methods to determine
  display names.
* Add a reference from Recipe back to the item it creates
This commit is contained in:
Andrew Miner
2015-01-03 20:08:11 -08:00
parent 392c410df8
commit 0d25ac723d
24 changed files with 260 additions and 285 deletions
+3 -3
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"mod_name": "Applied Energetics",
"mod_version": "rv-12b",
"dataVersion": 1,
"name": "Applied Energetics",
"version": "rv-12b",
"description": "Crafting recipes from Applied Energetics, by AlgorithmX2",
"recipes": [
{
+3 -3
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"mod_name": "BuildCraft",
"mod_version": "3.0",
"dataVersion": 1,
"name": "BuildCraft",
"version": "3.0",
"description": "Crafting recipes from BuildCraft, by SpaceToad",
"recipes": [
{
+3 -3
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"mod_name": "GraviSuite",
"mod_version": "unknown",
"dataVersion": 1,
"name": "GraviSuite",
"version": "1.0",
"description": "Crafting recipes from GraviSuite, by flotschi301",
"recipes": [
{
+3 -3
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"mod_name": "Industrial Craft",
"mod_version": "2.0",
"dataVersion": 1,
"name": "Industrial Craft",
"version": "2.0",
"description": "Crafting recipes from IndustrialCraft, by Alblaka",
"recipes": [
{
+3 -3
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"mod_name": "Minecraft",
"mod_version": "1.7.10",
"dataVersion": 1,
"name": "Minecraft",
"version": "1.7.10",
"description": "Crafting recipes from vanilla Minecraft",
"raw_materials": [
"Coal",
+3 -3
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"mod_name": "MoreBlocks",
"mod_version": "1.0",
"dataVersion": 1,
"name": "MoreBlocks",
"version": "1.0",
"description": "Crafting recipes from More Blocks Mod, by MCE626",
"recipes": [
{
+3 -3
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"mod_name": "Thermal Expansion",
"mod_version": "2.x",
"dataVersion": 1,
"name": "Thermal Expansion",
"version": "2.x",
"description": "Crafting Recipes from Thermal Expansion, by King Lemming and the CoFH team",
"recipes": [
{
@@ -100,12 +100,15 @@ module.exports = class CraftingTableController extends BaseController
@$resultList.empty()
if @model.plan?
@model.plan.need.each (item)=>
@$needList.append "<li><span class='quantity'>#{item.quantity}</span> #{item.name}</li>"
@model.plan.make.each (item)=>
@$makeList.append "<li><span class='quantity'>#{item.quantity}</span> #{item.name}</li>"
@model.plan.result.each (item)=>
@$resultList.append "<li><span class='quantity'>#{item.quantity}</span> #{item.name}</li>"
@model.plan.need.each (stack)=>
name = @model.modPack.findName stack.itemSlug
@$needList.append "<li><span class='quantity'>#{stack.quantity}</span> #{name}</li>"
@model.plan.make.each (stack)=>
name = @model.modPack.findName stack.itemSlug
@$makeList.append "<li><span class='quantity'>#{stack.quantity}</span> #{name}</li>"
@model.plan.result.each (stack)=>
name = @model.modPack.findName stack.itemSlug
@$resultList.append "<li><span class='quantity'>#{stack.quantity}</span> #{name}</li>"
super
@@ -33,14 +33,14 @@ module.exports = class ModVersionController extends BaseController
super
refresh: ->
if @model.modName in RequiredMods
if @model.name in RequiredMods
@$enabled.attr 'checked', 'checked'
@$enabled.attr 'disabled', 'disabled'
else
@$enabled.removeAttr 'disabled'
if @model.enabled then @$enabled.attr('checked', 'checked') else @$enabled.removeAttr('checked')
@$name.html "#{@model.modName} (#{@model.modVersion})"
@$name.html "#{@model.name} (#{@model.version})"
@$description.html "#{@model.description}"
# Backbone.View Overrides ######################################################################
+20 -22
View File
@@ -32,13 +32,12 @@ module.exports = class CraftingPlan
logger.trace "craft(#{name}, #{quantity}, #{have})"
item = @modPack.findItemByName name
logger.debug "item: #{item}"
if not item? then throw new Error "cannot find an item named: #{name}"
@clear()
@result.addInventory(have) if have?
@_expected.add item, quantity
@_expected.add item.slug, quantity
@_pending = @_expected.clone()
while not @_pending.isEmpty
@@ -55,8 +54,8 @@ module.exports = class CraftingPlan
_processPending: ->
targetStack = @_pending.pop()
targetItem = targetStack.item
logger.verbose "processing item: #{targetItem}, craftable? #{targetItem.isCraftable}"
targetItem = @modPack.findItem targetStack.itemSlug
logger.verbose "processing item: #{targetStack}"
return unless targetItem?
return if (not targetItem.isCraftable) or targetItem.isGatherable
@@ -64,11 +63,12 @@ module.exports = class CraftingPlan
logger.verbose "recipe: #{recipe}"
if @includingTools
for toolItem in recipe.tools
totalExpected = @result.quantityOf(toolItem.slug) + @_expected.quantityOf(toolItem.slug)
for toolStack in recipe.tools
slug = toolStack.itemSlug
totalExpected = @result.quantityOf(slug) + @_expected.quantityOf(slug)
if totalExpected < 1
@_pending.add toolItem
@_expected.add toolItem
@_pending.add slug
@_expected.add slug
while @_totalQuantityOf(targetItem.slug) < @_expected.quantityOf(targetItem.slug)
@steps.push recipe
@@ -80,28 +80,26 @@ module.exports = class CraftingPlan
@_processOutputStack stack
_processInputStack: (stack)->
slug = stack.item.slug
quantityAvailable = @result.quantityOf slug
quantityAvailable = @result.quantityOf stack.itemSlug
quantityUsed = Math.min quantityAvailable, stack.quantity
quantityNeeded = stack.quantity - quantityUsed
logger.verbose "processing input:#{stack.name},
logger.trace "processing input:#{stack.itemSlug},
a:#{quantityAvailable}, u:#{quantityUsed}, n:#{quantityNeeded}"
@result.remove slug, quantityUsed
@_pending.add stack.item, quantityNeeded
@need.add stack.item, quantityNeeded
@result.remove stack.itemSlug, quantityUsed
@_pending.add stack.itemSlug, quantityNeeded
@need.add stack.itemSlug, quantityNeeded
_processOutputStack: (stack)->
slug = stack.item.slug
quantityMissing = @need.quantityOf slug
quantityMissing = @need.quantityOf stack.itemSlug
quantityUsed = Math.min quantityMissing, stack.quantity
quantityLeft = stack.quantity - quantityUsed
logger.verbose "processing output:#{stack.name},
logger.trace "processing output:#{stack.itemSlug},
m:#{quantityMissing}, u:#{quantityUsed}, l:#{quantityLeft}"
@make.add stack.item, stack.quantity
@need.remove slug, quantityUsed
@result.add stack.item, quantityLeft
@make.add stack.itemSlug, stack.quantity
@need.remove stack.itemSlug, quantityUsed
@result.add stack.itemSlug, quantityLeft
_totalQuantityOf: (slug)->
return @result.quantityOf(slug) - @need.quantityOf(slug)
_totalQuantityOf: (itemSlug)->
return @result.quantityOf(itemSlug) - @need.quantityOf(itemSlug)
+30 -30
View File
@@ -21,24 +21,24 @@ module.exports = class Inventory extends BaseModel
# Public Methods ###############################################################################
add: (item, quantity=1)->
add: (itemSlug, quantity=1)->
return if quantity is 0
stack = @_stacks[item.slug]
stack = @_stacks[itemSlug]
if not stack?
stack = new Stack item:item, quantity:quantity
@_stacks[item.slug] = stack
@_slugs.push item.slug
stack = new Stack itemSlug:itemSlug, quantity:quantity
@_stacks[itemSlug] = stack
@_slugs.push itemSlug
@_slugs.sort()
else
stack.quantity += quantity
@trigger Event.add, this, item, quantity
@trigger Event.add, this, itemSlug, quantity
@trigger Event.change, this
return this
addInventory: (inventory)->
inventory.each (stack)=> @add stack.item, stack.quantity
inventory.each (stack)=> @add stack.itemSlug, stack.quantity
return this
clear: ->
@@ -47,51 +47,51 @@ module.exports = class Inventory extends BaseModel
clone: ->
inventory = new Inventory
@each (stack)-> inventory.add stack.item, stack.quantity
@each (stack)-> inventory.add stack.itemSlug, stack.quantity
return inventory
each: (onItem)->
for slug in @_slugs
stack = @_stacks[slug]
onItem stack
each: (onStack)->
for itemSlug in @_slugs
stack = @_stacks[itemSlug]
onStack stack
hasAtLeast: (slug, quantity=1)->
hasAtLeast: (itemSlug, quantity=1)->
if quantity is 0 then return true
stack = @_stacks[slug]
stack = @_stacks[itemSlug]
return false unless stack?
return stack.quantity >= quantity
pop: ->
slug = @_slugs.pop()
return null unless slug?
itemSlug = @_slugs.pop()
return null unless itemSlug?
stack = @_stacks[slug]
delete @_stacks[slug]
stack = @_stacks[itemSlug]
delete @_stacks[itemSlug]
@trigger Event.remove, this, stack.item, stack.quantity
@trigger Event.remove, this, stack.itemSlug, stack.quantity
@trigger Event.change, this
return stack
quantityOf: (slug)->
stack = @_stacks[slug]
quantityOf: (itemSlug)->
stack = @_stacks[itemSlug]
return 0 unless stack?
return stack.quantity
remove: (slug, quantity=1)->
remove: (itemSlug, quantity=1)->
return if quantity is 0
stack = @_stacks[slug]
if not stack? then throw new Error "cannot remove #{slug} since it is not in this inventory"
stack = @_stacks[itemSlug]
if not stack? then throw new Error "cannot remove #{itemSlug} since it is not in this inventory"
if stack.quantity < quantity
throw new Error "cannot remove #{quantity} #{slug} because there is only #{stack.quantity} in this inventory"
throw new Error "cannot remove #{quantity}: only #{stack.quantity} #{itemSlug} in this inventory"
stack.quantity -= quantity
if stack.quantity is 0
delete @_stacks[slug]
@_slugs = _(@_slugs).without slug
delete @_stacks[itemSlug]
@_slugs = _(@_slugs).without itemSlug
@trigger Event.remove, this, slug, quantity
@trigger Event.remove, this, itemSlug, quantity
@trigger Event.change, this
return this
@@ -99,9 +99,9 @@ module.exports = class Inventory extends BaseModel
result = []
@each (stack)->
if stack.quantity > 1
result.push [stack.quantity, stack.item.slug]
result.push [stack.quantity, stack.itemSlug]
else
result.push stack.item.slug
result.push stack.itemSlug
return result
# Object Overrides #############################################################################
+1 -1
View File
@@ -25,6 +25,6 @@ module.exports = class InventoryParser
quantity = if match? then parseInt(match[1]) else 1
if name.length > 0
inventory.add new Item(name:name), quantity
inventory.add _.slugify(name), quantity
return inventory
+2 -2
View File
@@ -27,8 +27,8 @@ module.exports = class Item extends BaseModel
# Public Methods ###############################################################################
addRecipe: (recipe)->
output = recipe.output[0].item
if output isnt this then throw new Error "invalid recipe for #{@name} because it makes a #{output.name}"
slug = recipe.output[0].itemSlug
if slug isnt @slug then throw new Error "invalid recipe for #{@slug} because it makes a #{slug}"
@recipes.push recipe
+20
View File
@@ -29,6 +29,16 @@ module.exports = class ModPack extends BaseModel
if modVersion.hasRecipe name
modVersion.enabled = true
findItem: (itemSlug, options={})->
options.includeDisabled ?= false
for modVersion in @modVersions
continue unless modVersion.enabled or options.includeDisabled
item = modVersion.items[itemSlug]
return item if item?
return null
findItemByName: (name, options={})->
options.includeDisabled ?= false
@@ -39,6 +49,16 @@ module.exports = class ModPack extends BaseModel
return null
findName: (slug, options={})->
options.includeDisabled ?= false
for modVersion in @modVersions
continue unless modVersion.enabled or options.includeDisabled
name = modVersion.findName slug
return name if name
return slug
gatherRecipeNames: (options={})->
options.includeDisabled ?= false
+18 -11
View File
@@ -13,12 +13,13 @@ BaseModel = require './base_model'
module.exports = class ModVersion extends BaseModel
constructor: (attributes={}, options={})->
if _.isEmpty(attributes.modName) then throw new Error 'modName cannot be empty'
if _.isEmpty(attributes.modVersion) then throw new Error 'modVersion cannot be empty'
if _.isEmpty(attributes.name) then throw new Error 'name cannot be empty'
if _.isEmpty(attributes.version) then throw new Error 'version cannot be empty'
attributes.description ?= ''
attributes.items ?= {}
attributes.enabled ?= attributes.modName in RequiredMods
attributes.enabled ?= attributes.name in RequiredMods
attributes.names ?= {}
super attributes, options
# Public Methods ###############################################################################
@@ -29,29 +30,32 @@ module.exports = class ModVersion extends BaseModel
return this
compareTo: (that)->
if this.modName is that.modName then return 0
if this.name is that.name then return 0
thisRequired = this.modName in RequiredMods
thatRequired = that.modName in RequiredMods
thisRequired = this.name in RequiredMods
thatRequired = that.name in RequiredMods
if thisRequired and thatRequired
return if this.modName < that.modName then -1 else +1
return if this.name < that.name then -1 else +1
else if thisRequired
return -1
else if thatRequired
return +1
else
return if this.modName < that.modName then -1 else +1
return if this.name < that.name then -1 else +1
findItemByName: (name)->
slug = _.slugify name
return @items[slug]
findName: (slug)->
return @names[slug]
gatherRecipeNames: (result={})->
for slug, item of @items
continue if result[item.slug]
continue unless item.isCraftable
result[item.slug] = value:item.name, label:"#{item.name} (from #{@modName} #{@modVersion})"
result[item.slug] = value:item.name, label:"#{item.name} (from #{@name} #{@version})"
return result
@@ -60,11 +64,14 @@ module.exports = class ModVersion extends BaseModel
return false unless item?
return item.recipes.length > 0
registerSlug: (slug, name)->
@names[slug] = name
# Object Overrides #############################################################################
toString: ->
return "ModVersion (#{@cid}) {
enabled:#{@enabled},
modName:#{@modName},
modVersion:#{@modVersion},
name:#{@name},
version:#{@version},
items:#{_.keys(@items).length} items}"
+40 -64
View File
@@ -23,18 +23,18 @@ module.exports = class ModVersionParser
parse: (data)->
if not data? then throw new Error 'mod description data is missing'
if not data.version? then throw new Error 'version is required'
if not data.dataVersion? then throw new Error 'dataVersion is required'
parser = @_parsers["#{data.version}"]
if not parser? then throw new Error "cannot parse version #{data.version} mod descriptions"
parser = @_parsers["#{data.dataVersion}"]
if not parser? then throw new Error "cannot parse version #{data.dataVersion} mod descriptions"
return parser.parse data
unparse: (modVersion, version=ModVersionParser.CURRENT_VERSION)->
unparse: (modVersion, dataVersion=ModVersionParser.CURRENT_VERSION)->
if not modVersion? then throw new Error 'modVersion is required'
parser = @_parsers["#{version}"]
if not parser? then throw new Error "version #{version} is not supported"
parser = @_parsers["#{dataVersion}"]
if not parser? then throw new Error "version #{dataVersion} is not supported"
return parser.unparse modVersion
@@ -49,7 +49,10 @@ module.exports.V1 = class V1
return @_parseModVersion data
unparse: (modVersion)->
return @_unparseModVersion modVersion
@modVersion = modVersion
text = @_unparseModVersion modVersion
@modVersion = null
return text
# Private Methods ##############################################################################
@@ -58,27 +61,16 @@ module.exports.V1 = class V1
if not item?
item = new Item name:name
@modVersion.addItem item
@modVersion.registerSlug item.slug, item.name
return item
_parseItemList: (data, options={})->
if not data? then throw new Error "#{@_errorLocation} must have an #{options.field} field"
if not _.isArray(data) then data = [data]
result = []
for name in data
item = @_findOrCreateItem name
result.push item
return result
_parseModVersion: (data)->
if not data? then throw new Error 'mod description data is missing'
if not data.name? then throw new Error 'name is required'
if not data.version? then throw new Error 'version is required'
if not data.mod_name? then throw new Error 'mod_name is required'
if not data.mod_version? then throw new Error 'mod_version is required'
if not _.isArray(data.recipes) then throw new Error 'recipes must be an array'
@modVersion = modVersion = new ModVersion modName:data.mod_name, modVersion:data.mod_version
@modVersion = modVersion = new ModVersion name:data.name, version:data.version
modVersion.description = data.description or ''
@_parseRawMaterials data.raw_materials
@@ -86,12 +78,13 @@ module.exports.V1 = class V1
for index in [0...data.recipes.length]
@_errorLocation = "recipe #{index + 1}"
recipeData = data.recipes[index]
@_parseRecipe recipeData
recipe = @_parseRecipe recipeData
recipe._originalIndex = index
@modVersion = null
return modVersion
_parseRawMaterials: (data, options={})->
_parseRawMaterials: (data)->
return unless data? and data.length > 0
results = []
@@ -100,21 +93,25 @@ module.exports.V1 = class V1
item.isGatherable = true
results.push item
_parseRecipe: (data, options={})->
_parseRecipe: (data)->
if not data? then throw new Error "recipe data is missing for #{@_errorLocation}"
if not data.output? then throw new Error "#{@_errorLocation} is missing output"
output = @_parseStackList data.output, field:'output', canBeEmpty:false
item = output[0].item
data.output = if _.isArray(data.output) then data.output else [data.output]
names = (e for e in _.flatten(data.output) when _.isString(e))
if names.length is 0 then throw new Error "#{@_errorLocation} has an empty output list"
item = @_findOrCreateItem names[0]
@_errorLocation = "recipe for #{item.name}"
if not data.input? then throw new Error "#{@_errorLocation} is missing input"
data.tools ?= []
input = @_parseStackList data.input, field:'input', canBeEmpty:true
tools = @_parseItemList data.tools, field:'tools'
output = @_parseStackList data.output, field:'output', canBeEmpty:false
input = @_parseStackList data.input, field:'input', canBeEmpty:true
tools = @_parseStackList data.tools, field:'tools', canBeEmpty:true
recipe = new Recipe input:input, output:output, tools:tools
recipe = new Recipe item:item, input:input, output:output, tools:tools
item.addRecipe recipe
return recipe
@@ -129,8 +126,11 @@ module.exports.V1 = class V1
if data.length isnt 2 then throw new Error "#{errorBase} must have at least one element"
if not _.isNumber(data[0]) then throw new Error "#{errorBase} must start with a number"
item = @_findOrCreateItem data[1]
return new Stack item:item, quantity:data[0]
name = data[1]
slug = _.slugify name
@modVersion.registerSlug slug, name
return new Stack itemSlug:slug, quantity:data[0]
_parseStackList: (data, options={})->
if not data? then throw new Error "#{@_errorLocation} must have an #{options.field} field"
@@ -146,25 +146,14 @@ module.exports.V1 = class V1
return result
_parseItemList: (data, options={})->
if not data? then throw new Error "#{@_errorLocation} must have an #{options.field} field"
if not _.isArray(data) then data = [data]
result = []
for name in data
item = @_findOrCreateItem name
result.push item
return result
# Un-parsing Methods ###########################################################################
_unparseModVersion: (modVersion)->
result = []
result.push '{\n'
result.push ' "version": 1,\n'
result.push ' "mod_name": "' + modVersion.modName + '",\n'
result.push ' "mod_version": "' + modVersion.modVersion + '",\n'
result.push ' "mod_name": "' + modVersion.name + '",\n'
result.push ' "mod_version": "' + modVersion.version + '",\n'
if modVersion.description.length > 0
result.push ' "description": "' + modVersion.description + '",\n'
@@ -209,7 +198,7 @@ module.exports.V1 = class V1
if recipe.tools.length > 0
result.push ',\n'
result.push ' "tools": '
@_unparseItemList recipe.tools, result
@_unparseStackList recipe.tools, result
result.push '\n'
return result
@@ -222,9 +211,9 @@ module.exports.V1 = class V1
else if stackList.length is 1
stack = stackList[0]
if stack.quantity is 1
result.push '"' + stack.name + '"'
result.push '"' + @modVersion.findName(stack.itemSlug) + '"'
else
result.push '[[' + stack.quantity + ', "' + stack.name + '"]]'
result.push '[[' + stack.quantity + ', "' + @modVersion.findName(stack.itemSlug) + '"]]'
else
result.push '['
@@ -233,32 +222,19 @@ module.exports.V1 = class V1
stacks.sort (a, b)->
if a.quantity isnt b.quantity
return if a.quantity > b.quantity then -1 else +1
if a.item.name isnt b.item.name
return if a.item.name < b.item.name then -1 else +1
if a.itemSlug isnt b.itemSlug
return if a.itemSlug < b.itemSlug then -1 else +1
return 0
firstItem = true
for stack in stacks
result.push ', ' if not firstItem
if stack.quantity is 1
result.push '"' + stack.name + '"'
result.push '"' + @modVersion.findName(stack.itemSlug) + '"'
else
result.push '[' + stack.quantity + ', "' + stack.name + '"]'
result.push '[' + stack.quantity + ', "' + @modVersion.findName(stack.itemSlug) + '"]'
firstItem = false
result.push ']'
return result
_unparseItemList: (itemList, result)->
if itemList.length is 1
result.push '"'; result.push itemList[0].name; result.push '"'
else
firstItem = true
for item in itemList
result.push if firstItem then '["' else '", "'
result.push item.name
firstItem = false
result.push '"]'
return result
+8 -7
View File
@@ -12,6 +12,7 @@ BaseModel = require './base_model'
module.exports = class Recipe extends BaseModel
constructor: (attributes={}, options={})->
if not attributes.item? then throw new Error 'attributes.item is required'
if not attributes.input? then throw new Error 'attributes.input is required'
if not attributes.output? then throw new Error 'attributes.output is required'
@@ -19,22 +20,22 @@ module.exports = class Recipe extends BaseModel
attributes.tools ?= []
super attributes, options
Object.defineProperty @prototype, 'name', get:-> @output[0].item.name
Object.defineProperty @prototype, 'name', get:-> @item.name
# Public Methods ###############################################################################
make: (inventory, missing)->
for stack in @input
item = stack.item
needed = stack.quantity
itemSlug = stack.itemSlug
needed = stack.quantity
while needed > 0
if inventory.hasAtLeast item.slug
inventory.remove item.slug
if inventory.hasAtLeast itemSlug
inventory.remove itemSlug
else
missing.add item.slug
missing.add itemSlug
for stack in @output
inventory.add stack.item, stack.quantity
inventory.add stack.itemSlug, stack.quantity
return this
+5 -20
View File
@@ -10,40 +10,25 @@ All rights reserved.
module.exports = class Stack
constructor: (attributes={})->
if not attributes.item? then throw new Error 'item is required'
if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required'
attributes.quantity ?= 1
@item = attributes.item
@itemSlug = attributes.itemSlug
@quantity = attributes.quantity
Object.defineProperty @prototype, 'name', get:-> @item?.name
Object.defineProperty @prototype, 'stackQuantity', get:@getStackQuantity
# Public Methods ###############################################################################
canMerge: (stack)->
return @item.slug is stack.item.slug
return @itemSlug is stack.itemSlug
merge: (stack)->
if not @canMerge stack
throw new Error "this stack of #{@item.name} cannot merge a stack of #{@stack.name}"
throw new Error "this stack of #{@itemSlug} cannot merge a stack of #{@stack.itemSlug}"
@quantity += stack.quantity
return this
# Property Methods #############################################################################
getStackQuantity: ->
count = 0
extra = @quantity
while extra > @item.stackSize
extra -= @item.stackSize
count += 1
return count:count, extra:extra
# Object Overrides #############################################################################
toString: ->
return "#{@quantity} #{@item.name}"
return "#{@quantity} #{@itemSlug}"
+3 -5
View File
@@ -19,9 +19,9 @@ describe 'CraftingPlan', ->
beforeEach ->
modPack = new ModPack
modPack.loadModVersionData {
version: 1
mod_name: 'Minecraft'
mod_version: '1.7.10'
dataVersion: 1
name: 'Minecraft'
version: '1.7.10'
recipes: [
{ input:'Oak Log', output:[[4, 'Oak Plank']] }
{ input:[[2, 'Oak Plank']], output:[[4, 'Stick']] }
@@ -31,8 +31,6 @@ describe 'CraftingPlan', ->
{ input:[[2, 'Iron Ingot'], 'Stick'], tools:'Crafting Table', output:'Iron Sword' }
]
}
logger.debug "modPack: #{modPack}"
logger.debug "modVersion: #{modPack.modVersions[0]}"
plan = new CraftingPlan modPack
+19 -20
View File
@@ -20,30 +20,30 @@ describe 'Inventory', ->
beforeEach ->
inventory = new Inventory
inventory.add new Item(name:'Wool'), 4
inventory.add new Item(name:'String'), 20
inventory.add new Item(name:'Boat')
inventory.add 'wool', 4
inventory.add 'string', 20
inventory.add 'boat'
describe 'add', ->
it 'can add to an empty inventory', ->
inventory.add new Item(name:'Iron Ingot'), 4
inventory.add 'iron_ingot', 4
stack = inventory._stacks['iron_ingot']
stack.constructor.name.should.equal 'Stack'
stack.name.should.equal 'Iron Ingot'
stack.itemSlug.should.equal 'iron_ingot'
stack.quantity.should.equal 4
it 'can augment quantity of existing items', ->
inventory.add new Item(name:'Wool'), 2
inventory.add 'wool', 2
inventory.toList().should.eql ['boat', [20, 'string'], [6, 'wool']]
it 'can add zero quantity', ->
inventory.add new Item(name:'Wool'), 0
inventory.add 'wool', 0
inventory.toList().should.eql ['boat', [20, 'string'], [4, 'wool']]
it 'emits the proper events', ->
events = new EventRecorder inventory
inventory.add new Item(name:'Iron Ingot'), 10
inventory.add 'iron_ingot', 10
events.names.should.eql [Event.add, Event.change]
describe 'addInventory', ->
@@ -55,7 +55,7 @@ describe 'Inventory', ->
it 'can add a mix of new and existing items', ->
newInventory = new Inventory
newInventory.add new Item(name:'String'), 2
newInventory.add 'string', 2
newInventory.addInventory inventory
newInventory.toList().should.eql ['boat', [22, 'string'], [4, 'wool']]
@@ -68,7 +68,6 @@ describe 'Inventory', ->
it 'faithfully copies an existing inventory', ->
copy = inventory.clone()
logger.debug "copy.toList(): #{copy.toList()}"
copy.toList().should.eql ['boat', [20, 'string'], [4, 'wool']]
describe 'each', ->
@@ -81,17 +80,17 @@ describe 'Inventory', ->
it 'works when items have only been added', ->
result = []
inventory.each (stack)-> result.push stack.name
result.should.eql ['Boat', 'String', 'Wool']
inventory.each (stack)-> result.push stack.itemSlug
result.should.eql ['boat', 'string', 'wool']
it 'works when items have been augmented', ->
inventory.add new Item name:'Iron Ingot'
inventory.add new Item name:'Boat'
inventory.add new Item(name:'Wool'), 2
inventory.add 'iron_ingot'
inventory.add 'boat'
inventory.add 'wool', 2
result = []
inventory.each (stack)-> result.push stack.name
result.should.eql ['Boat', 'Iron Ingot', 'String', 'Wool']
inventory.each (stack)-> result.push stack.itemSlug
result.should.eql ['boat', 'iron_ingot', 'string', 'wool']
describe 'hasAtLeast', ->
@@ -101,7 +100,7 @@ describe 'Inventory', ->
it 'always returns true for zero quantity', ->
inventory.hasAtLeast('chicken', 0).should.be.true
inventory.hasAtLeast('Wool', 0).should.be.true
inventory.hasAtLeast('wool', 0).should.be.true
it 'works for a quantity above 1', ->
inventory.hasAtLeast('wool', 3).should.be.true
@@ -117,7 +116,7 @@ describe 'Inventory', ->
it 'completely removes the last item', ->
stack = inventory.pop()
stack.name.should.equal 'Wool'
stack.itemSlug.should.equal 'wool'
stack.quantity.should.equal 4
inventory.toList().should.eql ['boat', [20, 'string']]
@@ -134,7 +133,7 @@ describe 'Inventory', ->
it 'throws when the item has insufficient quantity', ->
expect(-> inventory.remove('wool', 10)).to.throw Error,
'cannot remove 10 wool because there is only 4 in this inventory'
'cannot remove 10: only 4 wool in this inventory'
it 'removes a single item by default', ->
inventory.remove 'wool'
+1 -1
View File
@@ -37,6 +37,6 @@ describe 'InventoryParser', ->
it 're-uses the given inventory object', ->
inventory = new Inventory
inventory.add new Item(name:'String'), 8
inventory.add 'string', 8
result = parser.parse '4 Wool', inventory
result.toList().should.eql [[8, 'string'], [4, 'wool']]
+3 -3
View File
@@ -18,15 +18,15 @@ buildcraft = industrialCraft = minecraft = modPack = null
describe 'ModPack', ->
beforeEach ->
minecraft = new ModVersion modName:'Minecraft', modVersion:'1.7.10'
minecraft = new ModVersion name:'Minecraft', version:'1.7.10'
minecraft.addItem new Item name:'Wool'
minecraft.addItem new Item name:'Bed', recipes:['']
buildcraft = new ModVersion modName:'Buildcraft', modVersion:'4.0'
buildcraft = new ModVersion name:'Buildcraft', version:'4.0'
buildcraft.addItem new Item name:'Stone Gear', recipes:['']
buildcraft.addItem new Item name:'Bed', recipes:['']
industrialCraft = new ModVersion modName:'Industrial Craft', modVersion:'2.0'
industrialCraft = new ModVersion name:'Industrial Craft', version:'2.0'
industrialCraft.addItem new Item name:'Resin'
industrialCraft.addItem new Item name:'Rubber', recipes:['']
+5 -5
View File
@@ -16,15 +16,15 @@ modVersion = null
describe 'ModVersion', ->
beforeEach -> modVersion = new ModVersion modName:'Test', modVersion:'0.0'
beforeEach -> modVersion = new ModVersion name:'Test', version:'0.0'
describe 'constructor', ->
it 'requires a mod name', ->
expect(-> new ModVersion modVersion:'0.0').to.throw Error, 'modName cannot be empty'
expect(-> new ModVersion version:'0.0').to.throw Error, 'name cannot be empty'
it 'requires a mod version', ->
expect(-> new ModVersion modName:'Test').to.throw Error, 'modVersion cannot be empty'
expect(-> new ModVersion name:'Test').to.throw Error, 'version cannot be empty'
it 'supplies default values', ->
modVersion.description.should.equal ''
@@ -44,12 +44,12 @@ describe 'ModVersion', ->
describe 'compareTo', ->
it 'lists required mods first', ->
minecraft = new ModVersion modName:'Minecraft', modVersion:'1.7.10'
minecraft = new ModVersion name:'Minecraft', version:'1.7.10'
modVersion.compareTo(minecraft).should.equal +1
minecraft.compareTo(modVersion).should.equal -1
it 'sorts by name second', ->
buildcraft = new ModVersion modName:'Buildcraft', modVersion:'3.0'
buildcraft = new ModVersion name:'Buildcraft', version:'3.0'
modVersion.compareTo(buildcraft).should.equal +1
buildcraft.compareTo(modVersion).should.equal -1
+53 -65
View File
@@ -11,7 +11,7 @@ ModVersionParser = require '../src/scripts/models/mod_version_parser'
########################################################################################################################
parser = null
modVersion = parser = null
########################################################################################################################
@@ -19,93 +19,77 @@ describe 'ModVersionParser', ->
describe "V1", ->
before -> parser = new ModVersionParser.V1
describe '_parseItemList', ->
beforeEach -> parser.modVersion = new ModVersion modName:'Test', modVersion:'0.0'
it 'can parse an empty list', ->
parser._parseItemList []
_.keys(parser.modVersion.items).should.eql []
it 'can add new items', ->
parser._parseItemList ['Crafting Table', 'Furnace']
_.keys(parser.modVersion.items).should.eql ['crafting_table', 'furnace']
it 'can find existing items', ->
parser.modVersion.addItem new Item name:'Furnace'
parser._parseItemList ['Crafting Table', 'Furnace']
_.keys(parser.modVersion.items).should.eql ['furnace', 'crafting_table']
beforeEach ->
parser = new ModVersionParser.V1
modVersion = parser.modVersion = new ModVersion name:'Test', version:'0.0'
describe '_parseModVersion', ->
it 'requires a mod_name', ->
data = version:1, mod_version:'1.0', items:[]
expect(-> parser._parseModVersion data).to.throw Error, 'mod_name is required'
data = version:'1.0', items:[]
expect(-> parser._parseModVersion data).to.throw Error, 'name is required'
it 'requires a mod_version', ->
data = version:1, mod_name:'Empty', items:[]
expect(-> parser._parseModVersion data).to.throw Error, 'mod_version is required'
data = name:'Empty', items:[]
expect(-> parser._parseModVersion data).to.throw Error, 'version is required'
it 'can parse an empty modVersion', ->
data =
version: 1
mod_name: 'Empty'
mod_version: '1.0'
name: 'Empty'
version: '1.0'
recipes: []
modVersion = parser._parseModVersion data
modVersion.modName.should.equal 'Empty'
modVersion.modVersion.should.equal '1.0'
modVersion.name.should.equal 'Empty'
modVersion.version.should.equal '1.0'
it 'can parse a non-empty mod version', ->
data =
version: 1
mod_name: 'Minecraft'
mod_version: '1.7.10'
name: 'Minecraft'
version: '1.7.10'
recipes: [
{ input:'Sugar Cane', output:'Sugar' }
{ input:[[3, 'Wool'], [3, 'Planks']], tools:'Crafting Table', output:'Bed' }
]
modVersion = parser._parseModVersion data
modVersion.modName.should.equal 'Minecraft'
modVersion.modVersion.should.equal '1.7.10'
modVersion.name.should.equal 'Minecraft'
modVersion.version.should.equal '1.7.10'
slugs = (slug for slug, item of modVersion.items).sort()
slugs.should.eql ['bed', 'crafting_table', 'planks', 'sugar', 'sugar_cane', 'wool']
slugs.should.eql ['bed', 'sugar']
describe '_parseRawMaterials', ->
beforeEach -> parser.modVersion = new ModVersion modName:'Test', modVersion:'0.0'
it 'skips the section when missing', ->
parser._parseRawMaterials null
_.keys(parser.modVersion._items).length.should.equal 0
_.keys(modVersion._items).length.should.equal 0
it 'skips the section when empty', ->
parser._parseRawMaterials []
_.keys(parser.modVersion._items).length.should.equal 0
_.keys(modVersion._items).length.should.equal 0
it 'adds items marked as gatherable', ->
parser._parseRawMaterials ['Wool']
parser.modVersion.items['wool'].isGatherable.should.be.true
modVersion.items['wool'].isGatherable.should.be.true
it 'marks an existing item as gatherable', ->
parser.modVersion.addItem new Item name:'Wool'
parser.modVersion.items['wool'].isGatherable.should.be.false
modVersion.addItem new Item name:'Wool'
modVersion.items['wool'].isGatherable.should.be.false
parser._parseRawMaterials ['Wool']
parser.modVersion.items['wool'].isGatherable.should.be.true
modVersion.items['wool'].isGatherable.should.be.true
it 'registers the names of the items', ->
parser._parseRawMaterials ['Wool']
modVersion.names['wool'].should.equal 'Wool'
describe '_parseRecipe', ->
beforeEach -> parser.modVersion = new ModVersion modName:'Test', modVersion:'0.0'
it 'requires output to be defined', ->
parser._errorLocation = 'boat'
expect(-> parser._parseRecipe input:'wool').to.throw Error, 'boat is missing output'
test = -> parser._parseRecipe {input:'wool'}
expect(test).to.throw Error, 'boat is missing output'
it 'requires input to be defined', ->
expect(-> parser._parseRecipe output:'wool').to.throw Error, 'recipe for wool is missing input'
test = -> parser._parseRecipe {output:'wool'}
expect(test).to.throw Error, 'recipe for wool is missing input'
it 'can parse a regular recipe', ->
data =
@@ -113,20 +97,26 @@ describe 'ModVersionParser', ->
input: [[3, 'planks'], [3, 'wool']]
tools: 'crafting table'
recipe = parser._parseRecipe data
(stack.name for stack in recipe.output).should.eql ['bed']
(stack.name for stack in recipe.input).sort().should.eql ['planks', 'wool']
(item.name for item in recipe.tools).should.eql ['crafting table']
(stack.itemSlug for stack in recipe.output).should.eql ['bed']
(stack.itemSlug for stack in recipe.input).sort().should.eql ['planks', 'wool']
(stack.itemSlug for stack in recipe.tools).should.eql ['crafting_table']
it 'can parse a recipe without tools', ->
recipe = parser._parseRecipe output:'sugar', input:'sugar cane'
(stack.name for stack in recipe.output).should.eql ['sugar']
(stack.name for stack in recipe.input).sort().should.eql ['sugar cane']
(stack.name for stack in recipe.tools).should.eql []
recipe = parser._parseRecipe {output:'sugar', input:'sugar cane'}
(stack.itemSlug for stack in recipe.output).should.eql ['sugar']
(stack.itemSlug for stack in recipe.input).sort().should.eql ['sugar_cane']
(stack.itemSlug for stack in recipe.tools).should.eql []
it 'registers all names', ->
data =
output: 'Bed'
input: [[3, 'Oak Wood Planks'], [3, 'Wool']]
tools: 'Crafting Table'
parser._parseRecipe data
_.keys(modVersion.names).sort().should.eql ['bed', 'crafting_table', 'oak_wood_planks', 'wool']
describe '_parseStack', ->
beforeEach -> parser.modVersion = new ModVersion modName:'Test', modVersion:'0.0'
it 'requires the array to have at least one element', ->
parser._errorLocation = 'boat'
options = index:1, field:'output'
@@ -134,13 +124,13 @@ describe 'ModVersionParser', ->
"output element 1 for boat must have at least one element"
it 'can fill in a missing number', ->
item = parser._parseStack 'boat'
item.name.should.equal 'boat'
item.quantity.should.equal 1
stack = parser._parseStack 'boat'
stack.itemSlug.should.equal 'boat'
stack.quantity.should.equal 1
item2 = parser._parseStack ['boat']
item2.name.should.equal 'boat'
item2.quantity.should.equal 1
stack2 = parser._parseStack ['boat']
stack2.itemSlug.should.equal 'boat'
stack2.quantity.should.equal 1
it 'requires the data to start with a number', ->
parser._errorLocation = 'boat'
@@ -154,11 +144,9 @@ describe 'ModVersionParser', ->
describe '_parseStackList', ->
beforeEach -> parser.modVersion = new ModVersion modName:'Test', modVersion:'0.0'
it 'can promote a single item to a list', ->
list = parser._parseStackList 'boat'
(i.name for i in list).should.eql ['boat']
(stack.itemSlug for stack in list).should.eql ['boat']
it 'can require a list to be non-empty', ->
parser._errorLocation = 'boat'
@@ -171,4 +159,4 @@ describe 'ModVersionParser', ->
it 'can parse a non-empty list', ->
list = parser._parseStackList [[3, 'plank'], [3, 'wool']]
(i.name for i in list).sort().should.eql ['plank', 'wool']
(stack.itemSlug for stack in list).sort().should.eql ['plank', 'wool']