From 83868e2760b8c6032ec71f3ea38b1a3251c470bb Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Sat, 17 Jan 2015 11:49:07 -0800 Subject: [PATCH] Massive refactoring to clean up models * Tweak style of quantity label in output box to be more visible * Move data files for all mods under a directory for the specific version of the mod * Add a `silent` property to the base model to prevent models which shouldn't be observed from emitting events * Change the term "itemSlug" to just "slug" across the board * Change all models so that they don't require their parent in their constructors and so that they don't automatically add themselves to their parents lists * Remove a bunch of unnecessary event triggering * Tighten up access to various lists of children across all models * Refactor the guts of the V2 parser into an abstract base class so it can be used for other parsers in the future * Remove a number of unused methods --- src/css/templates/crafting_table.scss | 11 +- src/scripts/constants.coffee | 2 +- .../controllers/base_controller.coffee | 1 + .../crafting_table_controller.coffee | 4 +- .../controllers/mod_pack_controller.coffee | 14 +- .../controllers/stack_controller.coffee | 2 +- src/scripts/main.coffee | 1 + src/scripts/models/base_collection.coffee | 10 ++ src/scripts/models/base_model.coffee | 8 +- src/scripts/models/command_parser_base.coffee | 89 +++++++++++ src/scripts/models/crafting_grid.coffee | 13 +- src/scripts/models/crafting_plan.coffee | 71 +++++---- src/scripts/models/crafting_table.coffee | 2 +- src/scripts/models/inventory.coffee | 81 +++++----- src/scripts/models/item.coffee | 36 ++--- src/scripts/models/item_page.coffee | 4 +- src/scripts/models/mod.coffee | 7 +- src/scripts/models/mod_pack.coffee | 72 ++++----- src/scripts/models/mod_version.coffee | 78 ++++++---- src/scripts/models/mod_version_parser.coffee | 12 +- src/scripts/models/name_finder.coffee | 14 +- .../mod_version_parser_v1.coffee | 63 +++++--- .../mod_version_parser_v2.coffee | 141 ++++++------------ src/scripts/models/recipe.coffee | 63 ++------ src/scripts/models/stack.coffee | 24 +-- src/scripts/polyfill.coffee | 13 ++ src/templates/crafting_table.jade | 1 - test/crafting_plan.test.coffee | 10 +- test/inventory.test.coffee | 10 +- test/mod_pack.test.coffee | 66 ++------ test/mod_version.test.coffee | 36 ++--- .../mod_version_parser_v1.test.coffee | 37 +++-- .../mod_version_parser_v2.test.coffee | 52 +++---- test/test.coffee | 1 + 34 files changed, 530 insertions(+), 519 deletions(-) create mode 100644 src/scripts/models/base_collection.coffee create mode 100644 src/scripts/models/command_parser_base.coffee create mode 100644 src/scripts/polyfill.coffee diff --git a/src/css/templates/crafting_table.scss b/src/css/templates/crafting_table.scss index 5d23bc2bf..dec314c43 100644 --- a/src/css/templates/crafting_table.scss +++ b/src/css/templates/crafting_table.scss @@ -47,16 +47,11 @@ All rights reserved. } .quantity { - position: absolute; right: 0; bottom: 0; - color: black; + position: absolute; right: 0.1em; bottom: 0.1em; + color: white; font-family: PressStart; font-size: $font-size-large; - } - - .quantity + .quantity { - color: white; - right: 0.2em; bottom: 0.2em; - z-index: 1 + text-shadow: -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 2px 2px 0 #000; } } diff --git a/src/scripts/constants.coffee b/src/scripts/constants.coffee index 61da27f20..c7e6bcfbf 100644 --- a/src/scripts/constants.coffee +++ b/src/scripts/constants.coffee @@ -52,7 +52,7 @@ ModelState.loaded = 'loaded' ModelState.failed = 'failed' exports.Url = Url = {} -Url.itemIcon = _.template "/data/<%= modSlug %>/<%= modVersion %>/images/<%= itemSlug %>.png" +Url.itemIcon = _.template "/data/<%= modSlug %>/<%= modVersion %>/images/<%= slug %>.png" Url.item = _.template "/item/<%= encodeURIComponent(itemName) %>" Url.mod = _.template "/data/<%= modSlug %>/mod.cg" Url.modVersion = _.template "/data/<%= modSlug %>/<%= modVersion %>/mod-version.cg" diff --git a/src/scripts/controllers/base_controller.coffee b/src/scripts/controllers/base_controller.coffee index 134436d59..db4c5c187 100644 --- a/src/scripts/controllers/base_controller.coffee +++ b/src/scripts/controllers/base_controller.coffee @@ -74,6 +74,7 @@ module.exports = class BaseController extends Backbone.View return unless @onWillChangeModel @_model, newModel @_model = newModel + @_model.silent = false @_tryRefresh() # Backbone.View Overrides ###################################################################### diff --git a/src/scripts/controllers/crafting_table_controller.coffee b/src/scripts/controllers/crafting_table_controller.coffee index 4cb21b489..59ba056be 100644 --- a/src/scripts/controllers/crafting_table_controller.coffee +++ b/src/scripts/controllers/crafting_table_controller.coffee @@ -25,6 +25,8 @@ module.exports = class CraftingTableController extends BaseController @imageLoader = options.imageLoader @modPack = options.modPack + @model.plan.silent = false + # Event Methods ################################################################################ onNextClicked: -> @@ -79,7 +81,7 @@ module.exports = class CraftingTableController extends BaseController outputStack = @model.output if outputStack? - display = @modPack.findItemDisplay outputStack.itemSlug + display = @modPack.findItemDisplay outputStack.slug @$outputLink.attr 'href', display.itemUrl @$outputLink.attr 'title', display.itemName @$outputImg.attr 'alt', display.itemName diff --git a/src/scripts/controllers/mod_pack_controller.coffee b/src/scripts/controllers/mod_pack_controller.coffee index 682ddd38b..8c8d03f29 100644 --- a/src/scripts/controllers/mod_pack_controller.coffee +++ b/src/scripts/controllers/mod_pack_controller.coffee @@ -32,8 +32,9 @@ module.exports = class ModPackController extends BaseController onWillRender: -> for attributes in DefaultModVersions - modVersion = new ModVersion _.extend attributes, modPack:@model + modVersion = new ModVersion attributes modVersion.fetch() + @model.addModVersion modVersion onDidRender: -> @$table = @$('table') @@ -47,13 +48,14 @@ module.exports = class ModPackController extends BaseController return index = 0 - while index < Math.min @_controllers.length, @model.modVersions.length + modVersions = @model.getModVersions() + while index < Math.min @_controllers.length, modVersions.length controller = @_controllers[index] - controller.model = @model.modVersions[index] + controller.model = modVersions[index] index++ - while @_controllers.length < @model.modVersions.length - controller = new ModVersionController model:@model.modVersions[index] + while @_controllers.length < modVersions.length + controller = new ModVersionController model:modVersions[index] controller.render() @_controllers.push controller controller.$el.hide duration:0 @@ -61,7 +63,7 @@ module.exports = class ModPackController extends BaseController controller.$el.slideDown duration:Duration.normal index++ - while @_controllers.length > @model.modVersions.length + while @_controllers.length > modVersions.length controller = @_controllers.pop() controller.$el.slideUp duration:Duration.normal, complete:-> controller.$el.remove() diff --git a/src/scripts/controllers/stack_controller.coffee b/src/scripts/controllers/stack_controller.coffee index 1b71144ca..aabb6ca59 100644 --- a/src/scripts/controllers/stack_controller.coffee +++ b/src/scripts/controllers/stack_controller.coffee @@ -31,7 +31,7 @@ module.exports = class StackController extends BaseController super refresh: -> - display = @modPack.findItemDisplay @model.itemSlug + display = @modPack.findItemDisplay @model.slug @$nameField.html display.itemName @$quantityField.html @model.quantity diff --git a/src/scripts/main.coffee b/src/scripts/main.coffee index 7d07f4406..bfb92bc02 100644 --- a/src/scripts/main.coffee +++ b/src/scripts/main.coffee @@ -6,6 +6,7 @@ All rights reserved. ### require './underscore_mixins' +require './polyfill' views = require './views' FeedbackController = require './controllers/feedback_controller' diff --git a/src/scripts/models/base_collection.coffee b/src/scripts/models/base_collection.coffee new file mode 100644 index 000000000..393e1e490 --- /dev/null +++ b/src/scripts/models/base_collection.coffee @@ -0,0 +1,10 @@ +### +Crafting Guide - base_collection.coffee + +Copyright (c) 2015 by Redwood Labs +All rights reserved. +### + +######################################################################################################################## + +module.exports = class BaseCollection extends Backbone.Collection diff --git a/src/scripts/models/base_model.coffee b/src/scripts/models/base_model.coffee index d3ca6e06c..e6a174853 100644 --- a/src/scripts/models/base_model.coffee +++ b/src/scripts/models/base_model.coffee @@ -13,7 +13,7 @@ All rights reserved. module.exports = class BaseModel extends Backbone.Model constructor: (attributes={}, options={})-> - options.allowedSyncMethods = [] + options.silent ?= true super attributes, options makeGetter = (name)-> return -> @get name @@ -22,9 +22,9 @@ module.exports = class BaseModel extends Backbone.Model continue if name is 'id' Object.defineProperty this, name, get:makeGetter(name), set:makeSetter(name) - @allowedSyncMethods = options.allowedSyncMethods + @silent = options.silent + @state = ModelState.unloaded - @state = ModelState.unloaded @on 'request', => @state = ModelState.loading @on 'sync', => @state = ModelState.loaded @on 'error', => @state = ModelState.error @@ -61,6 +61,7 @@ module.exports = class BaseModel extends Backbone.Model success: (text, status, xhr)=> resolve @onLoadSucceeded text, status, xhr error: (xhr, status, error)=> reject @onLoadFailed error, status, xhr + @loading.catch -> # do nothing. prevents unhandled promise warnings return @loading parse: (text)-> @@ -70,6 +71,7 @@ module.exports = class BaseModel extends Backbone.Model throw new Error "#{@constructor.name} (#{@cid}) is not permitted to #{method}" trigger: (name)-> + return if @silent logger.trace "#{@constructor.name}.#{@cid} triggered a \"#{name}\" event" super diff --git a/src/scripts/models/command_parser_base.coffee b/src/scripts/models/command_parser_base.coffee new file mode 100644 index 000000000..ce2e85468 --- /dev/null +++ b/src/scripts/models/command_parser_base.coffee @@ -0,0 +1,89 @@ +### +Crafting Guide - command_parser_base.coffee + +Copyright (c) 2015 by Redwood Labs +All rights reserved. +### + +######################################################################################################################## + +module.exports = class CommandParserBase + + constructor: (options={})-> + if not options.model? then throw new Error 'options.model is required' + options.showAllErrors ?= false + + @_model = options.model + @_showAllErrors = options.showAllErrors + + # Class Methods ################################################################################ + + @COMMAND = /\ *([^:]*):?(.*)/ + + @COMMENT = /([^\\]?)#.*/ + + # Public Methods ############################################################################### + + parse: (text)-> + @_rawData = {} + @_lineNumber = 1 + + lines = text.split '\n' + for i in [0...lines.length] + @_lineNumber = i + 1 + commands = @_parseLine lines[i] + for command in commands + @_handleErrors @_execute, command + + @_handleErrors @_buildModel, @_rawData, @_model + return @_model + + unparse: -> + builder = new StringBuilder context:@model + @_unparseModel builder, model + return builder.toString() + + # Subclass Methods ############################################################################# + + _buildModel: (rawData, model)-> + throw new Error 'Subclasses must override this method' + + _unparseModel: (builder, model)-> + throw new Error 'Subclasses must override this method' + + # Private Methods ############################################################################## + + _execute: (command)-> + method = this["_command_#{command.name}"] + if not method? then throw new Error "Unknown command: #{command.name}" + + @_handleErrors method, command.args + + _parseLine: (line)-> + line = line.replace CommandParserBase.COMMENT, '$1' + line = line.trim() + return [] if line.length is 0 + + lineParts = (part.trim() for part in line.split(';')) + commands = [] + for linePart in lineParts + continue if linePart.length is 0 + + match = CommandParserBase.COMMAND.exec linePart + if not match? then throw new Error "Expected : , but found: \"#{linePart}\"" + + args = [] + args = (s.trim() for s in match[2].split(',')) if match[2]? + commands.push name:match[1], args:args + + return commands + + _handleErrors: (callback, args...)-> + if args.length is 1 and _.isArray(args[0]) then args = args[0] + + try + callback.apply this, args + catch e + e.message = "line #{@_lineNumber}: #{e.message}" + if not @_showAllErrors then throw e + logger.error e.message diff --git a/src/scripts/models/crafting_grid.coffee b/src/scripts/models/crafting_grid.coffee index 9426bf177..0e0fbfccb 100644 --- a/src/scripts/models/crafting_grid.coffee +++ b/src/scripts/models/crafting_grid.coffee @@ -18,16 +18,17 @@ module.exports = class CraftingGrid extends BaseModel attributes.recipe ?= null super attributes, options - Object.defineProperty @prototype, 'slotCount', get:-> CraftingGrid.SLOT_COUNT + Object.defineProperties this, + 'slotCount': { get:-> CraftingGrid.SLOT_COUNT } # Public Methods ############################################################################### - getItemDisplayAt: (index)-> - if index >= @slotCount then throw new Error "index (#{index}) must be less than #{@slotCount}" + getItemDisplayAt: (slot)-> + if slot >= @slotCount then throw new Error "slot (#{slot}) must be less than #{@slotCount}" return null unless @recipe? - itemSlug = @recipe.getItemSlugAt index - return null unless itemSlug? + slug = @recipe.getItemSlugAt slot + return null unless slug? - itemDisplay = @modPack.findItemDisplay itemSlug + itemDisplay = @modPack.findItemDisplay slug return itemDisplay diff --git a/src/scripts/models/crafting_plan.coffee b/src/scripts/models/crafting_plan.coffee index 50cbe7c50..4d2faa0c5 100644 --- a/src/scripts/models/crafting_plan.coffee +++ b/src/scripts/models/crafting_plan.coffee @@ -6,6 +6,7 @@ All rights reserved. ### BaseModel = require './base_model' +{Event} = require '../constants' Inventory = require './inventory' ######################################################################################################################## @@ -17,44 +18,50 @@ module.exports = class CraftingPlan extends BaseModel attributes.includingTools ?= false super attributes, options - @have = new Inventory - @want = new Inventory - @need = new Inventory - @result = new Inventory - @storage = options.storage + @have = new Inventory + @want = new Inventory + @need = new Inventory + @result = new Inventory - @have.on 'change', => @craft() - @want.on 'change', => @craft() - @modPack.on 'change', => @craft() + @clear silent:true - @on 'change:includingTools', => @craft() + @have.on Event.change, => @craft() + @want.on Event.change, => @craft() + @modPack.on Event.add, => @craft() - @clear() + @on Event.change + ':includingTools', => @craft() # Public Methods ############################################################################### - clear: -> + clear: (options={})-> + options.silent ?= false + @steps = [] @need.clear() @result.clear() - @trigger 'change', this + @trigger 'change', this unless options.silent return this craft: -> - @clear() + @clear silent:true + @need.silent = @result.silent = true + @result.addInventory @have @steps = {} @want.each (stack)=> - @_findSteps stack.itemSlug - @need.add stack.itemSlug, stack.quantity + @_findSteps stack.slug + @need.add stack.slug, stack.quantity @steps = _.values @steps @_resolveNeeds() @_removeExtraSteps() @result.addInventory @want + @need.silent = @result.silent = false + @need.trigger 'change', @need + @result.trigger 'change', @result @trigger 'change', this # Event Methods ################################################################################ @@ -82,10 +89,10 @@ module.exports = class CraftingPlan extends BaseModel @steps[recipe.slug] = recipe:recipe _chooseRecipe: (item)-> - return item.recipes[0] + return item.getPrimaryRecipe() - _findSteps: (itemSlug)-> - item = @modPack.findItem itemSlug + _findSteps: (slug)-> + item = @modPack.findItem slug return unless item? return unless item.isCraftable return if item.isGatherable @@ -94,16 +101,16 @@ module.exports = class CraftingPlan extends BaseModel if @includingTools for toolStack in recipe.tools - if not @_hasStep toolStack.itemSlug - @_findSteps toolStack.itemSlug + if not @_hasStep toolStack.slug + @_findSteps toolStack.slug for inputStack in recipe.input - @_findSteps inputStack.itemSlug + @_findSteps inputStack.slug @_addStep recipe - _hasStep: (itemSlug)-> - return @steps[itemSlug]? + _hasStep: (slug)-> + return @steps[slug]? _removeExtraSteps: -> result = (step for step in @steps when step.multiplier > 0) @@ -118,25 +125,25 @@ module.exports = class CraftingPlan extends BaseModel if @includingTools for stack in recipe.tools - slug = stack.itemSlug + slug = stack.slug available = @result.quantityOf(slug) + @need.quantityOf(slug) needed = Math.max 0, stack.quantity - available - @need.add stack.itemSlug, needed - @result.add stack.itemSlug, needed + @need.add stack.slug, needed + @result.add stack.slug, needed for stack in recipe.input needed = step.multiplier * stack.quantity - consumed = Math.min needed, @result.quantityOf(stack.itemSlug) + consumed = Math.min needed, @result.quantityOf(stack.slug) remaining = needed - consumed - @result.remove stack.itemSlug, consumed - @need.add stack.itemSlug, remaining + @result.remove stack.slug, consumed + @need.add stack.slug, remaining for stack in recipe.output created = stack.quantity * step.multiplier - consumed = Math.min created, @need.quantityOf stack.itemSlug + consumed = Math.min created, @need.quantityOf stack.slug remaining = created - consumed - @result.add stack.itemSlug, remaining - @need.remove stack.itemSlug, consumed + @result.add stack.slug, remaining + @need.remove stack.slug, consumed diff --git a/src/scripts/models/crafting_table.coffee b/src/scripts/models/crafting_table.coffee index 1693d9005..2caae89ee 100644 --- a/src/scripts/models/crafting_table.coffee +++ b/src/scripts/models/crafting_table.coffee @@ -81,7 +81,7 @@ module.exports = class CraftingTable extends BaseModel getToolNames: -> recipe = @plan.steps[@_step]?.recipe return '' unless recipe? - toolSlugs = (stack.itemSlug for stack in recipe.tools) + toolSlugs = (stack.slug for stack in recipe.tools) toolNames = (@modPack.findName(slug) for slug in toolSlugs).join ', ' return toolNames diff --git a/src/scripts/models/inventory.coffee b/src/scripts/models/inventory.coffee index b6124583d..a060d5d02 100644 --- a/src/scripts/models/inventory.coffee +++ b/src/scripts/models/inventory.coffee @@ -15,78 +15,80 @@ module.exports = class Inventory extends BaseModel constructor: (attributes={}, options={})-> super attributes, options - @clear() + @clear silent:true - Object.defineProperty @prototype, 'isEmpty', get:-> @_slugs.length is 0 + Object.defineProperties this, + isEmpty: { get:-> @_slugs.length is 0 } # Public Methods ############################################################################### - add: (itemSlug, quantity=1)-> - @_add itemSlug, quantity - @trigger Event.add, this, itemSlug, quantity + add: (slug, quantity=1)-> + @_add slug, quantity + @trigger Event.add, this, slug, quantity @trigger Event.change, this return this addInventory: (inventory)-> - inventory.each (stack)=> @_add stack.itemSlug, stack.quantity + @silent = true + inventory.each (stack)=> @_add stack.slug, stack.quantity + @silent = false + @trigger Event.change, this return this - clear: -> + clear: (options={})-> + options.silent ?= false @_stacks = {} @_slugs = [] - @trigger 'change', self + + @trigger Event.change, this unless options.silent clone: -> inventory = new Inventory - @each (stack)-> inventory.add stack.itemSlug, stack.quantity + inventory.addInventory this return inventory - each: (onStack)-> - for itemSlug in @_slugs - stack = @_stacks[itemSlug] - onStack stack + each: (callback)-> + for slug in @_slugs + callback @_stacks[slug] - getStack: (itemSlug)-> - return @_stacks[itemSlug] - - hasAtLeast: (itemSlug, quantity=1)-> + hasAtLeast: (slug, quantity=1)-> if quantity is 0 then return true - stack = @_stacks[itemSlug] + stack = @_stacks[slug] return false unless stack? return stack.quantity >= quantity pop: -> - itemSlug = @_slugs.pop() - return null unless itemSlug? + slug = @_slugs.pop() + return null unless slug? - stack = @_stacks[itemSlug] - delete @_stacks[itemSlug] + stack = @_stacks[slug] + delete @_stacks[slug] - @trigger Event.remove, this, stack.itemSlug, stack.quantity + @trigger Event.remove, this, stack.slug, stack.quantity @trigger Event.change, this return stack - quantityOf: (itemSlug)-> - stack = @_stacks[itemSlug] + quantityOf: (slug)-> + stack = @_stacks[slug] return 0 unless stack? return stack.quantity - remove: (itemSlug, quantity=1)-> + remove: (slug, quantity=1)-> return if quantity is 0 - stack = @_stacks[itemSlug] - if not stack? then throw new Error "cannot remove #{itemSlug} since it is not in this inventory" + stack = @_stacks[slug] + if not stack? then throw new Error "cannot remove #{slug} since it is not in this inventory" if stack.quantity < quantity - throw new Error "cannot remove #{quantity}: only #{stack.quantity} #{itemSlug} in this inventory" + throw new Error "cannot remove #{quantity}: only #{stack.quantity} #{slug} in this inventory" stack.quantity -= quantity if stack.quantity is 0 - delete @_stacks[itemSlug] - @_slugs = _(@_slugs).without itemSlug + delete @_stacks[slug] + @_slugs = _(@_slugs).without slug - @trigger Event.remove, this, itemSlug, quantity + @trigger Event.remove, this, slug, quantity @trigger Event.change, this return this @@ -94,9 +96,9 @@ module.exports = class Inventory extends BaseModel result = [] @each (stack)-> if stack.quantity > 1 - result.push [stack.quantity, stack.itemSlug] + result.push [stack.quantity, stack.slug] else - result.push stack.itemSlug + result.push stack.slug return result # Object Overrides ############################################################################# @@ -116,14 +118,15 @@ module.exports = class Inventory extends BaseModel # Private Methods ############################################################################## - _add: (itemSlug, quantity=1)-> + _add: (slug, quantity=1)-> + return unless slug? return if quantity is 0 - stack = @_stacks[itemSlug] + stack = @_stacks[slug] if not stack? - stack = new Stack itemSlug:itemSlug, quantity:quantity - @_stacks[itemSlug] = stack - @_slugs.push itemSlug + stack = new Stack slug:slug, quantity:quantity + @_stacks[slug] = stack + @_slugs.push slug @_slugs.sort() else stack.quantity += quantity diff --git a/src/scripts/models/item.coffee b/src/scripts/models/item.coffee index e504757b5..5a668450f 100644 --- a/src/scripts/models/item.coffee +++ b/src/scripts/models/item.coffee @@ -5,33 +5,38 @@ Copyright (c) 2014-2015 by Redwood Labs All rights reserved. ### -BaseModel = require './base_model' +BaseCollection = require './base_collection' +BaseModel = require './base_model' +{Event} = require '../constants' +Recipe = require './recipe' ######################################################################################################################## module.exports = class Item extends BaseModel - @DEFAULT_STACK_SIZE = 64 - constructor: (attributes={}, options={})-> if not attributes.name? then throw new Error 'attributes.name is required' - if not attributes.modVersion? then throw new Error 'attributes.modVersion is required' attributes.isGatherable ?= false - attributes.recipes ?= [] attributes.slug ?= _.slugify attributes.name - attributes.stackSize ?= Item.DEFAULT_STACK_SIZE super attributes, options - @modVersion.addItem this - - Object.defineProperty @prototype, 'isCraftable', get:-> @recipes.length > 0 + @_recipes = [] + Object.defineProperties this, + 'isCraftable': { get:-> @_recipes.length > 0 } # Public Methods ############################################################################### addRecipe: (recipe)-> - if recipe.item isnt this then throw new Error "cannot add a recipe which isn't associated with this item" - @recipes.push recipe + if recipe.slug isnt @slug then throw new Error "cannot add a recipe for #{recipe.slug} to #{@slug}" + @_recipes.push recipe + + eachRecipe: (callback)-> + for recipe in @_recipes + callback recipe + + getPrimaryRecipe: -> + return @_recipes[0] compareTo: (that)-> if this.slug isnt that.slug @@ -52,13 +57,10 @@ module.exports = class Item extends BaseModel if _.slugify(@name) isnt @slug result.push ', slug:'; result.push @slug - if @stackSize isnt Item.DEFAULT_STACK_SIZE - result.push ', stackSize:'; result.push @stackSize - if @recipes.length > 0 - result.push ', recipes:' - result.push @recipes.length - result.push ' items' + result.push ', recipes:«' + result.push @_recipes.length + result.push ' items»' result.push '}' return result.join '' diff --git a/src/scripts/models/item_page.coffee b/src/scripts/models/item_page.coffee index f5e6e6208..0ce0ebc67 100644 --- a/src/scripts/models/item_page.coffee +++ b/src/scripts/models/item_page.coffee @@ -41,7 +41,7 @@ module.exports = class ItemPage extends BaseModel _updateLocation: -> list = @plan.want.toList() if list.length is 1 - itemSlug = if _.isArray(list[0]) then list[0][1] else list[0] - router.navigate "/item/#{itemSlug}" + slug = if _.isArray(list[0]) then list[0][1] else list[0] + router.navigate "/item/#{slug}" else router.navigate "/" \ No newline at end of file diff --git a/src/scripts/models/mod.coffee b/src/scripts/models/mod.coffee index a3cfa879a..0dbc90c63 100644 --- a/src/scripts/models/mod.coffee +++ b/src/scripts/models/mod.coffee @@ -6,6 +6,7 @@ All rights reserved. ### BaseModel = require './base_model' +{Url} = require '../constants' ######################################################################################################################## @@ -20,9 +21,5 @@ module.exports = class Mod extends BaseModel parse: (response)-> - sync: (method, model, options={})-> - if method isnt 'read' then throw new Error "Mod data can only be read, not #{method}d" - super method, model, options - url: -> - return "/data/#{@slug}/mod.cg" + return Url.mod modSlug:@slug diff --git a/src/scripts/models/mod_pack.coffee b/src/scripts/models/mod_pack.coffee index 92db7d19b..6157bb27a 100644 --- a/src/scripts/models/mod_pack.coffee +++ b/src/scripts/models/mod_pack.coffee @@ -16,35 +16,18 @@ ModVersionParser = require './mod_version_parser' module.exports = class ModPack extends BaseModel constructor: (attributes={}, options={})-> - attributes.modVersions ?= [] super attributes, options + @_modVersions = [] + # Public Methods ############################################################################### - addModVersion: (modVersion)-> - if modVersion.modPack isnt this then throw new Error "the mod version must be associated with this mod pack" - return if @modVersions.indexOf(modVersion) isnt -1 - - @modVersions.push modVersion - @trigger Event.add, modVersion, this - - @modVersions.sort (a, b)-> a.compareTo b - @trigger Event.sort, this - @trigger Event.change, this - return this - - enableModsForItem: (name)-> - for modVersion in @modVersions - continue if modVersion.enabled - if modVersion.hasRecipe name - modVersion.enabled = true - - findItem: (itemSlug, options={})-> + findItem: (slug, options={})-> options.includeDisabled ?= false - for modVersion in @modVersions + for modVersion in @_modVersions continue unless modVersion.enabled or options.includeDisabled - item = modVersion.items[itemSlug] + item = modVersion.findItem slug return item if item? return null @@ -53,9 +36,9 @@ module.exports = class ModPack extends BaseModel options.includeDisabled ?= false slug = _.slugify name - for modVersion in @modVersions + for modVersion in @_modVersions continue unless modVersion.enabled or options.includeDisabled - item = modVersion.items[slug] + item = modVersion.findItem slug return item if item? return null @@ -63,12 +46,12 @@ module.exports = class ModPack extends BaseModel findName: (slug, options={})-> options.includeDisabled ?= false - for modVersion in @modVersions + for modVersion in @_modVersions continue unless modVersion.enabled or options.includeDisabled name = modVersion.findName slug return name if name - return slug + return null findItemDisplay: (slug)-> result = {} @@ -76,39 +59,50 @@ module.exports = class ModPack extends BaseModel if item? result.modSlug = item.modVersion.slug result.modVersion = item.modVersion.version - result.itemSlug = item.slug + result.slug = item.slug result.itemName = item.name else result.modSlug = _.slugify DefaultModVersions[0].name result.modVersion = DefaultModVersions[0].version - result.itemSlug = slug + result.slug = slug result.itemName = @findName slug, includeDisabled:true result.iconUrl = Url.itemIcon result result.itemUrl = Url.item result return result - hasRecipe: (name, options={})-> - options.includeDisabled ?= false - - for modVersion in @modVersions - continue unless modVersion.enabled or options.includeDisabled - return true if modVersion.hasRecipe name - - return false - isValidName: (name, options={})-> options.includeDisabled ?= false slug = _.slugify name - for modVersion in @modVersions + for modVersion in @_modVersions continue unless modVersion.enabled or options.includeDisabled name = modVersion.findName slug return true if name return false + # Property Methods ############################################################################# + + addModVersion: (modVersion)-> + return if @_modVersions.indexOf(modVersion) isnt -1 + + @_modVersions.push modVersion + @trigger Event.add, modVersion, this + + @_modVersions.sort (a, b)-> a.compareTo b + @trigger Event.change, this + + return this + + eachModVersion: (callback)-> + for modVersion in @_modVersions + callback modVersion + + getModVersions: -> + return @_modVersions[..] + # Object Overrides ############################################################################# toString: -> - return "ModPack (#{@cid}) {modVersions:#{@modVersions.length} items}" + return "ModPack (#{@cid}) {modVersions:#{@_modVersions.length} items}" diff --git a/src/scripts/models/mod_version.coffee b/src/scripts/models/mod_version.coffee index 3161a3dd4..13735d3f4 100644 --- a/src/scripts/models/mod_version.coffee +++ b/src/scripts/models/mod_version.coffee @@ -5,40 +5,38 @@ Copyright (c) 2014-2015 by Redwood Labs All rights reserved. ### -BaseModel = require './base_model' -{Event} = require '../constants' -{RequiredMods} = require '../constants' -{Url} = require '../constants' +BaseCollection = require './base_collection' +BaseModel = require './base_model' +{Event} = require '../constants' +Item = require './item' +{RequiredMods} = require '../constants' +{Url} = require '../constants' ######################################################################################################################## module.exports = class ModVersion extends BaseModel constructor: (attributes={}, options={})-> - if not attributes.modPack? then throw new Error 'attributes.modPack is required' - if _.isEmpty(attributes.name) then throw new Error 'attributes.name cannot be empty' - if _.isEmpty(attributes.version) then throw new Error 'attributes.version cannot be empty' + if not attributes.name? then throw new Error 'attributes.name is required' + if not attributes.version? then throw new Error 'attributes.version is required' attributes.description ?= '' attributes.enabled ?= true - attributes.items ?= {} - attributes.names ?= {} attributes.slug ?= _.slugify attributes.name super attributes, options - @modPack.addModVersion this + @_items = {} + @_names = {} + @_slugs = [] # Public Methods ############################################################################### addItem: (item)-> - if item.modVersion isnt this then throw new Error "cannot add item not associated with this mod version" - if @items[item.slug]? then throw new Error "duplicate item for #{item.name}" - - @items[item.slug] = item - @names[item.slug] = item.name - @trigger Event.add, item, this - @trigger Event.change, this + if @_items[item.slug]? then throw new Error "duplicate item for #{item.name}" + @_items[item.slug] = item + item.modVersion = this + @registerSlug item.slug, item.name return this compareTo: (that)-> @@ -56,26 +54,49 @@ module.exports = class ModVersion extends BaseModel else return if this.name < that.name then -1 else +1 - findName: (slug)-> - return @names[slug] + eachItem: (callback)-> + for slug in @_slugs + callback @_items[slug], slug + return this - hasRecipe: (name)-> - item = @findItemByName name - return false unless item? - return item.recipes.length > 0 + eachName: (callback)-> + for slug in @_slugs + callback @_names[slug], slug + return this + + findItem: (slug)-> + return @_items[slug] + + findItemByName: (name)-> + return @findItem _.slugify name + + findName: (slug)-> + return @_names[slug] registerSlug: (slug, name)-> - @names[slug] = name - @trigger Event.change + ':names', this, @names - @trigger Event.change, this + hasSlug = @_names[slug]? + @_names[slug] = name + + if not hasSlug + @_slugs.push slug + @_slugs.sort() + @_slugs = _.uniq @_slugs, true + return this # Backbone.Model Overrides ##################################################################### parse: (text)-> + currentSilent = @silent + @silent = true + ModVersionParser = require './mod_version_parser' # to avoid require cycles - @_parser ?= new ModVersionParser modVersion:this + @_parser ?= new ModVersionParser model:this @_parser.parse text + + @silent = currentSilent + @trigger Event.change, this + return null # prevent calling `set` url: -> @@ -88,4 +109,5 @@ module.exports = class ModVersion extends BaseModel enabled:#{@enabled}, name:#{@name}, version:#{@version}, - items:#{_.keys(@items).length} items}" + items:#{_.keys(@_items).length} items + }" diff --git a/src/scripts/models/mod_version_parser.coffee b/src/scripts/models/mod_version_parser.coffee index 570c62bb1..88ee65228 100644 --- a/src/scripts/models/mod_version_parser.coffee +++ b/src/scripts/models/mod_version_parser.coffee @@ -16,10 +16,10 @@ module.exports = class ModVersionParser @CURRENT_VERSION = '2' constructor: (options={})-> - if not options.modVersion? then throw new Error 'options.modVersion is required' + if not options.model? then throw new Error 'options.model is required' options.showAllErrors ?= false - @_modVersion = options.modVersion + @_model = options.model @_parsers = '1': new ModVersionParserV1 options '2': new ModVersionParserV2 options @@ -34,13 +34,9 @@ module.exports = class ModVersionParser parser = @_parsers['2'] if not parser? then throw new Error "cannot parse version #{data.dataVersion} mod descriptions" + parser.parse data - oldLevel = logger.level - logger.level = Logger.WARNING - result = parser.parse data - logger.level = oldLevel - - return @_modVersion + return @_model unparse: (dataVersion=ModVersionParser.CURRENT_VERSION)-> if not modVersion? then throw new Error 'modVersion is required' diff --git a/src/scripts/models/name_finder.coffee b/src/scripts/models/name_finder.coffee index b4ea08051..12e39d30e 100644 --- a/src/scripts/models/name_finder.coffee +++ b/src/scripts/models/name_finder.coffee @@ -59,19 +59,19 @@ module.exports = class NameFinder names = [] nameMap = {} - for modVersion in @modPack.modVersions - continue unless modVersion.enabled or @includeDisabledMods + @modPack.eachModVersion (modVersion)=> + return unless modVersion.enabled or @includeDisabledMods - for slug, name of modVersion.names - continue if nameMap[name] + modVersion.eachName (name, slug)=> + return if nameMap[name] - item = modVersion.items[slug] + item = modVersion.findItem slug if not @includeGatherable - continue unless item? and (not item.isGatherable) + return unless item? and (not item.isGatherable) scanName = "#{modVersion.name} : #{name}" if nameHint? - continue unless @_isMatch scanName.toLowerCase(), nameHint + return unless @_isMatch scanName.toLowerCase(), nameHint nameMap[name] = name names.push value:name, label:scanName, modVersion:modVersion diff --git a/src/scripts/models/parser_versions/mod_version_parser_v1.coffee b/src/scripts/models/parser_versions/mod_version_parser_v1.coffee index 4148cb2d4..d6d4b9c8a 100644 --- a/src/scripts/models/parser_versions/mod_version_parser_v1.coffee +++ b/src/scripts/models/parser_versions/mod_version_parser_v1.coffee @@ -15,8 +15,8 @@ Stack = require '../stack' module.exports = class ModVersionParserV1 constructor: (options={})-> - if not options.modVersion? then throw new Error 'options.modVersion is required' - @_modVersion = options.modVersion + if not options.model? then throw new Error 'options.model is required' + @_model = options.model @_errorLocation = 'the header information' parse: (data)-> @@ -27,25 +27,48 @@ module.exports = class ModVersionParserV1 # Private Methods ############################################################################## + _computeDefaultPattern: (input)-> + itemCount = input.length + slotCount = _.reduce input, ((total, stack)-> total + stack.quantity), 0 + + return '... .0. ...' if itemCount is 1 and slotCount is 1 + return '00. 00. ...' if itemCount is 1 and slotCount is 4 + return '000 000 000' if itemCount is 1 and slotCount is 9 + + result = ['.', '.', '.', '.', '.', '.', '.', '.', '.'] + indexes = [4, 7, 1, 3, 5, 6, 8, 0, 2] + + for i in [0...input.length] + stack = input[i] + for j in [0...stack.quantity] + index = indexes.shift() + result[index] = "#{i}" + + pattern = result.join '' + pattern = pattern.replace /(...)(...)(...)/, '$1 $2 $3' + return pattern + _findOrCreateItem: (name)-> - item = @_modVersion.findItemByName name + item = @_model.findItemByName name if not item? - item = new Item modVersion:@_modVersion, name:name - @_modVersion.registerSlug item.slug, item.name + item = new Item name:name + @_model.addItem item return item + # Parsing Methods ############################################################################## + _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 _.isArray(data.recipes) then throw new Error 'recipes must be an array' - if data.name isnt @_modVersion.name - throw new Error "the data is for #{data.name}, not #{@_modVersion.name} as expected" - if data.version isnt @_modVersion.version - throw new Error "the data is for version #{data.version}, not #{@_modVersion.version} as expected" + if data.name isnt @_model.name + throw new Error "the data is for #{data.name}, not #{@_model.name} as expected" + if data.version isnt @_model.version + throw new Error "the data is for version #{data.version}, not #{@_model.version} as expected" - @_modVersion.description = data.description or '' + @_model.description = data.description or '' @_parseRawMaterials data.raw_materials for index in [0...data.recipes.length] @@ -54,7 +77,7 @@ module.exports = class ModVersionParserV1 recipe = @_parseRecipe recipeData recipe._originalIndex = index - return @_modVersion + return @_model _parseRawMaterials: (data)-> return unless data? and data.length > 0 @@ -84,7 +107,7 @@ module.exports = class ModVersionParserV1 output: @_parseStackList(data.output, field:'output', canBeEmpty:false) input: @_parseStackList(data.input, field:'input', canBeEmpty:true) tools: @_parseStackList(data.tools, field:'tools', canBeEmpty:true) - attributes.pattern = data.pattern if data.pattern? + attributes.pattern = data.pattern or @_computeDefaultPattern attributes.input recipe = new Recipe attributes return recipe @@ -102,9 +125,9 @@ module.exports = class ModVersionParserV1 name = data[1] slug = _.slugify name - @_modVersion.registerSlug slug, name + @_model.registerSlug slug, name - return new Stack itemSlug:slug, quantity:data[0] + return new Stack slug:slug, quantity:data[0] _parseStackList: (data, options={})-> if not data? then throw new Error "#{@_errorLocation} must have an #{options.field} field" @@ -191,9 +214,9 @@ module.exports = class ModVersionParserV1 else if stackList.length is 1 stack = stackList[0] if stack.quantity is 1 - result.push '"' + @_modVersion.findName(stack.itemSlug) + '"' + result.push '"' + @_model.findName(stack.slug) + '"' else - result.push '[[' + stack.quantity + ', "' + @_modVersion.findName(stack.itemSlug) + '"]]' + result.push '[[' + stack.quantity + ', "' + @_model.findName(stack.slug) + '"]]' else result.push '[' @@ -202,17 +225,17 @@ module.exports = class ModVersionParserV1 stacks.sort (a, b)-> if a.quantity isnt b.quantity return if a.quantity > b.quantity then -1 else +1 - if a.itemSlug isnt b.itemSlug - return if a.itemSlug < b.itemSlug then -1 else +1 + if a.slug isnt b.slug + return if a.slug < b.slug then -1 else +1 return 0 firstItem = true for stack in stacks result.push ', ' if not firstItem if stack.quantity is 1 - result.push '"' + @_modVersion.findName(stack.itemSlug) + '"' + result.push '"' + @_model.findName(stack.slug) + '"' else - result.push '[' + stack.quantity + ', "' + @_modVersion.findName(stack.itemSlug) + '"]' + result.push '[' + stack.quantity + ', "' + @_model.findName(stack.slug) + '"]' firstItem = false result.push ']' diff --git a/src/scripts/models/parser_versions/mod_version_parser_v2.coffee b/src/scripts/models/parser_versions/mod_version_parser_v2.coffee index cfe9632f0..c5e2f721a 100644 --- a/src/scripts/models/parser_versions/mod_version_parser_v2.coffee +++ b/src/scripts/models/parser_versions/mod_version_parser_v2.coffee @@ -5,97 +5,38 @@ Copyright (c) 2014-2015 by Redwood Labs All rights reserved. ### -Item = require '../item' -ModVersion = require '../mod_version' -Recipe = require '../recipe' -Stack = require '../stack' -StringBuilder = require '../string_builder' +CommandParserBase = require '../command_parser_base' +Item = require '../item' +ModVersion = require '../mod_version' +Recipe = require '../recipe' +Stack = require '../stack' +StringBuilder = require '../string_builder' ######################################################################################################################## -module.exports = class ModVersionParserV2 - - constructor: (options={})-> - if not options.modVersion? then throw new Error 'options.modVersion is required' - options.showAllErrors ?= false - - @_modVersion = options.modVersion - @_showAllErrors = options.showAllErrors +module.exports = class ModVersionParserV2 extends CommandParserBase # Class Methods ################################################################################ - @COMMAND = /\ *([^:]*):?(.*)/ - - @COMMENT = /([^\\]?)#.*/ - @INTEGER = /[0-9]+/ @PATTERN = /^[0-9.]{3} ?[0-9.]{3} ?[0-9.]{3}$/ @STACK = /^([0-9]+) +(.*)$/ - # Public Methods ############################################################################### + # CommandParserBase Overrides ################################################################## - parse: (text)-> - @_modVersionData = {} - @_lineNumber = 1 + _buildModel: (rawData, model)-> + @_buildModVersion rawData, model - lines = text.split '\n' - for i in [0...lines.length] - @_lineNumber = i + 1 - commands = @_parseLine lines[i] - for command in commands - @_handleErrors @_execute, command - - return @_handleErrors @_buildModVersion, @_modVersionData, @_modVersion - - unparse: -> - builder = new StringBuilder context:@_modVersion - @_unparseModVersion builder - return builder.toString() - - # Private Methods ############################################################################## - - _execute: (command)-> - method = this["_command_#{command.name}"] - if not method? then throw new Error "Unknown command: #{command.name}" - - @_handleErrors method, command.args - - _parseLine: (line)-> - line = line.replace ModVersionParserV2.COMMENT, '$1' - line = line.trim() - return [] if line.length is 0 - - lineParts = (part.trim() for part in line.split(';')) - commands = [] - for linePart in lineParts - continue if linePart.length is 0 - - match = ModVersionParserV2.COMMAND.exec linePart - if not match? then throw new Error "Expected : , but found: \"#{linePart}\"" - - args = [] - args = (s.trim() for s in match[2].split(',')) if match[2]? - commands.push name:match[1], args:args - - return commands - - _handleErrors: (callback, args...)-> - if args.length is 1 and _.isArray(args[0]) then args = args[0] - - try - callback.apply this, args - catch e - e.message = "line #{@_lineNumber}: #{e.message}" - if not @_showAllErrors then throw e - logger.error e.message + _unparseModel: (builder, model)-> + @_unparseModVersion builder, model # Command Methods ############################################################################## _command_description: (descriptionParts...)-> - if @_modVersionData.description? then throw new Error 'duplicate declaration of "description"' - @_modVersionData.description = descriptionParts.join ', ' + if @_rawData.description? then throw new Error 'duplicate declaration of "description"' + @_rawData.description = descriptionParts.join ', ' _command_extras: (extraTerms...)-> if not @_recipeData? then throw new Error 'cannot declare "extras" before "recipe"' @@ -120,16 +61,16 @@ module.exports = class ModVersionParserV2 if not name.length > 0 then throw new Error 'the item name cannot be empty' @_itemData = name:name, line:@_lineNumber - @_modVersionData.items ?= [] - @_modVersionData.items.push @_itemData + @_rawData.items ?= [] + @_rawData.items.push @_itemData @_recipeData = null _command_name: (name='')-> - if @_modVersionData.name? then throw new Error 'duplicate declaration of "name"' + if @_rawData.name? then throw new Error 'duplicate declaration of "name"' if not name.length > 0 then throw new Error 'the mod name cannot be empty' - @_modVersionData.name = name + @_rawData.name = name _command_input: (inputNames...)-> if not @_recipeData? then throw new Error 'cannot declare "input" before "recipe"' @@ -175,10 +116,10 @@ module.exports = class ModVersionParserV2 @_recipeData.tools.push name _command_version: (version='')-> - if @_modVersionData.version? then throw new Error 'duplicate declaration of "version"' + if @_rawData.version? then throw new Error 'duplicate declaration of "version"' if version.length is 0 then throw new Error 'version cannot be empty' - @_modVersionData.version = version + @_rawData.version = version # Object Creation Methods ###################################################################### @@ -208,7 +149,8 @@ module.exports = class ModVersionParserV2 itemData.gatherable ?= false itemData.recipes ?= [] - item = new Item modVersion:modVersion, name:itemData.name, isGatherable:itemData.gatherable + item = new Item name:itemData.name, isGatherable:itemData.gatherable + modVersion.addItem item for recipeData in itemData.recipes @_handleErrors @_buildRecipe, modVersion, item, recipeData @@ -228,7 +170,7 @@ module.exports = class ModVersionParserV2 for name in recipeData.input slug = _.slugify name modVersion.registerSlug slug, name - inputStacks.push new Stack itemSlug:slug, quantity:0 + inputStacks.push new Stack slug:slug, quantity:0 for c in recipeData.pattern continue if c is '.' @@ -240,42 +182,43 @@ module.exports = class ModVersionParserV2 for i in [0...inputStacks.length] stack = inputStacks[i] if stack.quantity is 0 - name = modVersion.findName stack.itemSlug + name = modVersion.findName stack.slug throw new Error "#{name} is an input for this recipe, but it is not in the pattern" - outputStacks = [ new Stack itemSlug:item.slug, quantity:recipeData.quantity ] + outputStacks = [ new Stack slug:item.slug, quantity:recipeData.quantity ] for extraData in recipeData.extras slug = _.slugify extraData.name modVersion.registerSlug slug, extraData.name - outputStacks.push new Stack itemSlug:slug, quantity:extraData.quantity + outputStacks.push new Stack slug:slug, quantity:extraData.quantity toolStacks = [] for name in recipeData.tools slug = _.slugify name modVersion.registerSlug slug, name - toolStacks.push new Stack itemSlug:slug, quantity:1 + toolStacks.push new Stack slug:slug, quantity:1 attributes = - input: inputStacks - item: item - pattern: recipeData.pattern - output: outputStacks - tools: toolStacks + input: inputStacks + name: item.name + pattern: recipeData.pattern + output: outputStacks + tools: toolStacks recipe = new Recipe attributes + item.addRecipe recipe return recipe # Un-parsing Methods ########################################################################### - _unparseModVersion: (builder)-> - itemList = _.values @modVersion.items + _unparseModVersion: (builder, modVersion)-> + itemList = _.values modVersion.items itemList.sort (a, b)-> a.compareTo b builder .line 'schema: ', 2 - .line 'name: ', @modVersion.name - .line 'version: ', @modVersion.version - .onlyIf @modVersion.description?, => builder.line 'description: ', @modVersion.description + .line 'name: ', modVersion.name + .line 'version: ', modVersion.version + .onlyIf modVersion.description?, => builder.line 'description: ', modVersion.description .line() .onlyIf itemList.length > 0, => builder.loop itemList, delimiter:'\n', onEach:(b, i)=> @_unparseItem(b, i) @@ -291,13 +234,13 @@ module.exports = class ModVersionParserV2 .outdent() _unparseRecipe: (builder, recipe)-> - inputNames = (builder.context.findName(stack.itemSlug) for stack in recipe.input) + inputNames = (builder.context.findName(stack.slug) for stack in recipe.input) inputNames.sort() patternMap = {'.', '.'} for i in [0...recipe.input.length] stack = recipe.input[i] - patternMap["#{i}"] = "#{inputNames.indexOf builder.context.findName(stack.itemSlug)}" + patternMap["#{i}"] = "#{inputNames.indexOf builder.context.findName(stack.slug)}" pattern = recipe.pattern or recipe.defaultPattern newPattern = [] @@ -333,9 +276,9 @@ module.exports = class ModVersionParserV2 _unparseStackList: (builder, stackList)-> if stackList.length is 1 and stackList[0].quantity is 1 - builder.push builder.context.findName(stackList[0].itemSlug) + builder.push builder.context.findName(stackList[0].slug) else builder.loop stackList, onEach:(b, stack)=> builder .onlyIf stack.quantity > 1, => builder.push stack.quantity, ' ' - .push builder.context.findName stack.itemSlug + .push builder.context.findName stack.slug diff --git a/src/scripts/models/recipe.coffee b/src/scripts/models/recipe.coffee index 6532af3d7..91d3804f1 100644 --- a/src/scripts/models/recipe.coffee +++ b/src/scripts/models/recipe.coffee @@ -13,50 +13,36 @@ Stack = require './stack' module.exports = class Recipe extends BaseModel constructor: (attributes={}, options={})-> - if not attributes.input? then throw new Error 'attributes.input is required' - if not attributes.item? then throw new Error 'attributes.item is required' + if attributes.item? + attributes.name = attributes.item.name + attributes.slug = attributes.item.slug + attributes.output ?= [new Stack slug:item.slug, quantity:1] - attributes.output ?= [new Stack itemSlug:attributes.item.slug] + if not attributes.name? then throw new Error 'attributes.name 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' + + attributes.item ?= null + attributes.output ?= [new Stack slug:_.slugify(attributes.name), quantity:1] attributes.pattern = @_parsePattern attributes.pattern + attributes.slug ?= attributes.output[0].slug attributes.tools ?= [] super attributes, options - @item.addRecipe this - - Object.defineProperties this, - 'defaultPattern': { get: -> @_computeDefaultPattern() } - 'name': { get: -> @item.name } - 'slug': { get: -> @item.slug } - # Public Methods ############################################################################### - getItemSlugAt: (index)-> + getItemSlugAt: (patternSlot)-> pattern = if @pattern? then @pattern else @_computeDefaultPattern() trueIndex = 0:0, 1:1, 2:2, 3:4, 4:5, 5:6, 6:8, 7:9, 8:10 - patternDigit = pattern[trueIndex[index]] + patternDigit = pattern[trueIndex[patternSlot]] return null unless patternDigit? return null unless patternDigit.match /[0-9]/ stack = @input[parseInt(patternDigit)] return null unless stack? - return stack.itemSlug - - make: (inventory, missing)-> - for stack in @input - itemSlug = stack.itemSlug - needed = stack.quantity - while needed > 0 - if inventory.hasAtLeast itemSlug - inventory.remove itemSlug - else - missing.add itemSlug - - for stack in @output - inventory.add stack.itemSlug, stack.quantity - - return this + return stack.slug # Object Overrides ############################################################################# @@ -109,24 +95,3 @@ module.exports = class Recipe extends BaseModel pattern = array.join '' pattern = pattern.replace /(...)(...)(...)/, '$1 $2 $3' return pattern - - _computeDefaultPattern: -> - itemCount = @input.length - slotCount = _.reduce @input, ((total, stack)-> total + stack.quantity), 0 - - return '... .0. ...' if itemCount is 1 and slotCount is 1 - return '00. 00. ...' if itemCount is 1 and slotCount is 4 - return '000 000 000' if itemCount is 1 and slotCount is 9 - - result = ['.', '.', '.', '.', '.', '.', '.', '.', '.'] - indexes = [4, 7, 1, 3, 5, 6, 8, 0, 2] - - for i in [0...@input.length] - stack = @input[i] - for j in [0...stack.quantity] - index = indexes.shift() - result[index] = "#{i}" - - pattern = result.join '' - pattern = pattern.replace /(...)(...)(...)/, '$1 $2 $3' - return pattern diff --git a/src/scripts/models/stack.coffee b/src/scripts/models/stack.coffee index 7930706df..aab791fee 100644 --- a/src/scripts/models/stack.coffee +++ b/src/scripts/models/stack.coffee @@ -5,30 +5,18 @@ Copyright (c) 2014-2015 by Redwood Labs All rights reserved. ### +BaseModel = require './base_model' + ######################################################################################################################## -module.exports = class Stack +module.exports = class Stack extends BaseModel constructor: (attributes={})-> - if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required' + if not attributes.slug? then throw new Error 'attributes.slug is required' attributes.quantity ?= 1 - - @itemSlug = attributes.itemSlug - @quantity = attributes.quantity - - # Public Methods ############################################################################### - - canMerge: (stack)-> - return @itemSlug is stack.itemSlug - - merge: (stack)-> - if not @canMerge stack - throw new Error "this stack of #{@itemSlug} cannot merge a stack of #{@stack.itemSlug}" - - @quantity += stack.quantity - return this + super attributes # Object Overrides ############################################################################# toString: -> - return "#{@quantity} #{@itemSlug}" + return "#{@quantity} #{@slug}" diff --git a/src/scripts/polyfill.coffee b/src/scripts/polyfill.coffee new file mode 100644 index 000000000..1ae605ef5 --- /dev/null +++ b/src/scripts/polyfill.coffee @@ -0,0 +1,13 @@ +### +Crafting Table - polyfill.coffee + +Copyright (c) 2015 by Redwood Labs +All rights reserved. +### + +######################################################################################################################## + +if not Array.prototype.clear? + Object.defineProperty Array.prototype, 'clear', value:-> + @splice 0, @length + return this diff --git a/src/templates/crafting_table.jade b/src/templates/crafting_table.jade index 8a27ac949..2d8908bd2 100644 --- a/src/templates/crafting_table.jade +++ b/src/templates/crafting_table.jade @@ -18,7 +18,6 @@ .output a: img p.quantity - p.quantity p.multiplier .next .problem diff --git a/test/crafting_plan.test.coffee b/test/crafting_plan.test.coffee index 6d3982995..ff2278585 100644 --- a/test/crafting_plan.test.coffee +++ b/test/crafting_plan.test.coffee @@ -5,8 +5,9 @@ Copyright (c) 2014-2015 by Redwood Labs All rights reserved. ### -ModPack = require '../src/scripts/models/mod_pack' CraftingPlan = require '../src/scripts/models/crafting_plan' +ModVersion = require '../src/scripts/models/mod_version' +ModPack = require '../src/scripts/models/mod_pack' ######################################################################################################################## @@ -17,8 +18,8 @@ modPack = plan = null describe 'CraftingPlan', -> beforeEach -> - modPack = new ModPack - modPack.loadModVersionData """ + modVersion = new ModVersion name:'Minecraft', version:'1.7.10' + modVersion.parse """ schema:2; name:Minecraft; version:1.7.10 item:Oak Plank; recipe:; input:Oak Log; pattern:... .0. ...; quantity:4 @@ -28,6 +29,9 @@ describe 'CraftingPlan', -> item:Iron Ingot; recipe:; input:Iron Ore, furnace fuel; pattern:.0. ... .1.; tools:Furnace item:Iron Sword; recipe:; input:Iron Ingot, Stick; pattern:.0. .0. .1.; tools:Crafting Table """ + modPack = new ModPack + modPack.addModVersion modVersion + plan = new CraftingPlan modPack:modPack, includingTools:false describe 'craft', -> diff --git a/test/inventory.test.coffee b/test/inventory.test.coffee index 5ad16f78f..c49f605e2 100644 --- a/test/inventory.test.coffee +++ b/test/inventory.test.coffee @@ -19,7 +19,7 @@ inventory = null describe 'Inventory', -> beforeEach -> - inventory = new Inventory + inventory = new Inventory {}, silent:false inventory.add 'wool', 4 inventory.add 'string', 20 inventory.add 'boat' @@ -30,7 +30,7 @@ describe 'Inventory', -> inventory.add 'iron_ingot', 4 stack = inventory._stacks['iron_ingot'] stack.constructor.name.should.equal 'Stack' - stack.itemSlug.should.equal 'iron_ingot' + stack.slug.should.equal 'iron_ingot' stack.quantity.should.equal 4 it 'can augment quantity of existing items', -> @@ -80,7 +80,7 @@ describe 'Inventory', -> it 'works when items have only been added', -> result = [] - inventory.each (stack)-> result.push stack.itemSlug + inventory.each (stack)-> result.push stack.slug result.should.eql ['boat', 'string', 'wool'] it 'works when items have been augmented', -> @@ -89,7 +89,7 @@ describe 'Inventory', -> inventory.add 'wool', 2 result = [] - inventory.each (stack)-> result.push stack.itemSlug + inventory.each (stack)-> result.push stack.slug result.should.eql ['boat', 'iron_ingot', 'string', 'wool'] describe 'hasAtLeast', -> @@ -116,7 +116,7 @@ describe 'Inventory', -> it 'completely removes the last item', -> stack = inventory.pop() - stack.itemSlug.should.equal 'wool' + stack.slug.should.equal 'wool' stack.quantity.should.equal 4 inventory.toList().should.eql ['boat', [20, 'string']] diff --git a/test/mod_pack.test.coffee b/test/mod_pack.test.coffee index 6b72cfe43..8d872754c 100644 --- a/test/mod_pack.test.coffee +++ b/test/mod_pack.test.coffee @@ -19,43 +19,22 @@ describe 'ModPack', -> beforeEach -> minecraft = new ModVersion name:'Minecraft', version:'1.7.10', enabled:true - new Item modVersion:minecraft, name:'Wool' - new Item modVersion:minecraft, name:'Bed', recipes:[''] + minecraft.addItem new Item name:'Wool' + minecraft.addItem new Item name:'Bed', recipes:[''] minecraft.registerSlug 'iron_chestplate', 'Iron Chestplate' - buildcraft = new ModVersion name:'Buildcraft', version:'4.0', enabled:false - new Item modVersion:buildcraft, name:'Stone Gear', recipes:[''] - new Item modVersion:buildcraft, name:'Bed', recipes:[''] + buildcraft = new ModVersion name:'Buildcraft', version:'6.2.6', enabled:false + buildcraft.addItem new Item name:'Stone Gear', recipes:[''] + buildcraft.addItem new Item name:'Bed', recipes:[''] industrialCraft = new ModVersion name:'Industrial Craft', version:'2.0', enabled:false - new Item modVersion:industrialCraft, name:'Resin' - new Item modVersion:industrialCraft, name:'Rubber', recipes:[''] + industrialCraft.addItem new Item name:'Resin' + industrialCraft.addItem new Item name:'Rubber' - modPack = new ModPack modVersions:[minecraft, buildcraft, industrialCraft] - - describe 'enableModsForItem', -> - - it 'it ignores already-enabled mod versions', -> - buildcraft.enabled = true - modPack.enableModsForItem 'Stone Gear' - - minecraft.enabled.should.be.true - buildcraft.enabled.should.be.true - industrialCraft.enabled.should.be.false - - it 'it ignores mod versions not containing the item', -> - modPack.enableModsForItem 'Stone Gear' - - minecraft.enabled.should.be.true - buildcraft.enabled.should.be.true - industrialCraft.enabled.should.be.false - - it 'enables disabled mod versions with the item', -> - modPack.enableModsForItem 'Rubber' - - minecraft.enabled.should.be.true - buildcraft.enabled.should.be.false - industrialCraft.enabled.should.be.true + modPack = new ModPack + modPack.addModVersion minecraft + modPack.addModVersion buildcraft + modPack.addModVersion industrialCraft describe 'findItemByName', -> @@ -75,7 +54,7 @@ describe 'ModPack', -> it 'returns all data for a regular Minecraft item', -> display = modPack.findItemDisplay 'bed' - display.iconUrl.should.equal '/data/minecraft/images/bed.png' + display.iconUrl.should.equal '/data/minecraft/1.7.10/images/bed.png' display.itemUrl.should.equal '/item/Bed' display.itemName.should.equal 'Bed' display.modSlug.should.equal 'minecraft' @@ -83,7 +62,7 @@ describe 'ModPack', -> it 'returns all data for an item in an enabled mod', -> buildcraft.enabled = true display = modPack.findItemDisplay 'stone_gear' - display.iconUrl.should.equal '/data/buildcraft/images/stone_gear.png' + display.iconUrl.should.equal '/data/buildcraft/6.2.6/images/stone_gear.png' display.itemUrl.should.equal '/item/Stone%20Gear' display.itemName.should.equal 'Stone Gear' display.modSlug.should.equal 'buildcraft' @@ -94,24 +73,7 @@ describe 'ModPack', -> it 'assumes an unfound item is from Minecraft', -> display = modPack.findItemDisplay 'iron_chestplate' - display.iconUrl.should.equal '/data/minecraft/images/iron_chestplate.png' + display.iconUrl.should.equal '/data/minecraft/1.7.10/images/iron_chestplate.png' display.itemUrl.should.equal '/item/Iron%20Chestplate' display.itemName.should.equal 'Iron Chestplate' display.modSlug.should.equal 'minecraft' - - describe 'hasRecipe', -> - - it 'returns true when the item is present and has recipes', -> - modPack.hasRecipe('Bed').should.be.true - - it 'returns false when the item is not present', -> - modPack.hasRecipe('Iron Sword').should.be.false - - it 'returns false when the item does not have recipes', -> - modPack.hasRecipe('Wool').should.be.false - - it 'ignores disabled mod versions', -> - modPack.hasRecipe('Stone Gear').should.be.false - - it 'includes disabled mod versions when include disabled is requested', -> - modPack.hasRecipe('Stone Gear', includeDisabled:true).should.be.true diff --git a/test/mod_version.test.coffee b/test/mod_version.test.coffee index b4fd49b2a..a5fee6f41 100644 --- a/test/mod_version.test.coffee +++ b/test/mod_version.test.coffee @@ -21,25 +21,29 @@ describe 'ModVersion', -> describe 'constructor', -> it 'requires a mod name', -> - expect(-> new ModVersion version:'0.0').to.throw Error, 'name cannot be empty' + expect(-> new ModVersion version:'0.0').to.throw Error, 'attributes.name is required' it 'requires a mod version', -> - expect(-> new ModVersion name:'Test').to.throw Error, 'version cannot be empty' + expect(-> new ModVersion name:'Test').to.throw Error, 'attributes.version is required' it 'supplies default values', -> modVersion.description.should.equal '' - modVersion.items.should.eql {} modVersion.enabled.should.be.true + modVersion.slug.should.equal 'test' describe 'addItem', -> it 'refuses to add duplicates', -> - new Item modVersion:modVersion, name:'Wool' - expect(-> new Item modVersion:modVersion, name:'Wool').to.throw Error, 'duplicate item for Wool' + modVersion.addItem new Item name:'Wool' + expect(-> modVersion.addItem new Item name:'Wool').to.throw Error, 'duplicate item for Wool' - it 'adds an item indexes by its slug', -> - new Item modVersion:modVersion, name:'Wool' - modVersion.items.wool.name.should.equal 'Wool' + it 'adds an item indexed by its slug', -> + modVersion.addItem new Item name:'Wool' + modVersion._items.wool.name.should.equal 'Wool' + + it 'sets the modVersion', -> + modVersion.addItem new Item name:'Wool' + modVersion._items.wool.modVersion.should.equal modVersion describe 'compareTo', -> @@ -56,19 +60,5 @@ describe 'ModVersion', -> describe 'findItemByName', -> it 'locates items by slugified name', -> - new Item modVersion:modVersion, name:'Crafting Table' + modVersion.addItem new Item name:'Crafting Table' modVersion.findItemByName('Crafting Table').slug.should.equal 'crafting_table' - - describe 'hasRecipe', -> - - it 'returns false for an unknown item', -> - new Item modVersion:modVersion, name:'Oak Wood Planks', recipes:['foo'] - modVersion.hasRecipe('Pineapple Upside-Down Cake').should.be.false - - it 'returns false for a un-craftable item', -> - new Item modVersion:modVersion, name:'Wool' - modVersion.hasRecipe('Wool').should.be.false - - it 'returns true for a craftable item', -> - new Item modVersion:modVersion, name:'Oak Wood Planks', recipes:['foo'] - modVersion.hasRecipe('Oak Wood Planks').should.be.true diff --git a/test/parser_versions/mod_version_parser_v1.test.coffee b/test/parser_versions/mod_version_parser_v1.test.coffee index aa89d3d23..a42c04102 100644 --- a/test/parser_versions/mod_version_parser_v1.test.coffee +++ b/test/parser_versions/mod_version_parser_v1.test.coffee @@ -19,7 +19,7 @@ describe "ModVersionParserV1", -> beforeEach -> modVersion = new ModVersion name:'Test', version:'0.0' - parser = new ModVersionParserV1 modVersion:modVersion + parser = new ModVersionParserV1 model:modVersion describe '_parseModVersion', -> @@ -51,8 +51,7 @@ describe "ModVersionParserV1", -> modVersion = parser._parseModVersion data modVersion.name.should.equal 'Test' modVersion.version.should.equal '0.0' - slugs = (slug for slug, item of modVersion.items).sort() - slugs.should.eql ['bed', 'sugar'] + modVersion._slugs.should.eql ['bed', 'crafting_table', 'planks', 'sugar', 'sugar_cane', 'wool'] describe '_parseRawMaterials', -> @@ -66,17 +65,17 @@ describe "ModVersionParserV1", -> it 'adds items marked as gatherable', -> parser._parseRawMaterials ['Wool'] - modVersion.items['wool'].isGatherable.should.be.true + modVersion._items['wool'].isGatherable.should.be.true it 'marks an existing item as gatherable', -> - item = new Item modVersion:modVersion, name:'Wool' - modVersion.items['wool'].isGatherable.should.be.false + modVersion.addItem new Item name:'Wool' + modVersion._items['wool'].isGatherable.should.be.false parser._parseRawMaterials ['Wool'] - 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' + modVersion._names['wool'].should.equal 'Wool' describe '_parseRecipe', -> @@ -95,15 +94,15 @@ describe "ModVersionParserV1", -> input: [[3, 'planks'], [3, 'wool']] tools: 'crafting table' recipe = parser._parseRecipe data - (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'] + (stack.slug for stack in recipe.output).should.eql ['bed'] + (stack.slug for stack in recipe.input).sort().should.eql ['planks', 'wool'] + (stack.slug 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.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 [] + (stack.slug for stack in recipe.output).should.eql ['sugar'] + (stack.slug for stack in recipe.input).sort().should.eql ['sugar_cane'] + (stack.slug for stack in recipe.tools).should.eql [] it 'registers all names', -> data = @@ -111,7 +110,7 @@ describe "ModVersionParserV1", -> 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'] + modVersion._slugs.should.eql ['bed', 'crafting_table', 'oak_wood_planks', 'wool'] describe '_parseStack', -> @@ -123,11 +122,11 @@ describe "ModVersionParserV1", -> it 'can fill in a missing number', -> stack = parser._parseStack 'boat' - stack.itemSlug.should.equal 'boat' + stack.slug.should.equal 'boat' stack.quantity.should.equal 1 stack2 = parser._parseStack ['boat'] - stack2.itemSlug.should.equal 'boat' + stack2.slug.should.equal 'boat' stack2.quantity.should.equal 1 it 'requires the data to start with a number', -> @@ -144,7 +143,7 @@ describe "ModVersionParserV1", -> it 'can promote a single item to a list', -> list = parser._parseStackList 'boat' - (stack.itemSlug for stack in list).should.eql ['boat'] + (stack.slug for stack in list).should.eql ['boat'] it 'can require a list to be non-empty', -> parser._errorLocation = 'boat' @@ -157,4 +156,4 @@ describe "ModVersionParserV1", -> it 'can parse a non-empty list', -> list = parser._parseStackList [[3, 'plank'], [3, 'wool']] - (stack.itemSlug for stack in list).sort().should.eql ['plank', 'wool'] + (stack.slug for stack in list).sort().should.eql ['plank', 'wool'] diff --git a/test/parser_versions/mod_version_parser_v2.test.coffee b/test/parser_versions/mod_version_parser_v2.test.coffee index 858d0af7b..c39465f90 100644 --- a/test/parser_versions/mod_version_parser_v2.test.coffee +++ b/test/parser_versions/mod_version_parser_v2.test.coffee @@ -18,7 +18,7 @@ describe 'ModVersionParserV2', -> beforeEach -> modVersion = new ModVersion name:'Test', version:'0.0' - parser = new ModVersionParserV2 modVersion:modVersion + parser = new ModVersionParserV2 model:modVersion describe 'Item', -> @@ -29,15 +29,15 @@ describe 'ModVersionParserV2', -> recipe:; input:Alpha; pattern:... .0. ...; recipe:; input:Bravo; pattern:... 0.0 ...;" modVersion = parser.parse baseText + recipes - recipes = modVersion.items.charlie.recipes - recipes[0].input[0].itemSlug.should.equal 'alpha' - recipes[1].input[0].itemSlug.should.equal 'bravo' + recipes = modVersion._items.charlie._recipes + recipes[0].input[0].slug.should.equal 'alpha' + recipes[1].input[0].slug.should.equal 'bravo' describe 'name', -> it 'adds the name when present', -> modVersion = parser.parse baseText + 'item: Charlie' - modVersion.items.charlie.name.should.equal 'Charlie' + modVersion._items.charlie.name.should.equal 'Charlie' it 'requires a non-empty name', -> func = -> parser.parse baseText + 'item: \n' @@ -47,7 +47,7 @@ describe 'ModVersionParserV2', -> it 'adds "gatherable" when present', -> modVersion = parser.parse baseText + 'item: Alpha Bravo; gatherable: yes' - modVersion.items.alpha_bravo.isGatherable.should.be.true + modVersion._items.alpha_bravo.isGatherable.should.be.true it 'does not allow a duplicate "gatherable" declaration', -> func = -> parser.parse baseText + 'item: Alpha Bravo; gatherable: yes; gatherable: yes' @@ -67,7 +67,7 @@ describe 'ModVersionParserV2', -> modVersion = parser.parse 'item: Alpha; version:0.0; name:Test' modVersion.name.should.equal 'Test' modVersion.version.should.equal '0.0' - modVersion.items.alpha.name.should.equal 'Alpha' + modVersion._items.alpha.name.should.equal 'Alpha' it 'does not allow duplicate item declarations', -> func = -> parser.parse 'version:0.0; name:Test; item:Charlie; item:Charlie' @@ -75,7 +75,7 @@ describe 'ModVersionParserV2', -> it 'allows multiple items', -> modVersion = parser.parse 'name:Test; version:0.0; item:Bravo; item:Charlie' - _.keys(modVersion.items).sort().should.eql ['bravo', 'charlie'] + _.keys(modVersion._items).sort().should.eql ['bravo', 'charlie'] describe 'name', -> @@ -123,7 +123,7 @@ describe 'ModVersionParserV2', -> it 'adds "input" when present', -> modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo, Charlie; pattern: ... 012 ...' - slugs = (s.itemSlug for s in modVersion.items.charlie.recipes[0].input) + slugs = (s.slug for s in modVersion._items.charlie._recipes[0].input) slugs.should.eql ['alpha', 'bravo', 'charlie'] it 'requires an "input" declaration', -> @@ -140,13 +140,13 @@ describe 'ModVersionParserV2', -> it 'registers slugs for each input name', -> modVersion = parser.parse baseText + 'recipe:; input:Delta, Echo, Foxtrot; pattern:...012...' - _.keys(modVersion.names).sort().should.eql ['charlie', 'delta', 'echo', 'foxtrot'] + modVersion._slugs.should.eql ['charlie', 'delta', 'echo', 'foxtrot'] describe 'pattern', -> it 'adds "pattern" when present', -> modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern:... .0. .1.' - modVersion.items.charlie.recipes[0].pattern.should.equal '... .0. .1.' + modVersion._items.charlie._recipes[0].pattern.should.equal '... .0. .1.' it 'requires a "pattern" declaration', -> func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo' @@ -174,7 +174,7 @@ describe 'ModVersionParserV2', -> it 'computes the input stack sizes from the pattern', -> modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo, Charlie; pattern:111 .0. 2.2' - recipe = modVersion.items.charlie.recipes[0] + recipe = modVersion._items.charlie._recipes[0] recipe.input[0].quantity.should.equal 1 recipe.input[1].quantity.should.equal 3 recipe.input[2].quantity.should.equal 2 @@ -190,7 +190,7 @@ describe 'ModVersionParserV2', -> it 'adds "quantity" when present', -> modVersion = parser.parse baseText + 'quantity: 2' - modVersion.items.charlie.recipes[0].output[0].quantity.should.equal 2 + modVersion._items.charlie._recipes[0].output[0].quantity.should.equal 2 it 'does not allow a duplicate "quantity" declaration', -> func = -> parser.parse baseText + 'quantity:1; quantity:2' @@ -202,7 +202,7 @@ describe 'ModVersionParserV2', -> it 'assumes a quantity of 1 by default', -> modVersion = parser.parse baseText - modVersion.items.charlie.recipes[0].output[0].quantity.should.equal 1 + modVersion._items.charlie._recipes[0].output[0].quantity.should.equal 1 it 'does not allow "quantity" before recipe', -> func = -> parser.parse 'name:Alpha; version:1; item:Bravo; quantity:12; recipe:;' @@ -215,18 +215,18 @@ describe 'ModVersionParserV2', -> it 'adds a single item as the default output', -> modVersion = parser.parse baseText - stack = modVersion.items.bravo.recipes[0].output[0] - stack.itemSlug.should.equal 'bravo' + stack = modVersion._items.bravo._recipes[0].output[0] + stack.slug.should.equal 'bravo' stack.quantity.should.equal 1 it 'can add multiple extras with quantities', -> modVersion = parser.parse baseText + 'extras:2 Delta, 4 Echo' - output = modVersion.items.bravo.recipes[0].output - output[0].itemSlug.should.equal 'bravo' + output = modVersion._items.bravo._recipes[0].output + output[0].slug.should.equal 'bravo' output[0].quantity.should.equal 1 - output[1].itemSlug.should.equal 'delta' + output[1].slug.should.equal 'delta' output[1].quantity.should.equal 2 - output[2].itemSlug.should.equal 'echo' + output[2].slug.should.equal 'echo' output[2].quantity.should.equal 4 it 'does not allow "extras" before "recipe"', -> @@ -235,7 +235,7 @@ describe 'ModVersionParserV2', -> it 'registers slugs for each output name', -> modVersion = parser.parse baseText + 'extras:Delta, Echo' - _.keys(modVersion.names).sort().should.eql ['bravo', 'charlie', 'delta', 'echo'] + modVersion._slugs.should.eql ['bravo', 'charlie', 'delta', 'echo'] it 'does not allow a duplicate "extras" declaration', -> func = -> parser.parse baseText + 'extras:Echo; extras:Delta' @@ -248,17 +248,17 @@ describe 'ModVersionParserV2', -> it 'can add a single tool', -> modVersion = parser.parse baseText + 'tools: Furnace' - modVersion.items.bravo.recipes[0].tools[0].itemSlug.should.equal 'furnace' + modVersion._items.bravo._recipes[0].tools[0].slug.should.equal 'furnace' it 'can add multiple tools', -> modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace' - tools = modVersion.items.bravo.recipes[0].tools - tools[0].itemSlug.should.equal 'crafting_table' - tools[1].itemSlug.should.equal 'furnace' + tools = modVersion._items.bravo._recipes[0].tools + tools[0].slug.should.equal 'crafting_table' + tools[1].slug.should.equal 'furnace' it 'registers slugs for each tool name', -> modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace' - _.keys(modVersion.names).sort().should.eql ['bravo', 'charlie', 'crafting_table', 'furnace'] + modVersion._slugs.should.eql ['bravo', 'charlie', 'crafting_table', 'furnace'] it 'does not allow a duplicate "tools" declaration', -> func = -> parser.parse baseText + 'tools:Crafting Table; tools:Furnace' diff --git a/test/test.coffee b/test/test.coffee index 8176dd72b..70dc883f8 100644 --- a/test/test.coffee +++ b/test/test.coffee @@ -21,6 +21,7 @@ global.util = require 'util' Logger = require '../src/scripts/logger' global.logger = new Logger level:Logger.TRACE +require '../src/scripts/polyfill' require '../src/scripts/underscore_mixins' # Test Registry ########################################################################################################