From 7377559f7bbc2a607fd3ea4a08f8fb6aaa10bcd9 Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Sun, 25 Sep 2016 13:14:42 -0700 Subject: [PATCH 1/5] Initial take on crafting algo Mk.III --- Gruntfile.coffee | 6 +- scripts/clear_buffer | 3 + .../models/crafting/crafting_node.coffee | 134 -------- .../models/crafting/crafting_node.test.coffee | 44 --- .../models/crafting/crafting_plan.coffee | 258 +++++++-------- .../models/crafting/crafting_plan.test.coffee | 168 ++++------ .../models/crafting/crafting_plan_step.coffee | 39 +++ .../models/crafting/crafting_step.coffee | 73 ---- src/client/models/crafting/craftsman.coffee | 150 --------- src/client/models/crafting/evaluation.coffee | 112 +++++++ src/client/models/crafting/evaluator.coffee | 102 ++++++ src/client/models/crafting/fixtures.coffee | 313 ++++++++++++++++++ .../models/crafting/fixtures.test.coffee | 189 ----------- .../models/crafting/graph_builder.coffee | 72 ---- .../models/crafting/graph_builder.test.coffee | 77 ----- src/client/models/crafting/inventory.coffee | 90 +++++ .../models/crafting/inventory_node.coffee | 55 --- src/client/models/crafting/item.coffee | 93 ++++++ src/client/models/crafting/item_node.coffee | 108 ------ src/client/models/crafting/mod.coffee | 62 ++++ src/client/models/crafting/mod_pack.coffee | 52 +++ .../models/crafting/plan_builder.coffee | 151 ++++----- .../models/crafting/plan_builder.test.coffee | 163 +++++++-- .../models/crafting/plan_evaluator.coffee | 98 ------ .../crafting/plan_evaluator.test.coffee | 80 ----- src/client/models/crafting/recipe.coffee | 148 +++++++++ src/client/models/crafting/recipe_node.coffee | 87 ----- .../crafting/resources_evaluator.coffee | 45 +++ .../crafting/resources_evaluator.test.coffee | 125 +++++++ .../models/crafting/simple_inventory.coffee | 212 ------------ .../models/crafting/simple_stack.coffee | 33 -- src/client/models/crafting/stack.coffee | 42 +++ .../models/crafting/steps_evaluator.coffee | 43 +++ src/client/models/game/mod_version.coffee | 2 +- .../models/game/mod_version.test.coffee | 7 +- 35 files changed, 1660 insertions(+), 1776 deletions(-) create mode 100755 scripts/clear_buffer delete mode 100644 src/client/models/crafting/crafting_node.coffee delete mode 100644 src/client/models/crafting/crafting_node.test.coffee create mode 100644 src/client/models/crafting/crafting_plan_step.coffee delete mode 100644 src/client/models/crafting/crafting_step.coffee delete mode 100644 src/client/models/crafting/craftsman.coffee create mode 100644 src/client/models/crafting/evaluation.coffee create mode 100644 src/client/models/crafting/evaluator.coffee create mode 100644 src/client/models/crafting/fixtures.coffee delete mode 100644 src/client/models/crafting/fixtures.test.coffee delete mode 100644 src/client/models/crafting/graph_builder.coffee delete mode 100644 src/client/models/crafting/graph_builder.test.coffee create mode 100644 src/client/models/crafting/inventory.coffee delete mode 100644 src/client/models/crafting/inventory_node.coffee create mode 100644 src/client/models/crafting/item.coffee delete mode 100644 src/client/models/crafting/item_node.coffee create mode 100644 src/client/models/crafting/mod.coffee create mode 100644 src/client/models/crafting/mod_pack.coffee delete mode 100644 src/client/models/crafting/plan_evaluator.coffee delete mode 100644 src/client/models/crafting/plan_evaluator.test.coffee create mode 100644 src/client/models/crafting/recipe.coffee delete mode 100644 src/client/models/crafting/recipe_node.coffee create mode 100644 src/client/models/crafting/resources_evaluator.coffee create mode 100644 src/client/models/crafting/resources_evaluator.test.coffee delete mode 100644 src/client/models/crafting/simple_inventory.coffee delete mode 100644 src/client/models/crafting/simple_stack.coffee create mode 100644 src/client/models/crafting/stack.coffee create mode 100644 src/client/models/crafting/steps_evaluator.coffee diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 87946c40b..42cc2b10b 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -132,7 +132,7 @@ module.exports = (grunt)-> options: bail: true color: true - reporter: 'dot' + reporter: 'list' require: [ 'coffee-script/register' './src/test_helper.coffee' @@ -188,7 +188,7 @@ module.exports = (grunt)-> tasks: ['sass'] test: files: ['./src/**/*.coffee', './src/**/*.js', './test/**/*.coffee'] - tasks: ['test'] + tasks: ['script:clear', 'test'] # Compound Tasks ################################################################################################### @@ -277,7 +277,7 @@ module.exports = (grunt)-> grunt.registerTask 'script:clear', "clear the current terminal buffer", -> done = this.async() - grunt.util.spawn cmd:'clear', opts:{stdio:'inherit'}, (error)-> done(error) + grunt.util.spawn cmd:'./scripts/clear_buffer', opts:{stdio:'inherit'}, (error)-> done(error) grunt.registerTask 'script:deploy:prod', "deploy code by copying to the production branch", -> done = this.async() diff --git a/scripts/clear_buffer b/scripts/clear_buffer new file mode 100755 index 000000000..31cff086e --- /dev/null +++ b/scripts/clear_buffer @@ -0,0 +1,3 @@ +#!/bin/bash + +clear && printf '\e[3J' \ No newline at end of file diff --git a/src/client/models/crafting/crafting_node.coffee b/src/client/models/crafting/crafting_node.coffee deleted file mode 100644 index a54ec08cc..000000000 --- a/src/client/models/crafting/crafting_node.coffee +++ /dev/null @@ -1,134 +0,0 @@ -# -# Crafting Guide - crafting_node.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class CraftingNode - - @::TYPES = - INVENTORY: 0 - ITEM: 1 - RECIPE: 2 - - @::TYPE = null - - constructor: (options={})-> - if not options.modPack? then throw new Error 'options.modPack is required' - - @modPack = options.modPack - - @_children = [] - @_complete = null - @_id = _.uniqueId 'node_' - @_rotations = 0 - @_valid = null - - # Public Methods ############################################################################### - - expand: (queue=[])-> - return unless @valid - return queue if @children.length > 0 - - for child in @_createChildren() - child.parent = this - @_children.push child - queue.push child - return queue - - acceptVisitor: (visitor)-> - enter = visitor[@ENTER_METHOD] - if enter? - enter.call visitor, this - else - enter = visitor['onEnterOtherNode'] - if enter? then enter.call visitor, this - - for child in @_children - child.acceptVisitor visitor - - leave = visitor[@LEAVE_METHOD] - if leave? - leave.call visitor, this - else - leave = visitor['onLeaveOtherNode'] - if leave? then leave.call visitor, this - - pruneInvalidChildren: -> - @_pruneInvalidChildren() - - removeChild: (index)-> - @_children.splice index, 1 - - rotateChildren: -> - @_children.push @_children.shift() - @_rotations += 1 - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - children: - get: -> return @_children - - complete: - get: -> - if not @_complete? - @_complete = @_checkCompleteness() - return @_complete - - completeText: - get: -> if @complete then "◼︎" else "◻︎" - - depth: - get: -> - maxDepth = 1 - for child in @children - maxDepth = Math.max maxDepth, child.depth + 1 - return maxDepth - - id: - get: -> return @_id - - rotations: - get: -> return @_rotations - - size: - get: -> - size = 1 - for child in @children - size += child.size - return size - - valid: - get: -> @_checkValidity() - - validText: - get: -> if @valid then "✓" else "✗" - - - # Virtual Methods ############################################################################## - - # Subclasses must override this method to create such children as are appropriate for that kind of node. The new - # nodes should be appended to the given array. - _createChildren: (result)-> - throw new Error "#{@constructor.name} must override the _createChildren method" - - # Subclasses must override this method to indicate whether the node has at least one "complete" subtree rooted with - # itself. Completeness may be defined differently by different subclasses. - _checkCompleteness: -> - throw new Error "#{@constructor.name} must override the _checkCompletness method" - - # Subclasses must override this method to determine whether the subtree represented by a node should still be - # explored for having a useful crafting plan. - _checkValidity: -> - throw new Error "#{@constructor.name} must override the _checkValidity method" - - # Subclasses may override this method to remove any children determined to be invalid and therefore unable to be - # of any use in finding legitimate crafting plans. - _pruneInvalidChildren: -> - for child in @children - child.pruneInvalidChildren() diff --git a/src/client/models/crafting/crafting_node.test.coffee b/src/client/models/crafting/crafting_node.test.coffee deleted file mode 100644 index 189e719f5..000000000 --- a/src/client/models/crafting/crafting_node.test.coffee +++ /dev/null @@ -1,44 +0,0 @@ -# -# Crafting Guide - crafting_node.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -fixtures = require './fixtures.test' - -######################################################################################################################## - -root = null - -######################################################################################################################## - -describe 'crafting_node.coffee', -> - - describe 'acceptVisitor', -> - - it 'can provide a prefix, depth-first traversal', -> - root = fixtures.makeTree 'test__iron_ingot' - - visitor = - nodes: [] - onEnterOtherNode: (node)-> visitor.nodes.push node - - root.acceptVisitor visitor - - (v.constructor.name.replace('Node', '') for v in visitor.nodes).should.eql [ - "Inventory", "Item", "Recipe", "Item", "Item", "Recipe", "Item", "Item", "Recipe", "Item", "Item" - ] - - it 'can provide a postfix, depth-first traversal', -> - root = fixtures.makeTree 'test__iron_ingot' - - visitor = - nodes: [] - onLeaveOtherNode: (node)-> visitor.nodes.push node - - root.acceptVisitor visitor - - (n.constructor.name.replace('Node', '') for n in visitor.nodes).should.eql [ - "Item", "Item", "Item", "Recipe", "Item", "Recipe", "Item", "Item", "Recipe", "Item", "Inventory" - ] diff --git a/src/client/models/crafting/crafting_plan.coffee b/src/client/models/crafting/crafting_plan.coffee index 75535f14a..98f791f49 100644 --- a/src/client/models/crafting/crafting_plan.coffee +++ b/src/client/models/crafting/crafting_plan.coffee @@ -5,174 +5,140 @@ # All rights reserved. # -SimpleInventory = require './simple_inventory' +Inventory = require "./inventory" +{StringBuilder} = require "crafting-guide-common" ######################################################################################################################## module.exports = class CraftingPlan - constructor: (modPack, want, have, steps)-> - if not modPack? then throw new Error 'modPack is required' - if not want? then throw new Error 'want is required' - if not have? then throw new Error 'have is required' - if not steps? then throw new Error 'steps is required' + constructor: (attributes={})-> + @_id = _.uniqueId "crafting-plan-" + @_make = null + @_need = null + @have = attributes.have + @steps = attributes.steps + @want = attributes.want - @_have = have - @_made = null - @_modPack = modPack - @_need = null - @_rawScores = {} - @_scores = {} - @_steps = steps - @_tools = null - @_want = want + @_consolidateSteps() + @_computeResources() - @_numberSteps() - - # Public Methods ############################################################################### - - computeRequired: -> - @_need = new SimpleInventory modPack:@_modPack - @_need.addInventory @_want - - @_made = new SimpleInventory modPack:@_modPack - @_made.addInventory @_have - - @_tools = new SimpleInventory modPack:@_modPack - - for i in [@_steps.length-1..0] by -1 - step = @_steps[i] - step.multiplier = 0 - recipe = step.recipe - - for stack in step.recipe.output - continue if recipe.isPassThroughFor stack.itemSlug - qualifiedSlug = @_modPack.qualifySlug stack.itemSlug - - while @_need.quantityOf(qualifiedSlug) > 0 - @_executeStep step - - if step.multiplier > 0 - for stack in step.recipe.output - qualifiedSlug = @_modPack.qualifySlug stack.itemSlug - continue unless recipe.isPassThroughFor stack.itemSlug - continue if @_made.hasAtLeast qualifiedSlug, 1 - continue if @_need.hasAtLeast qualifiedSlug, 1 - continue if @_tools.hasAtLeast qualifiedSlug, 1 - @_need.add qualifiedSlug, 1 - @_tools.add qualifiedSlug, 1 - - @_made.addInventory @_want - @_made.addInventory @_tools - @_pruneEmptySteps() - @_numberSteps() - - hasRawScore: (name)-> - return @_rawScores[name]? - - getRawScore: (name)-> - return @_rawScores[name] - - setRawScore: (name, rawScore)-> - @_rawScores[name] = rawScore - - hasScore: (name)-> - return @_scores[name]? - - getScore: (name)-> - return @_scores[name] - - setScore: (name, score)-> - @_scores[name] = score - - # Property Methods ############################################################################# + # Properties ################################################################################### Object.defineProperties @prototype, - have: - get: -> @_have - length: - get: -> @steps.length - made: - get: -> @_made - need: - get: -> @_need - steps: - get: -> @_steps - want: - get: -> @_want + + have: # an Inventory specifying what the player already has + get: -> return @_have + set: (have)-> + have ?= new Inventory + if @_have is have then return + if @_have? then throw new Error "have cannot be reassigned" + @_have = new Inventory have + + id: # a string uniquely specifying this crafting plan + get: -> return @_id + set: -> throw new Error "id is not assignabled" + + make: # an Inventory specifying what the results of executing the plan will be + get: -> return @_make + set: -> throw new Error "make cannot be assigned" + + need: # an Inventory specifying what the player will need to gather before executing the plan + get: -> return @_need + set: -> throw new Error "need cannot be assigned" + + want: # an Inventory specifying what the player wants to make + get: -> return @_want + set: (want)-> + if not want? then throw new Error "want is required" + if want.isEmpty then throw new Error "want cannot be empty" + if @_want is want then return + if @_want? then throw new Error "want cannot be reassigned" + @_want = new Inventory want + + steps: # an array of CraftingPlanSteps detailing the steps of the plan + get: -> return @_steps + set: (steps)-> + steps ?= [] + if steps is @_steps then return + if @_steps? then throw new Error "steps cannot be reassigned" + @_steps = steps # Object Overrides ############################################################################# - toString: -> - result = ["To Make:"] - @_want.each (stack)-> - result.push " #{stack}" + toString: (options={})-> + options.full ?= false - result.push "When you already have:" - @_have.each (stack)-> - result.push " #{stack}" + if options.full + b = new StringBuilder + b.line "CraftingPlan<", @_id, ">" + b.indent() + b.line "have: ", @_have.toString full:true + b.line "want: ", @_want.toString full:true + b.line "need: ", @_need.toString full:true + b.line "steps:" + b.indent() + b.loop @_steps, delimiter:"\n", onEach:(b, step)-> + b.push step.count, " × ", step.recipe.toString(full:true) + b.line() + b.outdent() + b.line "make: ", @_make.toString full:true + b.outdent() - result.push "Start with:" - if @_need? - @_need.each (stack)-> - result.push " #{stack}" - - result.push "Use these recipes:" - for step in @_steps - result.push " #{step}" - - result.push "To produce:" - if @_made? - @_made.each (stack)-> - result.push " #{stack}" - - if _.keys(@_rawScores).length > 0 - result.push "Scores:" - for criteria, score of @_rawScores - if @_scores[criteria]? - result.push " #{criteria}: #{score} (#{@_scores[criteria]})" - else - result.push " #{criteria}: #{score}" - - return result.join '\n' + return b.toString() + else + return "CraftingPlan:#{@_make.toString(full:true)}<#{@_id}>" # Private Methods ############################################################################## - _executeStep: (step)-> - step.multiplier += 1 - recipe = step.recipe + _computeResources: -> + need = new Inventory @_want + make = new Inventory @_have + steps = @_steps[..].reverse() - for stack in recipe.input - qualifiedSlug = @_modPack.qualifySlug stack.itemSlug + for step in steps + step.count = 0 - available = @_made.quantityOf qualifiedSlug - required = recipe.getQuantityRequired stack.itemSlug - consumed = Math.min required, available - deficit = required - consumed + for productStack in step.recipe.allProducts + continue unless need.contains productStack.item + productCount = Math.ceil need.getQuantity(productStack.item) / productStack.quantity + step.count = Math.max step.count, productCount - @_made.remove qualifiedSlug, consumed - @_need.add qualifiedSlug, deficit + continue unless step.count > 0 - for stack in recipe.output - qualifiedSlug = @_modPack.qualifySlug stack.itemSlug + for itemId, item of step.recipe.inputs + amountNeeded = step.count * step.recipe.computeQuantityRequired item + amountAvailable = make.getQuantity item + amountUsed = Math.min amountAvailable, amountNeeded + amountMissing = amountNeeded - amountUsed - deficit = @_need.quantityOf qualifiedSlug - created = recipe.getQuantityProduced stack.itemSlug - replenished = Math.min deficit, created - surplus = created - replenished + make.remove item, amountUsed + need.add item, amountMissing - @_made.add qualifiedSlug, surplus - @_need.remove qualifiedSlug, replenished + for productStack in step.recipe.allProducts + amountCreated = step.count * productStack.quantity + amountNeeded = need.getQuantity productStack.item + amountFulfilled = Math.min amountNeeded, amountCreated + amountSurplus = amountCreated - amountFulfilled - _numberSteps: -> - for step, i in @_steps - step.number = i + 1 + need.remove productStack.item, amountFulfilled + make.add productStack.item, amountSurplus - _pruneEmptySteps: -> - index = 0 - while index < @_steps.length - step = @_steps[index] - if step.multiplier is 0 - @_steps.splice index, 1 - else - index++ + make.merge @_want + + @_make = make + @_need = need + + _consolidateSteps: -> + steps = [] + stepsByRecipeId = {} + + for step in @_steps + priorStep = stepsByRecipeId[step.recipe.id] + if priorStep? + priorStep.count += step.count + else if step.count > 0 + steps.push step + stepsByRecipeId[step.recipe.id] = step + + @_steps = steps diff --git a/src/client/models/crafting/crafting_plan.test.coffee b/src/client/models/crafting/crafting_plan.test.coffee index d1e696ff8..83249514e 100644 --- a/src/client/models/crafting/crafting_plan.test.coffee +++ b/src/client/models/crafting/crafting_plan.test.coffee @@ -5,119 +5,95 @@ # All rights reserved. # -CraftingPlan = require './crafting_plan' -fixtures = require './fixtures.test' -ItemSlug = require '../game/item_slug' +CraftingPlan = require './crafting_plan' +CraftingPlanStep = require './crafting_plan_step' +Inventory = require './inventory' +fixtures = require './fixtures' ######################################################################################################################## -describe 'crafting_plan.coffee', -> +describe "CraftingPlan", -> - it 'requires wanted item if gatherable', -> - plans = fixtures.makePlans [1, 'test__coal'] - plans.length.should.equal 1 + beforeEach -> + @mod = fixtures.createMod() + @want = new Inventory + @have = new Inventory - plan = plans[0] - plan.computeRequired() - plan.need.unparse().should.equal 'coal' - plan.made.unparse().should.equal 'coal' + describe "with a single step plan", -> - it 'can compute a single item with one single step plan', -> - plans = fixtures.makePlans [1, 'test__charcoal'] - plans.length.should.equal 1 + beforeEach -> + @oakPlank = fixtures.configureOakPlank @mod + @want.add @oakPlank, 11 - plan = plans[0] - plan.computeRequired() - plan.need.unparse().should.equal 'coal:8.oak_wood' - plan.made.unparse().should.equal '8.charcoal' - (s.toString() for s in plan.steps).should.eql ['1x 8 test__oak_wood,test__coal>.0. ... .1.>>8 test__charcoal'] + @plan = new CraftingPlan want:@want, steps:[ + new CraftingPlanStep @oakPlank.firstRecipe + ] - it 'can compute a large quantity of a single item with one single step plan', -> - plans = fixtures.makePlans [15, 'test__charcoal'] - plans.length.should.equal 1 + it "correctly computes the step counts", -> + @plan.steps[0].count.should.equal 3 - plan = plans[0] - plan.computeRequired() - plan.need.unparse().should.equal '2.coal:16.oak_wood' - plan.made.unparse().should.equal '16.charcoal' - (s.toString() for s in plan.steps).should.eql ['2x 8 test__oak_wood,test__coal>.0. ... .1.>>8 test__charcoal'] + it "correctly determines the inputs needed", -> + @plan.need.toString(full:true).should.equal "3 Oak Wood" - it 'can compute a single item with multiple plans', -> - plans = fixtures.makePlans [1, 'test__iron_ingot'] - plans.length.should.equal 2 + it "correctly computes the products created", -> + @plan.make.toString(full:true).should.equal "12 Oak Planks" - plan = plans[0] - plan.computeRequired() - plan.need.unparse().should.equal 'coal:8.iron_ore:8.oak_wood' - plan.made.unparse().should.equal '7.charcoal:8.iron_ingot' - (s.toString() for s in plan.steps).should.eql [ - '1x 8 test__oak_wood,test__coal>.0. ... .1.>>8 test__charcoal' - '1x 8 test__iron_ore,test__charcoal>.0. ... .1.>test__furnace>8 test__iron_ingot' - ] + describe "with a multi-step plan", -> - plan = plans[1] - plan.computeRequired() - plan.need.unparse().should.equal 'coal:8.iron_ore' - plan.made.unparse().should.equal '8.iron_ingot' - (s.toString() for s in plan.steps).should.eql [ - '1x 8 test__iron_ore,test__coal>.0. ... .1.>test__furnace>8 test__iron_ingot' - ] + beforeEach -> + @ironIngot = fixtures.configureIronIngot @mod + @ironSword = fixtures.configureIronSword @mod + @oakPlank = fixtures.configureOakPlank @mod + @stick = fixtures.configureStick @mod - it 'uses the correct amount of passthrough items', -> - plans = fixtures.makePlans [10, 'test__split_oak_wood'], [10, 'test__split_spruce_wood'] - plan = plans[2] - plan.computeRequired() + @want.add @ironSword, 20 - plan.need.unparse().should.equal 'coal:8.iron_ore:6.oak_wood:5.spruce_wood' - plan.made.unparse().should.equal '4.iron_ingot:maul:2.oak_planks:10.split_oak_wood:10.split_spruce_wood:stick' - (s.toString() for s in plan.steps).should.eql [ - '1x test__oak_wood>... .0. ...>>4 test__oak_planks' - '1x test__oak_planks>.0. .0. ...>>4 test__stick' - '1x 8 test__iron_ore,test__coal>.0. ... .1.>test__furnace>8 test__iron_ingot' - '1x test__iron_ingot,test__stick>001 001 ..1>test__crafting_table>test__maul' - '5x test__spruce_wood,test__maul>.1. .0. ...>>2 test__split_spruce_wood,test__maul' - '5x test__oak_wood,test__maul>.1. .0. ...>>2 test__split_oak_wood,test__maul' - ] + @plan = new CraftingPlan want:@want, steps:[ + new CraftingPlanStep @oakPlank.firstRecipe + new CraftingPlanStep @stick.firstRecipe + new CraftingPlanStep @ironIngot.firstRecipe + new CraftingPlanStep @ironSword.firstRecipe + ] - it 'can compute multiple items with multiple plans', -> - plans = fixtures.makePlans [1, 'test__copper_block'], [1, 'test__iron_sword'] - plans.length.should.equal 4 + it "correctly computes the step counts", -> + @plan.steps[0].count.should.equal 3 + @plan.steps[1].count.should.equal 5 + @plan.steps[2].count.should.equal 5 + @plan.steps[3].count.should.equal 20 - for plan in plans - plan.computeRequired() - plan.need.unparse().should.match /16.copper_ore.*8.iron_ore/ - plan.made.unparse().should.match /copper_block.*:iron_sword/ + it "correctly determines the inputs needed", -> + @plan.need.toString(full:true).should.equal "5 Coal, 40 Iron Ore, 3 Oak Wood" - plan = plans[0] - plan.need.unparse().should.match /3.coal.*9.oak_wood/ - plan.made.unparse().should.match /7.charcoal/ - "#{plan.steps[3]}".should.equal \ - '1x 8 test__iron_ore,test__charcoal>.0. ... .1.>test__furnace>8 test__iron_ingot' - "#{plan.steps[4]}".should.equal \ - '2x 8 test__copper_ore,test__coal>.0. ... .1.>test__furnace>8 test__copper_ingot' - plan.steps.length.should.equal 7 + it "correctly computes the products created", -> + @plan.make.toString(full:true).should.equal "20 Iron Sword, 2 Oak Planks" - plan = plans[1] - plan.need.unparse().should.match /3.coal.*:oak_wood/ - plan.made.unparse().should.not.match /charcoal/ - "#{plan.steps[2]}".should.equal '1x 8 test__iron_ore,test__coal>.0. ... .1.>test__furnace>8 test__iron_ingot' - "#{plan.steps[3]}".should.equal \ - '2x 8 test__copper_ore,test__coal>.0. ... .1.>test__furnace>8 test__copper_ingot' - plan.steps.length.should.equal 6 + describe "with a plan which recycles some items", -> - plan = plans[2] - plan.need.unparse().should.match /^coal.*9.oak_wood/ - plan.made.unparse().should.match /5.charcoal/ - "#{plan.steps[3]}".should.equal \ - '1x 8 test__iron_ore,test__charcoal>.0. ... .1.>test__furnace>8 test__iron_ingot' - "#{plan.steps[4]}".should.equal \ - '2x 8 test__copper_ore,test__charcoal>.0. ... .1.>test__furnace>8 test__copper_ingot' - plan.steps.length.should.equal 7 + beforeEach -> + @bucket = fixtures.configureBucket @mod + @cake = fixtures.configureCake @mod + @ironIngot = fixtures.configureIronIngot @mod + @milkBucket = fixtures.configureMilkBucket @mod + @sugar = fixtures.configureSugar @mod - plan = plans[3] - plan.need.unparse().should.match /2.coal.*9.oak_wood/ - plan.made.unparse().should.match /6.charcoal/ - "#{plan.steps[3]}".should.equal '1x 8 test__iron_ore,test__coal>.0. ... .1.>test__furnace>8 test__iron_ingot' - "#{plan.steps[4]}".should.equal \ - '2x 8 test__copper_ore,test__charcoal>.0. ... .1.>test__furnace>8 test__copper_ingot' - plan.steps.length.should.equal 7 + @want.add @cake, 2 + + @plan = new CraftingPlan want:@want, steps:[ + new CraftingPlanStep @ironIngot.firstRecipe + new CraftingPlanStep @bucket.firstRecipe + new CraftingPlanStep @milkBucket.firstRecipe + new CraftingPlanStep @sugar.firstRecipe + new CraftingPlanStep @cake.firstRecipe + ] + + it "correctly computes the step counts", -> + @plan.steps.length.should.equal 3 + @plan.steps[0].count.should.equal 6 + @plan.steps[1].count.should.equal 4 + @plan.steps[2].count.should.equal 2 + + it "correctly determines the inputs needed", -> + @plan.need.toString(full:true).should.equal "2 Egg, 6 Milk, 4 Sugar Cane, 6 Wheat" + + it "correctly computes the products created", -> + @plan.make.toString(full:true).should.equal "2 Cake" diff --git a/src/client/models/crafting/crafting_plan_step.coffee b/src/client/models/crafting/crafting_plan_step.coffee new file mode 100644 index 000000000..2986e2b4e --- /dev/null +++ b/src/client/models/crafting/crafting_plan_step.coffee @@ -0,0 +1,39 @@ +# +# Crafting Guide - crafting_plan_step.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +######################################################################################################################## + +module.exports = class CraftingPlanStep + + constructor: (recipe, count=1)-> + @_id = _.uniqueId "crafting-plan-step-" + @recipe = recipe + @count = count + + # Properties ################################################################################### + + Object.defineProperties @prototype, + + recipe: + get: -> return @_recipe + set: (recipe)-> + if not recipe? then throw new Error 'recipe is required' + if @_recipe is recipe then return + if @_recipe? then throw new Error 'recipe cannot be reassigned' + @_recipe = recipe + + count: + get: -> return @_count + set: (count)-> + count = parseInt "#{count}" + count = if Number.isNaN(count) then 0 else Math.max 0, count + @_count = count + + # Object Overrides ############################################################################# + + toString: -> + return "CraftingPlanStep:#{@_recipe}×#{@_count}<#{@_id}>" diff --git a/src/client/models/crafting/crafting_step.coffee b/src/client/models/crafting/crafting_step.coffee deleted file mode 100644 index 2d1a2bd60..000000000 --- a/src/client/models/crafting/crafting_step.coffee +++ /dev/null @@ -1,73 +0,0 @@ -# -# Crafting Guide - crafting_step.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -SimpleInventory = require './simple_inventory' - -######################################################################################################################## - -module.exports = class CraftingStep - - constructor: (recipe, modPack, multiplier=0)-> - if not recipe? then throw new Error 'recipe is required' - if not modPack? then throw new Error 'modPack is required' - if multiplier < 0 then throw new Error 'multiplier must be at least 1' - - @number = null - - @_inventory = null - @_modPack = modPack - @_multiplier = multiplier - @_recipe = recipe - - # Public Methods ############################################################################### - - addToolsTo: (targetInventory)-> - for stack in @_recipe.tools - targetInventory.add stack.itemSlug, stack.quantity, insert:true - - completeInto: (targetInventory)-> - for stack in @_recipe.output - continue if @_recipe.isPassThroughFor stack.itemSlug - quantity = @_recipe.getQuantityProduced stack.itemSlug - targetInventory.add stack.itemSlug, quantity * @_multiplier - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - inventory: - get: -> - if not @_inventory? - @_refreshInventory() - return @_inventory - - recipe: - get: -> @_recipe - - multiplier: - get: -> @_multiplier - set: (newMultiplier)-> - @_multiplier = newMultiplier - @_refreshInventory() if @_inventory? - - slug: - get: -> "#{@multiplier}x #{@_recipe.slug}" - - # Object Overrides ############################################################################# - - toString: -> - return @slug - - # Private Methods ############################################################################## - - _refreshInventory: -> - @_inventory = new SimpleInventory - for stack in @_recipe.input - qualifiedSlug = @_modPack.qualifySlug stack.itemSlug - required = @_recipe.getQuantityRequired(stack.itemSlug) - @_recipe.getQuantityProduced(stack.itemSlug) - required = if required > 0 then required * @multiplier else 1 - @_inventory.add qualifiedSlug, required diff --git a/src/client/models/crafting/craftsman.coffee b/src/client/models/crafting/craftsman.coffee deleted file mode 100644 index 9fd8419c9..000000000 --- a/src/client/models/crafting/craftsman.coffee +++ /dev/null @@ -1,150 +0,0 @@ -# -# Crafting Guide - craftsman.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -BaseModel = require '../base_model' -GraphBuilder = require './graph_builder' -Inventory = require '../game/inventory' -PlanBuilder = require './plan_builder' -PlanEvaluator = require './plan_evaluator' - -######################################################################################################################## - -module.exports = class Craftsman extends BaseModel - - @::ANALYZE_STEP_INCREMENT = 29 - - @::GRAPH_STEP_INCREMENT = 59 - - @::PLAN_STEP_INCREMENT = 39 - - @::STAGE = - EMPTY: 'empty' - READY: 'ready' - GRAPHING: 'examining recipes' - PLANNING: 'computing plans' - ANALYZING: 'analyzing plans' - COMPLETE: 'complete' - INVALID: 'invalid' - OUTDATED: 'outdated' - - constructor: (modPack)-> - if not modPack? then throw new Error 'modPack is required' - attributes = - stage: @STAGE.READY - stageCount: 0 - super attributes, {} - - @_modPack = modPack - - @_have = new Inventory modPack:@_modPack - @_have.on c.event.change, => @_resetStage() - - @_want = new Inventory modPack:@_modPack - @_want.on c.event.change, => @_resetStage() - - @on c.event.change + ':stage', => logger.info "Craftsman has started #{@stage}..." - @on 'scheduleNextWork', => @_scheduleNextWork() - @reset() - - # Public Methods ############################################################################### - - reset: -> - @_graphBuilder = null - @_planBuilder = null - @_planEvaluator = null - @_plans = null - - @_resetStage() - - work: -> - return if @_want.isEmpty - - if not @_graphBuilder? - @_want.localize() - @_have.localize() - logger.info -> "Craftsman starting to build #{@_want} from #{@_have}" - - @_graphBuilder = new GraphBuilder modPack:@_modPack, want:@_want, have:@_have - @stage = @STAGE.GRAPHING - @stageCount = 0 - else if not @_graphBuilder.complete - @_graphBuilder.expandGraph @GRAPH_STEP_INCREMENT - @stageCount = @_graphBuilder.stepCount - logger.verbose => "Craftsman is expanding graph: #{@stageCount} nodes" - else if not @_graphBuilder.rootNode.valid - @stage = @STAGE.INVALID - @stageCount = 0 - logger.warning => "Craftsman could not complete a crafting plan:\n#{@_graphBuilder.rootNode}" - else if not @_planBuilder? - @_graphBuilder.pruneInvalidNodes() - logger.debug => "Craftsman finished computing graph:\n#{@_graphBuilder.rootNode}" - - @_planBuilder = new PlanBuilder @_graphBuilder.rootNode, @_modPack, have:@_have, want:@_want - @stage = @STAGE.PLANNING - @stageCount = 0 - else if not @_planBuilder.complete - @_planBuilder.producePlans @PLAN_STEP_INCREMENT - @stageCount = @_planBuilder.plans.length - logger.verbose => "Craftsman is producing plans: #{@_planBuilder.plans.length} plans" - else if not @_planEvaluator? - logger.verbose => "Craftsman finished producing plans: #{@_planBuilder.plans.length} plans" - @_planEvaluator = new PlanEvaluator @_planBuilder.plans - @stage = @STAGE.ANALYZING - @stageCount = 0 - else if not @_planEvaluator.complete - @_planEvaluator.scorePlans @ANALYZE_STEP_INCREMENT - @stageCount = @_planEvaluator.lastScored - logger.verbose => "Craftsman is rating plans: #{@stageCount} plans" - else - @_plans = [ - @_planEvaluator.findBestPlan PlanEvaluator::CRITERIA.FEWEST_STEPS - @_planEvaluator.findBestPlan PlanEvaluator::CRITERIA.LEAST_MATERIALS - ] - @stage = @STAGE.COMPLETE - @stageCount = 0 - - logger.info => "Craftsman has finished with plans: #{(p.toString() for p in @_plans).join('\n\n')}" - - @trigger c.event.change, this - @trigger 'scheduleNextWork' - return @complete - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - complete: - get: -> @stage in [@STAGE.COMPLETE, @STAGE.INVALID, @STAGE.OUTDATED] - - have: - get: -> @_have - - plan: - get: -> @_plans?[0] - - want: - get: -> @_want - - # Private Methods ############################################################################## - - _resetStage: -> - @stageCount = 0 - - if @want.isEmpty - if @stage isnt @STAGE.EMPTY - @stage = @STAGE.EMPTY - @reset() - else if not @_plans? - @stage = @STAGE.READY - else - @stage = @STAGE.OUTDATED - - _scheduleNextWork: -> - return if @want.isEmpty - return if @complete - - _.defer => @work() diff --git a/src/client/models/crafting/evaluation.coffee b/src/client/models/crafting/evaluation.coffee new file mode 100644 index 000000000..ea4cde295 --- /dev/null +++ b/src/client/models/crafting/evaluation.coffee @@ -0,0 +1,112 @@ +# +# Crafting Guide - evaluation.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +######################################################################################################################## + +module.exports = class Evaluation + + constructor: (attributes={})-> + @evaluator = attributes.evaluator + @item = attributes.item if attributes.item? + @recipe = attributes.recipe if attributes.recipe? + @baseScore = attributes.baseScore + + @_baseEvaluations = [] + @_includedTools = {} + @_toolScore = null + + # Properties ################################################################################### + + Object.defineProperties @prototype, + + baseEvaluations: + get: -> return @_baseEvaluations + set: -> throw new Error "baseEvaluations cannot be assigned" + + baseScore: + get: -> return @_baseScore + set: (baseScore)-> + baseScore = parseFloat "#{baseScore}" + baseScore = if Number.isNaN(baseScore) then null else baseScore + @_baseScore = baseScore + + evaluator: + get: -> return @_evaluator + set: (evaluator)-> + if not evaluator? then throw new Error "evaluator is required" + if @_evaluator is evaluator then return + if @_evaluator? then throw new Error "evaluator cannot be reassigned" + @_evaluator = evaluator + + includedTools: + get: -> return @_includedTools + set: -> throw new Error "includedTools cannot be assigned" + + item: + get: -> return @_item + set: (item)-> + if @_recipe? then throw new Error "this evaluation is for a recipe: cannot set an item" + if @_item is item then return + if not item? then throw new Error "item cannot be assigned null" + if @_item? then throw new Error "item cannot be reassigned" + @_item = item + + recipe: + get: -> return @_recipe + set: (recipe)-> + if @_item? then throw new Error "this evaluation is for an item: cannot set an recipe" + if @_recipe is recipe then return + if not recipe? then throw new Error "recipe cannot be assigned null" + if @_recipe? then throw new Error "recipe cannot be reassigned" + @_recipe = recipe + + toolScore: + get: -> @_computeToolScore() + set: -> throw new Error "toolScore cannot be assigned" + + # Public Methods ############################################################################### + + addBaseEvaluation: (evaluation)-> + @_baseEvaluations.push evaluation + + addIncludedTool: (item)-> + @_includedTools[item.id] = item + @_toolScore = null + + computeTotalScore: (quantity=1)-> + if @item? + multiplier = quantity + else if @recipe? + multiplier = Math.ceil 1.0 * quantity / @recipe.output.quantity + + return @baseScore * multiplier + @toolScore + + isToolIncluded: (item)-> + return true if @_includedTools[item.id]? + + for baseEvaluation in @_baseEvaluations + return true if baseEvaluation.isToolIncluded item + + return false + + # Object Overrides ############################################################################# + + toString: -> + obj = if @item? then @item else @recipe + return "#{@evaluator}=>#{obj}@#{@baseScore}" + + # Private Methods ############################################################################## + + _computeToolScore: -> + if not @_toolScore? + @_toolScore = 0 + for id, toolItem of @_includedTools + evaluation = @evaluator.evaluateItem toolItem + continue unless evaluation?.baseScore? + @_toolScore += evaluation.baseScore + + return @_toolScore diff --git a/src/client/models/crafting/evaluator.coffee b/src/client/models/crafting/evaluator.coffee new file mode 100644 index 000000000..079e56384 --- /dev/null +++ b/src/client/models/crafting/evaluator.coffee @@ -0,0 +1,102 @@ +# +# Crafting Guide - evaluator.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +Evaluation = require './evaluation' + +######################################################################################################################## + +module.exports = class Evaluator + + constructor: -> + @_id = _.uniqueId "evaluator-" + @_evaluations = {} + + # Property Methods ############################################################################# + + Object.defineProperties @prototype, + + id: + get: -> return @_id + set: -> throw new Error "id cannot be assigned" + + # Public Methods ############################################################################### + + evaluateItem: (item)-> + return null unless item? + + evaluation = @_evaluations[item.id] + if not evaluation? + evaluation = @_evaluations[item.id] = new Evaluation evaluator:this, item:item + + recipeEvaluation = @_findBestRecipeEvaluationFor item + if recipeEvaluation? + evaluation.addBaseEvaluation recipeEvaluation + evaluation.baseScore = recipeEvaluation.baseScore + else + @_computeGatherableItemScore item, evaluation + + return evaluation + + evaluateRecipe: (recipe)-> + return null unless recipe? + + evaluation = @_evaluations[recipe.id] + if not evaluation? + evaluation = @_evaluations[recipe.id] = new Evaluation evaluator:this, recipe:recipe + @_computeRecipeScore recipe, evaluation + + for id, toolItem of recipe.tools + evaluation.addIncludedTool toolItem + + for id, toolItem of @evaluateItem(toolItem).includedTools + evaluation.addIncludedTool toolItem + + + return evaluation + + getOrderedRecipes: (item, quantity=1)-> + recipes = (recipe for recipeId, recipe of item.recipes) + + recipes.sort (a, b)=> + scoreA = @evaluateRecipe(a).computeTotalScore quantity + scoreB = @evaluateRecipe(b).computeTotalScore quantity + + if scoreA isnt scoreB + return if scoreA < scoreB then -1 else +1 + + return 0 + + return recipes + + # Object Overrides ############################################################################# + + toString: -> + return "#{@constructor.name}<#{@id}>" + + # Overrideable Methods ######################################################################### + + _computeRecipeScore: (recipe, evaluation)-> + throw new Error "#{@constructor.name} must override _computeRecipeScore" + + _computeGatherableItemScore: (item, evaluation)-> + throw new Error "#{@constructor.name} must override _computeGatherableItemScore" + + # Private Methods ############################################################################## + + _findBestRecipeEvaluationFor: (item)-> + return null if item.isGatherable + result = null + + for recipeMap in [item.recipes, item.recipesAsExtra] + for id, recipe of recipeMap + evaluation = @evaluateRecipe recipe + continue unless evaluation?.baseScore? + + if not result? then result = evaluation + if evaluation.baseScore < result.baseScore then result = evaluation + + return result diff --git a/src/client/models/crafting/fixtures.coffee b/src/client/models/crafting/fixtures.coffee new file mode 100644 index 000000000..7b2751c35 --- /dev/null +++ b/src/client/models/crafting/fixtures.coffee @@ -0,0 +1,313 @@ +# +# Crafting Guide - fixtures.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +Item = require "./item" +Mod = require "./mod" +ModPack = require "./mod_pack" +Recipe = require "./recipe" +Stack = require "./stack" + +# Instance Creation Fixtures ########################################################################################### + +exports.createModPack = createModPack = (attributes={})-> + attributes.id ?= _.uniqueId "mod-pack-" + attributes.displayName ?= "Test ModPack" + return new ModPack attributes + +exports.createMod = createMod = (attributes={})-> + attributes.modPack ?= createModPack() + attributes.id ?= _.uniqueId "mod-" + attributes.displayName ?= "Test Mod" + return new Mod attributes + +exports.createItem = createItem = (attributes={})-> + attributes.mod ?= createMod() + attributes.id ?= _.uniqueId "item-" + attributes.displayName ?= _.uniqueId "Test Item " + return new Item attributes + +exports.createRecipe = createRecipe = (attributes={})-> + attributes.id ?= _.uniqueId "recipe-" + attributes.output ?= createItem() + return new Recipe attributes + +exports.createStack = createStack = (attributes={})-> + attributes.item ?= createItem() + attributes.quantity ?= 1 + return new Stack attributes + +# Item Configuration Fixtures ########################################################################################## + +exports.configureBucket = configureBucket = (mod)-> + bucket = mod.items["bucket"] + if not bucket? + ironIngot = configureIronIngot mod + bucket = createItem mod:mod, id:"bucket", displayName:"Bucket" + craftingTable = configureCraftingTable mod + + recipe = createRecipe output:createStack item:bucket + recipe.setInputAt 0, 0, createStack item:ironIngot + recipe.setInputAt 1, 1, createStack item:ironIngot + recipe.setInputAt 0, 2, createStack item:ironIngot + recipe.addTool craftingTable + + return bucket + +exports.configureCake = configureCake = (mod)-> + cake = mod.items["cake"] + if not cake? + bucket = configureBucket mod + cake = createItem mod:mod, id:"cake", displayName:"Cake" + craftingTable = configureCraftingTable mod + egg = configureEgg mod + milkBucket = configureMilkBucket mod + sugar = configureSugar mod + wheat = configureWheat mod + + recipe = createRecipe output:createStack item:cake + recipe.setInputAt 0, 0, createStack item:milkBucket + recipe.setInputAt 0, 1, createStack item:milkBucket + recipe.setInputAt 0, 2, createStack item:milkBucket + recipe.setInputAt 1, 0, createStack item:sugar + recipe.setInputAt 1, 1, createStack item:egg + recipe.setInputAt 1, 2, createStack item:sugar + recipe.setInputAt 2, 0, createStack item:wheat + recipe.setInputAt 2, 1, createStack item:wheat + recipe.setInputAt 2, 2, createStack item:wheat + recipe.addTool craftingTable + recipe.addExtra createStack item:bucket, quantity:3 + + return cake + +exports.configureCoal = configureCoal = (mod)-> + coal = mod.items["coal"] + if not coal? + coal = createItem mod:mod, id:"coal", displayName:"Coal", isGatherable:true + return coal + +exports.configureCobblestone = configureCobblestone = (mod)-> + cobblestone = mod.items["cobblestone"] + if not cobblestone? + cobblestone = createItem mod:mod, displayName:"Cobblestone", isGatherable:true + return cobblestone + +exports.configureCraftingTable = configureCraftingTable = (mod)-> + craftingTable = mod.items["crafting_table"] + if not craftingTable? + craftingTable = createItem mod:mod, id:"crafting_table", displayName:"Crafting Table" + oakPlanks = configureOakPlank mod + + recipe = createRecipe output:createStack item:craftingTable + recipe.setInputAt 0, 0, createStack item:oakPlanks + recipe.setInputAt 0, 1, createStack item:oakPlanks + recipe.setInputAt 1, 0, createStack item:oakPlanks + recipe.setInputAt 1, 1, createStack item:oakPlanks + + return craftingTable + +exports.configureEgg = configureEgg = (mod)-> + egg = mod.items["egg"] + if not egg? + egg = createItem mod:mod, id:"egg", displayName:"Egg", isGatherable:true + return egg + +exports.configureFurnace = configureFurnace = (mod)-> + furnace = mod.items["furnace"] + if not furnace? + cobblestone = configureCobblestone mod + craftingTable = configureCraftingTable mod + furnace = createItem mod:mod, displayName:"Furnace" + + recipe = createRecipe output:createStack item:furnace + recipe.setInputAt 0, 0, createStack item:cobblestone + recipe.setInputAt 0, 1, createStack item:cobblestone + recipe.setInputAt 0, 2, createStack item:cobblestone + recipe.setInputAt 1, 0, createStack item:cobblestone + recipe.setInputAt 1, 2, createStack item:cobblestone + recipe.setInputAt 2, 0, createStack item:cobblestone + recipe.setInputAt 2, 1, createStack item:cobblestone + recipe.setInputAt 2, 2, createStack item:cobblestone + recipe.addTool craftingTable + + return furnace + +exports.configureIronIngot = configureIronIngot = (mod)-> + ironIngot = mod.items["iron_ingot"] + if not ironIngot? + coal = configureCoal mod + furnace = configureFurnace mod + ironIngot = createItem mod:mod, id:"iron_ingot", displayName:"Iron Ingot" + ironOre = configureIronOre mod + + recipe = createRecipe output:createStack item:ironIngot, quantity:8 + recipe.setInputAt 0, 1, createStack item:ironOre, quantity:8 + recipe.setInputAt 2, 1, createStack item:coal + recipe.addTool furnace + + return ironIngot + +exports.configureIronBlock = configureIronBlock = (mod)-> + ironBlock = mod.items["iron_block"] + if not ironBlock? + craftingTable = configureCraftingTable mod + ironBlock = createItem mod:mod, id:"iron_block", displayName:"Iron Block" + ironIngot = configureIronIngot mod + + recipe = createRecipe output:createStack item:ironBlock + for row in [0..2] + for col in [0..2] + recipe.setInputAt row, col, createStack item:ironIngot + recipe.addTool craftingTable + + recipe = createRecipe output:createStack item:ironIngot, quantity:9 + recipe.setInputAt 1, 1, createStack item:ironBlock + + return ironBlock + +exports.configureIronSword = configureIronSword = (mod)-> + ironSword = mod.items["iron_sword"] + if not ironSword? + craftingTable = configureCraftingTable mod + ironIngot = configureIronIngot mod + ironSword = createItem mod:mod, id:"iron_sword", displayName:"Iron Sword" + stick = configureStick mod + + recipe = createRecipe output:createStack item:ironSword + recipe.setInputAt 0, 1, createStack item:ironIngot + recipe.setInputAt 1, 1, createStack item:ironIngot + recipe.setInputAt 2, 1, createStack item:stick + recipe.addTool craftingTable + + return ironSword + +exports.configureIronShovel = configureIronShovel = (mod)-> + ironShovel = mod.items["iron_shovel"] + if not ironShovel? + craftingTable = configureCraftingTable mod + ironIngot = configureIronIngot mod + ironShovel = createItem mod:mod, id:"iron_shovel", displayName:"Iron Shovel" + stick = configureStick mod + + recipe = createRecipe output:createStack item:ironShovel + recipe.setInputAt 0, 1, createStack item:ironIngot + recipe.setInputAt 1, 1, createStack item:stick + recipe.setInputAt 2, 1, createStack item:stick + recipe.addTool craftingTable + + return ironShovel + +exports.configureIronOre = configureIronOre = (mod)-> + ironOre = mod.items["iron_ore"] + if not ironOre? + ironOre = createItem mod:mod, id:"iron_ore", displayName:"Iron Ore", isGatherable:true + return ironOre + +exports.configureMilk = configureMilk = (mod)-> + milk = mod.items["milk"] + if not milk? + milk = createItem mod:mod, id:"milk", displayName:"Milk", isGatherable:true + return milk + +exports.configureMilkBucket = configureMilkBucket = (mod)-> + milkBucket = mod.items["milk_bucket"] + if not milkBucket? + bucket = configureBucket mod + milk = configureMilk mod + milkBucket = createItem mod:mod, id:"milkBucket", displayName:"Milk Bucket" + + recipe = createRecipe output:createStack item:milkBucket + recipe.setInputAt 0, 1, createStack item:milk + recipe.setInputAt 1, 1, createStack item:bucket + + return milkBucket + +exports.configureOakPlank = configureOakPlank = (mod)-> + oakPlanks = mod.items["oak_planks"] + if not oakPlanks? + oakPlanks = createItem mod:mod, id:"oak_planks", displayName:"Oak Planks" + oakWood = configureOakWood mod + + recipe = createRecipe output:createStack item:oakPlanks, quantity:4 + recipe.setInputAt 1, 1, createStack item:oakWood + + return oakPlanks + +exports.configureOakWood = configureOakWood = (mod)-> + oakWood = mod.items["oak_wood"] + if not oakWood? + oakWood = createItem mod:mod, id:"oak_wood", displayName:"Oak Wood", isGatherable:true + return oakWood + +exports.configureRedstoneDust = configureRedstoneDust = (mod)-> + redstoneDust = mod.items["redstone_dust"] + if not redstoneDust? + redstoneDust = createItem mod:mod, id:"redstone_dust", displayName:"Redstone Dust", isGatherable:true + return redstoneDust + +exports.configureStick = configureStick = (mod)-> + stick = mod.items["stick"] + if not stick? + oakPlanks = configureOakPlank mod + stick = createItem mod:mod, id:"stick", displayName:"Stick" + + recipe = createRecipe output:createStack item:stick, quantity:4 + recipe.setInputAt 0, 0, createStack item:oakPlanks + recipe.setInputAt 1, 0, createStack item:oakPlanks + + return stick + +exports.configureSugar = configureSugar = (mod)-> + sugar = mod.items["sugar"] + if not sugar? + sugar = createItem mod:mod, id:"sugar", displayName:"Sugar" + sugarCane = configureSugarCane mod + + recipe = createRecipe output:createStack item:sugar + recipe.setInputAt 1, 1, createStack item:sugarCane + + return sugar + +exports.configureSaw = configureSaw = (mod)-> + saw = mod.items["saw"] + if not saw? + craftingTable = configureCraftingTable mod + ironBlock = configureIronBlock mod + ironIngot = configureIronIngot mod + oakPlank = configureOakPlank mod + oakWood = configureOakWood mod + redstoneDust = configureRedstoneDust mod + saw = createItem mod:mod, id:"saw", displayName:"Saw" + + recipe = createRecipe output:createStack item:saw + recipe.setInputAt 0, 0, createStack item:oakPlank + recipe.setInputAt 0, 1, createStack item:ironIngot + recipe.setInputAt 0, 2, createStack item:oakPlank + recipe.setInputAt 1, 0, createStack item:oakPlank + recipe.setInputAt 1, 1, createStack item:ironBlock + recipe.setInputAt 1, 2, createStack item:oakPlank + recipe.setInputAt 2, 0, createStack item:oakPlank + recipe.setInputAt 2, 1, createStack item:redstoneDust + recipe.setInputAt 2, 2, createStack item:oakPlank + recipe.addTool craftingTable + + recipe = createRecipe output:createStack item:oakPlank, quantity:8 + recipe.setInputAt 1, 1, createStack item:oakWood + recipe.addTool saw + + return saw + +exports.configureSugarCane = configureSugarCane = (mod)-> + sugarCane = mod.items["sugar_cane"] + if not sugarCane? + sugarCane = createItem mod:mod, id:"sugar_cane", displayName:"Sugar Cane", isGatherable:true + return sugarCane + +exports.configureWheat = configureWheat = (mod)-> + wheat = mod.items["wheat"] + if not wheat? + wheat = createItem mod:mod, id:"wheat", displayName:"Wheat", isGatherable:true + return wheat diff --git a/src/client/models/crafting/fixtures.test.coffee b/src/client/models/crafting/fixtures.test.coffee deleted file mode 100644 index c151b35aa..000000000 --- a/src/client/models/crafting/fixtures.test.coffee +++ /dev/null @@ -1,189 +0,0 @@ -# -# Crafting Guide - fixtures.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -GraphBuilder = require './graph_builder' -Inventory = require '../game/inventory' -ItemSlug = require '../game/item_slug' -Mod = require '../game/mod' -ModPack = require '../game/mod_pack' -ModVersion = require '../game/mod_version' -PlanBuilder = require './plan_builder' - -######################################################################################################################## - -MOD_VERSION_FILE = - """ - schema: 1 - - item: Bed - recipe: - input: Oak Planks, Wool - pattern: ... 000 111 - - item: Charcoal - recipe: - input: 8 Oak Wood, Coal - pattern: .0. ... .1. - quantity: 8 - - item: Crafting Table - recipe: - input: Oak Planks - pattern: .00 .00 ... - - item: Coal - gatherable: yes - - item: Cobblestone - gatherable: yes - - item: Copper Block - recipe: - input: Copper Ingot - pattern: 000 000 000 - tools: Crafting Table - - item: Copper Ingot - recipe: - input: 8 Copper Ore, Coal - pattern: .0. ... .1. - quantity: 8 - tools: Furnace - recipe: - input: 8 Copper Ore, Charcoal - pattern: .0. ... .1. - quantity: 8 - tools: Furnace - recipe: - input: Copper Block - pattern: ... .0. ... - quantity: 9 - - item: Copper Ore - gatherable: yes - - item: Furnace - recipe: - input: Cobblestone - pattern: 000 0.0 000 - tools: Crafting Table - - item: Iron Ore - gatherable: yes - - item: Iron Ingot - recipe: - input: 8 Iron Ore, Charcoal - pattern: .0. ... .1. - quantity: 8 - tools: Furnace - recipe: - input: 8 Iron Ore, Coal - pattern: .0. ... .1. - quantity: 8 - tools: Furnace - - item: Iron Sword - recipe: - input: Iron Ingot, Stick - pattern: .0. .0. .1. - tools: Crafting Table - - item: Lever - recipe: - input: Stick, Cobblestone - pattern: .0. .1. ... - - item: Maul - recipe: - input: Iron Ingot, Stick - pattern: 001 001 ..1 - tools: Crafting Table - - item: Oak Planks - recipe: - input: Oak Wood - pattern: ... .0. ... - quantity: 4 - - item: Oak Wood - gatherable: yes - - item: Spruce Wood - gatherable: yes - - item: Stick - recipe: - input: Oak Planks - pattern: .0. .0. ... - quantity: 4 - - item: Split Oak Wood - recipe: - extras: Maul - input: Oak Wood, Maul - pattern: .1. .0. ... - quantity: 2 - - item: Split Spruce Wood - recipe: - extras: Maul - input: Spruce Wood, Maul - pattern: .1. .0. ... - quantity: 2 - - item: String - gatherable: yes - - item: Wool - gatherable: yes - recipe: - input: String - pattern: 00. 00. ... - """ - -######################################################################################################################## - -module.exports = fixtures = - - makeGraphBuilder: -> - return new GraphBuilder modPack:fixtures.makeModPack(), want:new Inventory - - makeModPack: -> - modPack = new ModPack - - mod = new Mod name:'Test', slug:'test' - modPack.addMod mod - - modVersion = new ModVersion modSlug:'test', version:'0.0' - modVersion.parse MOD_VERSION_FILE - mod.addModVersion modVersion - - return modPack - - makePlans: (stacks...)-> - modPack = fixtures.makeModPack() - - have = new Inventory - want = new Inventory - for stack in stacks - want.add ItemSlug.slugify(stack[1]), stack[0] - - graphBuilder = new GraphBuilder modPack:modPack, want:want - graphBuilder.expandGraph() - - planBuilder = new PlanBuilder graphBuilder.rootNode, modPack, want:want, have:have - return planBuilder.producePlans() - - makeTree: (itemSlug, quantity=1)-> - want = new Inventory - want.add ItemSlug.slugify itemSlug - - builder = new GraphBuilder modPack:fixtures.makeModPack(), want:want - builder.expandGraph() - - return builder.rootNode diff --git a/src/client/models/crafting/graph_builder.coffee b/src/client/models/crafting/graph_builder.coffee deleted file mode 100644 index fc8b0ce69..000000000 --- a/src/client/models/crafting/graph_builder.coffee +++ /dev/null @@ -1,72 +0,0 @@ -# -# Crafting Guide - graph_builder.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Inventory = require '../game/inventory' -InventoryNode = require './inventory_node' - -######################################################################################################################## - -module.exports = class GraphBuilder - - constructor: (options={})-> - if not options.modPack? then throw new Error 'options.modPack is required' - if not options.want? then throw new Error 'options.want is required' - - @_maximumGraphSize = c.limits.maximumGraphSize - @_modPack = options.modPack - @_stepCount = 0 - @_want = options.want - - @_rootNode = new InventoryNode modPack:@_modPack, inventory:@_want - @_queue = [@_rootNode] - - # Public Methods ############################################################################### - - expandGraph: (steps=null)-> - maxSteps = if steps? then @_stepCount + steps else Number.MAX_VALUE - @_queue ?= [] - - while true - break if @_queue.length is 0 - break if @_stepCount >= maxSteps - - node = @_queue.shift() - break unless node? - - node.expand @_queue - @_stepCount += 1 - - pruneInvalidNodes: -> - @_rootNode.pruneInvalidChildren() - - reset: -> - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - complete: - get: -> - return true if @_stepCount > @_maximumGraphSize - return false unless @_rootNode? - return false unless @_queue? - return false unless @_queue.length is 0 - return true - - rootNode: - get: -> @_rootNode - - stepCount: - get: -> @_stepCount - - want: - get: -> @_want - - # Object Overrides ############################################################################ - - toString: (indent='')-> - "Build Tree\n#{@_rootNode.toString(indent + ' ')}" diff --git a/src/client/models/crafting/graph_builder.test.coffee b/src/client/models/crafting/graph_builder.test.coffee deleted file mode 100644 index 241c73ca8..000000000 --- a/src/client/models/crafting/graph_builder.test.coffee +++ /dev/null @@ -1,77 +0,0 @@ -# -# Crafting Guide - graph_builder.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -fixtures = require './fixtures.test' -ItemSlug = require '../game/item_slug' - -######################################################################################################################## - -builder = null - -######################################################################################################################## - -describe 'GraphBuilder.coffee', -> - - beforeEach -> - builder = fixtures.makeGraphBuilder() - - describe 'expand', -> - - it 'can work a few steps at a time', -> - builder.want.add ItemSlug.slugify 'test__iron_sword' - builder.expandGraph 9 - - builder.rootNode.depth.should.equal 6 - builder.rootNode.size.should.equal 13 - builder.complete.should.be.false - - builder.expandGraph 9 - - builder.rootNode.depth.should.equal 8 - builder.rootNode.size.should.equal 18 - builder.complete.should.be.true - - it 'works properly with an empty inventory', -> - builder.expandGraph 100 - - builder.rootNode.depth.should.equal 1 - builder.rootNode.size.should.equal 1 - builder.complete.should.be.true - - describe 'can build a tree for', -> - - runSingleItemTreeBuildingTest = (slug, depth, size)-> - builder.want.add ItemSlug.slugify slug - builder.expandGraph 100 - - builder.rootNode.depth.should.equal depth - builder.rootNode.size.should.equal size - builder.complete.should.be.true - - it 'a gatherable item', -> - runSingleItemTreeBuildingTest 'test__oak_wood', 2, 2 - - it 'a single-step item', -> - runSingleItemTreeBuildingTest 'test__crafting_table', 6, 6 - - it 'an item with multiple inputs', -> - runSingleItemTreeBuildingTest 'test__lever', 8, 9 - - it 'an item with multiple recipes', -> - runSingleItemTreeBuildingTest 'test__iron_ingot', 6, 11 - - it 'an item with multiple inputs and multiple recipes', -> - runSingleItemTreeBuildingTest 'test__iron_sword', 8, 18 - - it 'an item with one recursive recipe', -> - runSingleItemTreeBuildingTest 'test__copper_ingot', 6, 15 - - it 'an item which gatherable and craftable', -> - runSingleItemTreeBuildingTest 'test__wool', 4, 4 - - it 'an item which requires a gatherable and craftable item', -> - runSingleItemTreeBuildingTest 'test__bed', 6, 7 diff --git a/src/client/models/crafting/inventory.coffee b/src/client/models/crafting/inventory.coffee new file mode 100644 index 000000000..4eadaa7ba --- /dev/null +++ b/src/client/models/crafting/inventory.coffee @@ -0,0 +1,90 @@ +# +# Crafting Guide - inventory.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +Stack = require './stack' + +######################################################################################################################## + +module.exports = class Inventory + + constructor: (inventory=null)-> + @_id = _.uniqueId "inventory-" + @_stacks = {} + + if inventory? then @merge inventory + + # Properties ################################################################################### + + Object.defineProperties @prototype, + + isEmpty: + get: -> (id for id, stack of @_stacks).length is 0 + + stacks: + get: -> return @_stacks + set: -> throw new Error "stacks cannot be replaced" + + # Public Methods ############################################################################### + + add: (item, quantity)-> + return unless item? + return if quantity is 0 + + existingStack = @_stacks[item.id] + if existingStack? + if existingStack.quantity + quantity < 0 then throw new Error "cannot have a negative quantity" + existingStack.quantity += quantity + else + if quantity < 0 then throw new Error "cannot have a negative quantity" + @_stacks[item.id] = new Stack item:item, quantity:quantity + + if @_stacks[item.id].quantity is 0 + delete @_stacks[item.id] + + clear: -> + @_stacks = {} + + contains: (item)-> + return @_stacks[item.id]? + + getQuantity: (item)-> + existingStack = @_stacks[item.id] + return 0 unless existingStack? + return existingStack.quantity + + merge: (inventory)-> + for id, stack of inventory.stacks + @add stack.item, stack.quantity + + remove: (item, quantity)-> + @add item, -1 * quantity + + # Object Overrides ############################################################################# + + toString: (options={})-> + options.full ?= false + + if options.full + result = [] + needsDelimiter = false + + stackList = (stack for itemId, stack of @_stacks) + stackList.sort (a, b)-> + if a.item.displayName isnt b.item.displayName + return if a.item.displayName < b.item.displayName then -1 else +1 + return 0 + + for stack in stackList + if needsDelimiter then result.push ", " + needsDelimiter = true + + result.push stack.quantity + result.push " " + result.push stack.item.displayName + return result.join "" + else + return "Inventory<#{@_id}>@#{(id for id, item of @_stacks).length}" diff --git a/src/client/models/crafting/inventory_node.coffee b/src/client/models/crafting/inventory_node.coffee deleted file mode 100644 index 8f5555e32..000000000 --- a/src/client/models/crafting/inventory_node.coffee +++ /dev/null @@ -1,55 +0,0 @@ -# -# Crafting Guide - inventory_node.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CraftingNode = require './crafting_node' -ItemNode = require './item_node' - -######################################################################################################################## - -module.exports = class InventoryNode extends CraftingNode - - @::ENTER_METHOD = 'onEnterInventoryNode' - @::LEAVE_METHOD = 'onLeaveInventoryNode' - @::TYPE = CraftingNode::TYPES.INVENTORY - - constructor: (options={})-> - if not options.inventory? then throw new Error 'options.inventory is required' - super options - - @inventory = options.inventory - - # CraftingNode Overrides ####################################################################### - - _createChildren: (result=[])-> - @inventory.each (stack)=> - item = @modPack.findItem stack.itemSlug - if not item? then throw new Error "Could not find an item for slug: #{stack.itemSlug}" - result.push new ItemNode modPack:@modPack, item:item, ignoreGatherable:true - return result - - _checkCompleteness: -> - for child in @children - return false unless child.complete - return true - - _checkValidity: -> - for child in @children - return false unless child.valid - return true - - # Object Overrides ############################################################################# - - toString: (options={})-> - options.indent ?= '' - options.recursive ?= true - - parts = ["#{options.indent}#{@completeText} #{@validText} InventoryNode for #{@inventory}"] - nextIndent = options.indent + ' ' - if options.recursive - for child in @children - parts.push child.toString indent:nextIndent - return parts.join '\n' diff --git a/src/client/models/crafting/item.coffee b/src/client/models/crafting/item.coffee new file mode 100644 index 000000000..beb259cfe --- /dev/null +++ b/src/client/models/crafting/item.coffee @@ -0,0 +1,93 @@ +# +# Crafting Guide - item.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +######################################################################################################################## + +module.exports = class Item + + constructor: (attributes={})-> + @id = attributes.id + @displayName = attributes.displayName + @isGatherable = attributes.isGatherable + @mod = attributes.mod + + @_hasPrimaryRecipe = false + @_recipesAsPrimary = {} + @_recipesAsExtra = {} + + # Property Methods ############################################################################# + + Object.defineProperties @prototype, + + displayName: + get: -> return @_displayName + set: (displayName)-> + if not displayName? then throw new Error "displayName is required" + @_displayName = displayName + + firstRecipe: + get: -> return recipe for id, recipe of @recipes + set: -> throw new Error "firstRecipe cannot be assigned" + + id: + get: -> return @_id + set: (id)-> + if not id? then throw new Error "id is required" + return if @_id is id + if @_id? then throw new Error "id cannot be reassigned" + @_id = id + + isGatherable: + get: -> + return true if @_isGatherable is true + return false if (id for id, recipe of @_recipesAsPrimary).length > 0 + return false if (id for id, recipe of @_recipesAsExtra).length > 0 + return true + set: (isGatherable)-> + @_isGatherable = null unless isGatherable? + @_isGatherable = !!isGatherable + + mod: + get: -> return @_mod + set: (mod)-> + if not mod? then throw new Error "mod is required" + if @_mod is mod then return + if @_mod? then throw new Error "mod cannot be reassigned" + @_mod = mod + @_mod.addItem this + + modPack: + get: -> return @_mod.modPack + set: -> throw new Error "modPack cannot be replaced" + + recipes: + get: -> return if @_hasPrimaryRecipe then @_recipesAsPrimary else @_recipesAsExtra + set: -> throw new Error "recipes cannot be assigned" + + recipesAsPrimary: + get: -> return @_recipesAsPrimary + set: -> throw new Error "recipes cannot be assigned" + + recipesAsExtra: + get: -> return @_recipesAsExtra + set: -> throw new Error "recipesAsExtra cannot be assigned" + + # Public Recipes ############################################################################### + + addRecipe: (recipe)-> + if recipe.output.item is this + @_recipesAsPrimary[recipe.id] = recipe + @_hasPrimaryRecipe = true + else if recipe.extras[this.id] is this + @_recipesAsExtra[recipe.id] = recipe + else + throw new Error "recipe<#{recipe.id}> does not produce this item<#{@id}>" + + # Object Overrides ############################################################################# + + toString: -> + return "Item:#{@displayName}<#{@id}>" diff --git a/src/client/models/crafting/item_node.coffee b/src/client/models/crafting/item_node.coffee deleted file mode 100644 index b58f44f53..000000000 --- a/src/client/models/crafting/item_node.coffee +++ /dev/null @@ -1,108 +0,0 @@ -# -# Crafting Guide - item_node.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CraftingNode = require './crafting_node' -RecipeNode = require './recipe_node' - -######################################################################################################################## - -module.exports = class ItemNode extends CraftingNode - - @::ENTER_METHOD = 'onEnterItemNode' - @::LEAVE_METHOD = 'onLeaveItemNode' - @::TYPE = CraftingNode::TYPES.ITEM - - constructor: (options={})-> - if not options.item? then throw new Error 'options.item is required' - super options - - @item = options.item - @_ignoreGatherable = options.ignoreGatherable ?= false - @_recipes = null - - # Property Methods ############################################################################# - - getRecipes: -> - if not @_recipes? - @_recipes = @modPack.findRecipes @item.slug, forCrafting:true, onlyPrimary:true - if not @_recipes - @_recipes = @modPack.findRecipes @item.slug, forCrafting:true - - return @_recipes or [] - - isGatherable: -> - if not @_ignoreGatherable - return true if @item.isGatherable - return true if @getRecipes().length is 0 - return false - - Object.defineProperties @prototype, - gatherable: { get:@prototype.isGatherable } - recipes: { get:@prototype.getRecipes } - - # CraftingNode Overrides ####################################################################### - - _createChildren: (result=[])-> - recipes = @getRecipes() - return [] if @gatherable - - for recipe in recipes - child = new RecipeNode modPack:@modPack, recipe:recipe - child.parent = this - if child.valid - result.push child - - return result - - _checkCompleteness: -> - return true if @gatherable - return false unless @children? - - for child in @children - return true if child.complete - return false - - _checkValidity: -> - return false if @_isRepeatedItem() - return true unless @children.length > 0 - - for child in @children - return true if child.valid - return false - - _pruneInvalidChildren: -> - index = 0 - while index < @children.length - child = @children[index] - if child.valid - child.pruneInvalidChildren() - index++ - else - @removeChild index - - # Object Overrides ############################################################################# - - toString: (options={})-> - options.indent ?= '' - options.recursive ?= true - - parts = ["#{options.indent}#{@completeText} #{@validText} ItemNode for #{@item.name}"] - nextIndent = options.indent + ' ' - if options.recursive - for child in @children - parts.push child.toString indent:nextIndent - return parts.join '\n' - - # Private Methods ############################################################################## - - _isRepeatedItem: -> - nextParent = @parent - while nextParent? - return true if nextParent.item is @item - nextParent = nextParent.parent - - return false diff --git a/src/client/models/crafting/mod.coffee b/src/client/models/crafting/mod.coffee new file mode 100644 index 000000000..b4e013b86 --- /dev/null +++ b/src/client/models/crafting/mod.coffee @@ -0,0 +1,62 @@ +# +# Crafting Guide - mod.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +######################################################################################################################## + +module.exports = class Mod + + constructor: (attributes={})-> + @id = attributes.id + @displayName = attributes.displayName + @modPack = attributes.modPack + + @_items = {} + + # Properties ################################################################################### + + Object.defineProperties @prototype, + + displayName: + get: -> return @_displayName + set: (displayName)-> + if not displayName? then throw new Error "displayName is required" + return if @_displayName is displayName + @_displayName = displayName + + id: + get: -> return @_id + set: (id)-> + if not id? then throw new Error "id is required" + return if @_id is id + if @_id? then throw new Error "id cannot be reassigned" + @_id = id + + items: + get: -> return @_items + set: -> throw new Error "items cannot be replaced" + + modPack: + get: -> return @_modPack + set: (modPack)-> + if not modPack? then throw new Error "modPack is required" + if @_modPack is modPack then return + if @_modPack? then throw new Error "modPack cannot be reassigned" + @_modPack = modPack + @_modPack.addMod this + + # Public Methods ############################################################################### + + addItem: (item)-> + if not item? then return + if @_items[item.id] is item then return + @_items[item.id] = item + item.mod = this + + # Object Overrides ############################################################################# + + toString: -> + return "Mod:#{@displayName}<#{@id}>" diff --git a/src/client/models/crafting/mod_pack.coffee b/src/client/models/crafting/mod_pack.coffee new file mode 100644 index 000000000..2272fef3c --- /dev/null +++ b/src/client/models/crafting/mod_pack.coffee @@ -0,0 +1,52 @@ +# +# Crafting Guide - mod_pack.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +######################################################################################################################## + +module.exports = class ModPack + + constructor: (attributes={})-> + @id = attributes.id + @displayName = attributes.displayName + + @_mods = {} + @_oreDict = {} + + # Property Methods ############################################################################# + + Object.defineProperties @prototype, + + displayName: + get: -> return @_displayName + set: (displayName)-> + if not displayName? then throw new Error "displayName is required" + if @_displayName is displayName then return + @_displayName = displayName + + id: + get: -> return @_id + set: (id)-> + if not id? then throw new Error "id is required" + if @_id is id then return + if @_id? then throw new Error "id cannot be reassigned" + + mods: + get: -> return @_mods + set: -> throw new Error "mods cannot be replaced" + + # Public Methods ############################################################################### + + addMod: (mod)-> + if not mod? then return + if @_mods[mod.id] is mod then return + @_mods[mod.id] = mod + mod.modPack = this + + # Object Overrides ############################################################################# + + toString: -> + return "ModPack:#{@displayName}<#{@id}>" diff --git a/src/client/models/crafting/plan_builder.coffee b/src/client/models/crafting/plan_builder.coffee index a8687acc9..47ad57257 100644 --- a/src/client/models/crafting/plan_builder.coffee +++ b/src/client/models/crafting/plan_builder.coffee @@ -5,123 +5,88 @@ # All rights reserved. # -CraftingNode = require './crafting_node' -CraftingPlan = require './crafting_plan' -CraftingStep = require './crafting_step' -Inventory = require '../game/inventory' +CraftingPlan = require "./crafting_plan" +CraftingPlanStep = require "./crafting_plan_step" ######################################################################################################################## module.exports = class PlanBuilder - constructor: (rootNode, modPack, options={})-> - if not rootNode? then throw new Error 'rootNode is required' - if not modPack? then throw new Error 'modPack is required' + constructor: (evaluator)-> + @evaluator = evaluator - @want = options.want - @have = options.have - - @_choiceNodes = [] - @_complete = false - @_maxPlanCount = c.limits.maximumPlanCount - @_modPack = modPack - @_plans = [] - @_rootNode = rootNode - - @_isolateChoiceNodes() - - # Public Methods ############################################################################### - - producePlans: (maxPlans=null)-> - maxPlans = if maxPlans then @plans.length + maxPlans else Number.MAX_VALUE - - if @plans.length >= @_maxPlanCount - @_complete = true - - while not @complete and (@plans.length < maxPlans) - plan = @_captureCurrentPlan() - if plan? - @plans.push plan - - @_incrementChoiceNodes() - - return @_plans - - # Property Methods ############################################################################# + # Properties ################################################################################### Object.defineProperties @prototype, - complete: - get: -> @_complete + evaluator: + get: -> return @_evaluator + set: (evaluator)-> + if not evaluator? then throw new Error "evaluator is required" + if @_evaluator is evaluator then return + if @_evaluator? then throw new Error "evaluator cannot be reassigned" + @_evaluator = evaluator - have: - get: -> @_have - set: (have)-> @_have = have or new Inventory + # Public Methods ############################################################################### - maxPlanCount: - get: -> @_maxPlanCount - set: (value)-> @_maxPlanCount = value + createPlan: (want, have)-> + @_alreadyMaking = {} + @_recipesInUse = {} - plans: - get: -> @_plans + stepList = [] + for itemId, stack of want.stacks + steps = @_findStepsForItem stack.item, stack.quantity + return null unless steps? - want: - get: -> @_want - set: (want)-> @_want = want or new Inventory + stepList.push steps + + plan = new CraftingPlan want:want, have:have, steps:_.flatten(stepList) + return plan # Private Methods ############################################################################## - _captureCurrentPlan: -> - toVisit = [@_rootNode] - stepNodes = [] + _findStepsForItem: (item, quantity=1)-> + steps = null - while toVisit.length > 0 - node = toVisit.shift() + if not @_alreadyMaking[item.id] + @_alreadyMaking[item.id] = true - if node.TYPE is CraftingNode::TYPES.INVENTORY - toVisit.push(c) for c in node.children - else if node.TYPE is CraftingNode::TYPES.ITEM - toVisit.push node.children[0] if node.children.length > 0 - else if node.TYPE is CraftingNode::TYPES.RECIPE - stepNodes.push node - toVisit.push(c) for c in node.children + recipes = @_evaluator.getOrderedRecipes item, quantity + if recipes.length is 0 + steps = [] + else + for recipe in recipes + continue if @_recipesInUse[recipe.id]? - steps = [] - seenRecipes = {} - index = stepNodes.length - 1 - while index >= 0 - node = stepNodes[index] - index -= 1 - return null unless node.valid and node.complete + steps = @_findStepsForRecipe recipe, quantity + break if steps? - recipeSlug = node.recipe.slug - continue if seenRecipes[recipeSlug]? + delete @_alreadyMaking[item.id] - seenRecipes[recipeSlug] = true - steps.push new CraftingStep node.recipe, @_modPack + return steps - plan = new CraftingPlan @_modPack, @_want, @_have, steps - return plan + _findStepsForRecipe: (recipe, quantity=1)-> + steps = null - _incrementChoiceNodes: -> - if @_choiceNodes.length is 0 - @_complete = true - return + if not @_recipesInUse[recipe.id] + @_recipesInUse[recipe.id] = true - index = @_choiceNodes.length - 1 - while true - if index is -1 - @_complete = true - return + invalidRecipe = false + for itemId, item of recipe.inputs + inputSteps = @_findStepsForItem item, quantity * recipe.computeQuantityRequired(item) + if not inputSteps? + invalidRecipe = true + break - node = @_choiceNodes[index] - node.rotateChildren() + steps ?= [] + for step in inputSteps + steps.push step - return unless node.rotations % node.children.length is 0 - index -= 1 + if invalidRecipe + steps = null + else + steps.push new CraftingPlanStep recipe - _isolateChoiceNodes: -> - @_rootNode.acceptVisitor - onEnterItemNode: (node)=> - if node.children.length > 1 - @_choiceNodes.push node + delete @_recipesInUse[recipe.id] + + return steps diff --git a/src/client/models/crafting/plan_builder.test.coffee b/src/client/models/crafting/plan_builder.test.coffee index f81a97cd5..bb2224457 100644 --- a/src/client/models/crafting/plan_builder.test.coffee +++ b/src/client/models/crafting/plan_builder.test.coffee @@ -5,39 +5,154 @@ # All rights reserved. # -fixtures = require './fixtures.test' -PlanBuilder = require './plan_builder' +fixtures = require './fixtures' +Inventory = require './inventory' +PlanBuilder = require './plan_builder' +ResourcesEvaluator = require './resources_evaluator' ######################################################################################################################## -describe 'plan_builder.coffee', -> +describe.only "PlanBuilder", -> - printPlan = (plan)-> - return '' unless plan? - return ((s.recipe.slug.replace(/^.*>.*>/, '') for s in plan.steps;;)).join ' > ' + beforeEach -> + @planner = new PlanBuilder new ResourcesEvaluator + @mod = fixtures.createMod() + @want = new Inventory + @have = new Inventory - it 'generates an empty plan for a gatherable item', -> - plans = fixtures.makePlans [1, 'test__oak_wood'] + describe "for a gatherable item, creates a plan that", -> - plans.length.should.equal 1 - plans[0].length.should.equal 0 + beforeEach -> + @oakWood = fixtures.configureOakWood @mod + @want.add @oakWood, 2 + @plan = @planner.createPlan @want - it 'can find a multi-step plan', -> - plans = fixtures.makePlans [1, 'test__lever'] + it "has no steps at all", -> + @plan.steps.length.should.equal 0 - printPlan(plans[0]).should.equal '4 test__oak_planks > 4 test__stick > test__lever' - plans.length.should.equal 1 + it "demands the item itself as the input", -> + @plan.need.toString(full:true).should.equal "2 Oak Wood" - it 'can find multiple plans', -> - plans = fixtures.makePlans [1, 'test__iron_ingot'] + it "produces the item as the result", -> + @plan.make.toString(full:true).should.equal "2 Oak Wood" - printPlan(plans[0]).should.equal '8 test__charcoal > 8 test__iron_ingot' - printPlan(plans[1]).should.equal '8 test__iron_ingot' - plans.length.should.equal 2 + describe "for a single item with a one-step recipe, it creates a plan that", -> - it 'ignores invalid plans', -> - plans = fixtures.makePlans [1, 'test__copper_block'] + beforeEach -> + @oakPlank = fixtures.configureOakPlank @mod + @want.add @oakPlank, 63 + @plan = @planner.createPlan @want - printPlan(plans[0]).should.equal '8 test__copper_ingot > test__copper_block' - printPlan(plans[1]).should.equal '8 test__charcoal > 8 test__copper_ingot > test__copper_block' - plans.length.should.equal 2 + it "has the recipe as the only step", -> + @plan.steps.length.should.equal 1 + @plan.steps[0].recipe.output.item.displayName.should.equal "Oak Planks" + + it "demands the right inputs", -> + @plan.need.toString(full:true).should.equal "16 Oak Wood" + + it "produces the right results", -> + @plan.make.toString(full:true).should.equal "64 Oak Planks" + + describe "for a single item with a complex recipe, it creates a plan that", -> + + beforeEach -> + @ironSword = fixtures.configureIronSword @mod + @want.add @ironSword, 2 + @plan = @planner.createPlan @want + + it "has the right steps", -> + @plan.steps.length.should.equal 4 + @plan.steps[0].recipe.output.item.displayName.should.equal "Iron Ingot" + @plan.steps[1].recipe.output.item.displayName.should.equal "Oak Planks" + @plan.steps[2].recipe.output.item.displayName.should.equal "Stick" + @plan.steps[3].recipe.output.item.displayName.should.equal "Iron Sword" + + it "demands the right inputs", -> + @plan.need.toString(full:true).should.equal "1 Coal, 8 Iron Ore, 1 Oak Wood" + + it "produces the right results", -> + @plan.make.toString(full:true).should.equal "4 Iron Ingot, 2 Iron Sword, 2 Oak Planks, 2 Stick" + + describe "for multiple items with complex recipes, it creates a plan that", -> + + beforeEach -> + @ironSword = fixtures.configureIronSword @mod + @ironShovel = fixtures.configureIronShovel @mod + @want.add @ironSword, 3 + @want.add @ironShovel, 3 + @plan = @planner.createPlan @want + + it "has consolidated the common steps", -> + (step.recipe.output.item.displayName for step in @plan.steps).should.eql [ + "Iron Ingot", "Oak Planks", "Stick", "Iron Sword", "Iron Shovel" + ] + + it "requires the correct repetitions for each step", -> + (step.count for step in @plan.steps).should.eql [ 2, 2, 3, 3, 3 ] + + it "demands the right inputs", -> + @plan.need.toString(full:true).should.equal "2 Coal, 16 Iron Ore, 2 Oak Wood" + + it "produces the right results", -> + @plan.make.toString(full:true).should.equal( + "7 Iron Ingot, 3 Iron Shovel, 3 Iron Sword, 2 Oak Planks, 3 Stick" + ) + + describe "for an item which has a recursive recipe, it creates a plan that", -> + + beforeEach -> + @ironBlock = fixtures.configureIronBlock @mod + @want.add @ironBlock, 4 + @plan = @planner.createPlan @want + + it "avoids the recursive options", -> + (step.recipe.output.item.displayName for step in @plan.steps).should.eql [ + "Iron Ingot", "Iron Block" + ] + + it "requires the correct repetitions for each step", -> + (step.count for step in @plan.steps).should.eql [ 5, 4 ] + + it "demands the right inputs", -> + @plan.need.toString(full:true).should.equal "5 Coal, 40 Iron Ore" + + it "produces the right results", -> + @plan.make.toString(full:true).should.equal( + "4 Iron Block, 4 Iron Ingot" + ) + + describe "when making enormous quantities of something simple", -> + + beforeEach -> + @oakPlank = fixtures.configureOakPlank @mod + @want.add @oakPlank, Number.MAX_VALUE / 2 + @start = Date.now() + @plan = @planner.createPlan @want + @duration = Date.now() - @start + + it "doesn't take forever to create the plan", -> + @duration.should.be.lessThan 10 + + describe "when making an item which can be made with and without a tool, for a", -> + + beforeEach -> + @oakPlank = fixtures.configureOakPlank @mod + @saw = fixtures.configureSaw @mod + + describe "small batch, the plan", -> + + beforeEach -> + @want.add @oakPlank, 2 + @plan = @planner.createPlan @want + + it "avoids the expensive tool", -> + (step.recipe.needsTools for step in @plan.steps).should.eql [ false ] + + describe "large batch, the plan", -> + + beforeEach -> + @want.add @oakPlank, 2048 + @plan = @planner.createPlan @want + + it "uses the tool for efficiency", -> + (step.recipe.needsTools for step in @plan.steps).should.eql [ true ] diff --git a/src/client/models/crafting/plan_evaluator.coffee b/src/client/models/crafting/plan_evaluator.coffee deleted file mode 100644 index 0676976b1..000000000 --- a/src/client/models/crafting/plan_evaluator.coffee +++ /dev/null @@ -1,98 +0,0 @@ -# -# Crafting Guide - plan_evaluator.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class PlanEvaluator - - @::CRITERIA = - FEWEST_STEPS: 'fewest steps' - LEAST_MATERIALS: 'least materials' - - constructor: (plans)-> - if not plans? then throw new Error 'plans is required' - - @_plans = plans - @_lastScored = -1 - @_normalized = false - - # Public Methods ############################################################################### - - findBestPlan: (mainCriteria)-> - criteriaList = [mainCriteria].concat (criteria for key, criteria of @CRITERIA when criteria isnt mainCriteria) - @_plans.sort (a, b)-> - for criteria in criteriaList - scoreA = a.getScore criteria - scoreB = b.getScore criteria - - if scoreA isnt scoreB - return if scoreA > scoreB then -1 else +1 - - return 0 - - return @_plans[0] - - scorePlans: (count)-> - count ?= @_plans.length - return if @complete - - maxPlanIndex = Math.min @_lastScored + count, @_plans.length - 1 - for i in [(@_lastScored + 1)..maxPlanIndex] - @_plans[i].computeRequired() - @_scorePlan @_plans[i] - @_lastScored = i - - if @complete then @_normalizeScores() - - return @complete - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - complete: - get: -> @_lastScored is @_plans.length - 1 - - lastScored: - get: -> @_lastScored - - # Private Methods ############################################################################## - - _computeFewestStepsScore: (plan)-> - total = 0 - for step in plan.steps - total += step.multiplier - plan.setRawScore @CRITERIA.FEWEST_STEPS, total - - _computeLeastMaterialsScore: (plan)-> - total = 0 - plan.need.each (stack)-> - total += stack.quantity - plan.setRawScore @CRITERIA.LEAST_MATERIALS, total - - _normalizeScores: -> - for key, criteria of @CRITERIA - maxScore = 0 - minScore = Number.MAX_VALUE - - for plan in @_plans - continue unless plan.hasRawScore criteria - maxScore = Math.max maxScore, plan.getRawScore(criteria) - minScore = Math.min minScore, plan.getRawScore(criteria) - - adjustedMaxScore = maxScore - minScore - for plan in @_plans - continue unless plan.hasRawScore criteria - if adjustedMaxScore > 0 - adjustedRawScore = plan.getRawScore(criteria) - minScore - plan.setScore criteria, 1.0 - (1.0 * adjustedRawScore / adjustedMaxScore) - else - plan.setScore criteria, 1.0 - - _scorePlan: (plan)-> - @_computeFewestStepsScore plan - @_computeLeastMaterialsScore plan diff --git a/src/client/models/crafting/plan_evaluator.test.coffee b/src/client/models/crafting/plan_evaluator.test.coffee deleted file mode 100644 index 70f87b91d..000000000 --- a/src/client/models/crafting/plan_evaluator.test.coffee +++ /dev/null @@ -1,80 +0,0 @@ -# -# Crafting Guide - plan_evaluator.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CraftingPlan = require './crafting_plan' -fixtures = require './fixtures.test' -Inventory = require '../game/inventory' -PlanEvaluator = require './plan_evaluator' - -######################################################################################################################## - -criteria = evaluator = planA = planB = planC = plans = wanted = null - -######################################################################################################################## - -describe 'plan_evaluator.coffee', -> - - beforeEach -> - criteria = PlanEvaluator::CRITERIA.FEWEST_STEPS - - describe 'findBestPlan', -> - - it 'can find the shortest of two options', -> - evaluator = new PlanEvaluator fixtures.makePlans [1, 'test__copper_block'] - evaluator.scorePlans() - - bestPlan = evaluator.findBestPlan criteria - bestPlan.getScore(criteria).should.equal 1.0 - - it 'can find the shortest of many options', -> - evaluator = new PlanEvaluator fixtures.makePlans [1, 'test__copper_block'], [1, 'test__iron_sword'] - evaluator.scorePlans() - - bestPlan = evaluator.findBestPlan criteria - bestPlan.getScore(criteria).should.equal 1.0 - - it 'can break ties using the non-primary criteria', -> - evaluator = new PlanEvaluator fixtures.makePlans [2, 'test__iron_ingot'], [1, 'test__charcoal'] - evaluator.scorePlans() - - bestPlan = evaluator.findBestPlan criteria - bestPlan.getScore(criteria).should.equal 1.0 - - (p.getRawScore('fewest steps') for p in evaluator._plans).should.eql [2, 2] - (p.getRawScore('least materials') for p in evaluator._plans).should.eql [17, 18] - - describe '_normalizeScores', -> - - beforeEach -> - modPack = fixtures.makeModPack() - wanted = new Inventory modPack:modPack - have = new Inventory modPack:modPack - planA = new CraftingPlan modPack, wanted, have, [] - planB = new CraftingPlan modPack, wanted, have, [] - planC = new CraftingPlan modPack, wanted, have, [] - plans = [planA, planB, planC] - evaluator = new PlanEvaluator plans - criteria = PlanEvaluator::CRITERIA.FEWEST_STEPS - - it 'does not set scores when none have been evaluated', -> - evaluator._normalizeScores() - (plan.hasRawScore(criteria) for plan in plans).should.eql [false, false, false] - - it 'assigns a 1.0 when all plans equal', -> - for plan in plans - plan.setRawScore criteria, 5 - - evaluator._normalizeScores() - (plan.getScore(criteria) for plan in plans).should.eql [1.0, 1.0, 1.0] - - it 'computes correct scores from raw scores', -> - planA.setRawScore criteria, 7 - planB.setRawScore criteria, 8 - planC.setRawScore criteria, 9 - - evaluator._normalizeScores() - (plan.getScore(criteria) for plan in plans).should.eql [1.0, 0.5, 0.0] diff --git a/src/client/models/crafting/recipe.coffee b/src/client/models/crafting/recipe.coffee new file mode 100644 index 000000000..5c1cbe6fa --- /dev/null +++ b/src/client/models/crafting/recipe.coffee @@ -0,0 +1,148 @@ +# +# Crafting Guide - recipe.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +{StringBuilder} = require 'crafting-guide-common' + +######################################################################################################################## + +module.exports = class Recipe + + constructor: (attributes={})-> + @id = attributes.id + @height = attributes.height + @output = attributes.output + @width = attributes.width + + @_extras = {} + @_inputs = {} + @_inputGrid = [] + @_tools = {} + + # Properties ################################################################################### + + Object.defineProperties @prototype, + + allProducts: # an array of Stacks starting the the primary output of this recipe + get: -> return [].concat @output, (stack for id, stack of @extras) + set: -> throw new Error "allProducts cannot be assigned" + + extras: # a hash of item id to Stack of all the non-primary outputs of this recipe + get: -> return @_extras + set: -> throw new Error "extras cannot be replaced" + + id: # a string which uniquely identifies this recipe + get: -> return @_id + set: (id)-> + if not id? then throw new Error "id is required" + if @_id is id then return + if @_id? then throw new Error "id cannot be reassigned" + @_id = id + + height: # an integer specifying the number of rows needed by this recipe + get: -> return @_height + set: (height)-> + height = parseInt "#{height}" + height = if Number.isNaN(height) then 0 else Math.max(0, height) + @_height = height + + inputs: # a hash of item id to Item containing all the inputs to this recipe + get: -> return @_inputs + set: -> throw new Error "inputs cannot be replaced" + + needsTools: # a boolean indicating whether this recipe requires a tool + get: -> return (id for id, toolItem of @_tools).length > 0 + + output: # a Stack specifying the primary output of this recipe + get: -> return @_output + set: (output)-> + if not output? then throw new Error "output is required" + if @_output is output then return + if @_output? then throw new Error "output cannot be reassigned" + @_output = output + @_output.item.addRecipe this + + modPack: # the ModPack to which this recipe belongs + get: -> return @_output.modPack + set: -> throw new Error "modPack cannot be replaced" + + tools: # a hash of item id to Item of all the tools required for this recipe + get: -> return @_tools + set: -> throw new Error "tools cannot be assigned" + + width: # an integer specifying the number of columns needed by this recipe + get: -> return @_width + set: (width)-> + width = parseInt "#{width}" + width = if Number.isNaN(width) then 0 else Math.max(0, width) + @_width = width + + # Public Methods ############################################################################### + + addExtra: (stack)-> + return unless stack + @_extras[stack.item.id] = stack + + addTool: (item)-> + return unless item + @_tools[item.id] = item + + computeQuantityRequired: (item)-> + result = 0 + + for row in [0...@height] + for col in [0...@width] + stack = @_inputGrid[row]?[col] + continue unless stack? + continue unless stack.item.id is item.id + result += stack.quantity + + return result + + computeQuantityProduced: (item)-> + result = 0 + + if @_output.item.id is item.id + result += @_output.quantity + + for itemId, stack of @_extras + continue unless itemId is item.id + result += stack.quantity + + return result + + getInputAt: (row, col)-> + return @_inputGrid[row]?[col] + + setInputAt: (row, col, stack)-> + @_height = Math.max @_height, row + 1 + @_width = Math.max @_width, col + 1 + + @_inputGrid[row] ?= [] + @_inputGrid[row][col] = stack + @_inputs[stack.item.id] = stack.item + + # Object Overrides ############################################################################# + + toString: (options={})-> + options.full ?= false + + if options.full + b = new StringBuilder + b.loop (item for id, item of @inputs), delimiter:" + ", onEach:(b, item)=> + b.push @computeQuantityRequired(item), " ", item.displayName + b.push " =" + b.onlyIf @needsTools, (b)=> + b.push "(" + b.loop (toolItem for id, toolItem of @tools), onEach:(b, toolItem)-> b.push toolItem.displayName + b.push ")" + b.push "=> " + b.loop @allProducts, delimiter:" + ", onEach:(b, stack)=> + b.push stack.quantity, " ", stack.item.displayName + + return b.toString() + else + return "Recipe:#{@output}<#{@id}>" diff --git a/src/client/models/crafting/recipe_node.coffee b/src/client/models/crafting/recipe_node.coffee deleted file mode 100644 index b262a4141..000000000 --- a/src/client/models/crafting/recipe_node.coffee +++ /dev/null @@ -1,87 +0,0 @@ -# -# Crafting Guide - recipe_node.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CraftingNode = require './crafting_node' -Item = require '../game/item' -# ItemNode = require './item_node' # don't include here, causes a cycle -ItemSlug = require '../game/item_slug' - -######################################################################################################################## - -module.exports = class RecipeNode extends CraftingNode - - @::ENTER_METHOD = 'onEnterRecipeNode' - @::LEAVE_METHOD = 'onLeaveRecipeNode' - @::TYPE = CraftingNode::TYPES.RECIPE - - constructor: (options={})-> - if not options.recipe? then throw new Error 'options.recipe is required' - super options - - @recipe = options.recipe - - # CraftingNode Overrides ####################################################################### - - _createChildren: (result=[])-> - ItemNode = require './item_node' # include here to avoid a cycle - - for stack in @recipe.input - item = @modPack.findItem stack.itemSlug - if not item? - name = @modPack.findName stack.itemSlug - item = new Item name:name, slug:stack.itemSlug, gatherable:true - result.push new ItemNode modPack:@modPack, item:item - return result - - _checkCompleteness: -> - for child in @children - return false unless child.complete - return true - - _checkValidity: -> - return false if @_isRepeatedRecipe() - return false if @_requiresToolBeingMade() - - for child in @children - return false unless child.valid - - return true - - # Private Methods ############################################################################## - - _isRepeatedRecipe: -> - nextParent = @parent - while nextParent? - return true if nextParent.recipe is @recipe - nextParent = nextParent.parent - - return false - - _requiresToolBeingMade: -> - for toolStack in @recipe.tools - toolSlug = toolStack.itemSlug - - nextParent = @parent - while nextParent? - if ItemSlug.equal toolSlug, nextParent.item?.slug - return true - nextParent = nextParent.parent - - return false - - # Object Overrides ############################################################################ - - toString: (options={})-> - options.indent ?= '' - options.recursive ?= true - - parts = ["#{options.indent}#{@completeText} #{@validText} RecipeNode for #{@recipe.slug}"] - nextIndent = options.indent + ' ' - if options.recursive - for child in @children - parts.push child.toString indent:nextIndent - return parts.join '\n' diff --git a/src/client/models/crafting/resources_evaluator.coffee b/src/client/models/crafting/resources_evaluator.coffee new file mode 100644 index 000000000..51c5b6655 --- /dev/null +++ b/src/client/models/crafting/resources_evaluator.coffee @@ -0,0 +1,45 @@ +# +# Crafting Guide - resources_evaluator.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +Evaluator = require './evaluator' + +######################################################################################################################## + +module.exports = class ResourcesEvaluator extends Evaluator + + # Evaluator Overrides ########################################################################## + + _computeRecipeScore: (recipe, evaluation)-> + evaluation.baseScore = 0 + + for row in [0...recipe.height] + for col in [0...recipe.width] + stack = recipe.getInputAt row, col + continue unless stack? + + inputEvaluation = @evaluateItem stack.item + if inputEvaluation?.baseScore? + evaluation.baseScore += inputEvaluation.baseScore * stack.quantity + evaluation.addBaseEvaluation inputEvaluation + else + evaluation.baseScore = null + logger.outdent() + return + + for id, extraStack of recipe.extras + extraEvaluation = @evaluateItem extraStack.item + continue unless extraEvaluation?.baseScore? + evaluation.baseScore -= extraStack.quantity * extraEvaluation.baseScore + + for id, toolItem of recipe.tools + toolEvaluation = @evaluateItem toolItem + evaluation.addBaseEvaluation toolEvaluation + + evaluation.baseScore = evaluation.baseScore / recipe.output.quantity + + _computeGatherableItemScore: (item, evaluation)-> + evaluation.baseScore = 1 diff --git a/src/client/models/crafting/resources_evaluator.test.coffee b/src/client/models/crafting/resources_evaluator.test.coffee new file mode 100644 index 000000000..29b716fee --- /dev/null +++ b/src/client/models/crafting/resources_evaluator.test.coffee @@ -0,0 +1,125 @@ +# +# Crafting Guide - resources_evaluator.test.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +ResourcesEvaluator = require './resources_evaluator' +fixtures = require './fixtures' + +######################################################################################################################## + +describe "ResourcesEvaluator", -> + + beforeEach -> + @evaluator = new ResourcesEvaluator + @mod = fixtures.createMod() + + describe 'evaluating a gatherable item', -> + + beforeEach -> + @cobblestone = fixtures.configureCobblestone @mod + @evaluation = @evaluator.evaluateItem @cobblestone + + it 'should return a score of 1 for each item', -> + # 1 cobblestone + @evaluation.computeTotalScore().should.equal 1 + + describe 'evaluating a first-level recipe', -> + + beforeEach -> + @craftingTable = fixtures.configureCraftingTable @mod + @evaluation = @evaluator.evaluateItem @craftingTable + + it 'should return a score which is the sum of all resources', -> + # 1 oak wood => 4 planks (1/4) + # 4 oak planks => 1 crafting table (4 * 1/4 = 1) + @evaluation.computeTotalScore().should.equal 1 + + describe 'evaluating a recipe which requires a tool', -> + + beforeEach -> + @furnace = fixtures.configureFurnace @mod + @evaluation = @evaluator.evaluateItem @furnace + + it 'includes the cost of the tool', -> + # 1 oak wood => 4 planks (1/4) + # 4 oak planks => 1 crafting table (4 * 1/4 = 1) + # 8 cobblestone =(crafting table)=> 1 furnace (8 + 1 = 9) + @evaluation.computeTotalScore().should.equal 9 + + describe 'evaluating a recipe where a tool requires a tool', -> + + beforeEach -> + @ironIngot = fixtures.configureIronIngot @mod + @evaluation = @evaluator.evaluateItem @ironIngot + + it 'should include both tools in the score', -> + # raw ingredients: + # 8 iron ore + 1 coal =(furnace)=> 8 iron ingots ((8 + 1) / 8 = 1.125) + # tools: + # 8 cobblestone =(crafting table)=> 1 furnace (8) + # 4 oak planks => 1 crafting table (4 * 1/4 = 1) + # 1 oak wood => 4 planks (1/4) + @evaluation.computeTotalScore().should.equal 10.125 + + describe 'evaluating a recipe which requires a tool multiple times', -> + + beforeEach -> + @ironSword = fixtures.configureIronSword @mod + @evaluation = @evaluator.evaluateItem @ironSword + + it 'should include the tool only once', -> + # raw ingredients: + # 1 oak wood ==> 4 planks (0.25) + # 2 oak planks ==> 4 sticks (0.125) + # 8 iron ore + 1 coal =(furnace)=> 8 iron ingots (1.125) + # 2 iron ingot + 1 stick =(crafting table)=> 1 iron sword (2.375) + # + # tools: + # 8 cobblestone =(crafting table)=> 1 furnace (8) + # + # 1 oak wood => 4 planks (0.25) + # 4 oak planks => 1 crafting table (1) + # + @evaluation.computeTotalScore().should.equal 11.375 + + describe 'evaluating a recipe which has a recursive recipe', -> + + beforeEach -> + @ironBlock = fixtures.configureIronBlock @mod + @evaluation = @evaluator.evaluateItem @ironBlock + + it 'should avoid the recursive recipes', -> + # raw ingredients: + # 8 iron ore + 1 coal =(furnace)=> 8 iron ingots ((8 + 1) / 8 = 1.125) + # 9 iron ingot =(crafting table)=> 1 iron block (10.125) + # + # tools: + # 8 cobblestone =(crafting table)=> 1 furnace (8) + # 4 oak planks => 1 crafting table (4 * 1/4 = 1) + # 1 oak wood => 4 planks (1/4) + @evaluation.computeTotalScore().should.equal 19.125 + + describe 'evaluating a recipe with multiple ouputs', -> + + beforeEach -> + @cake = fixtures.configureCake @mod + @evaluation = @evaluator.evaluateItem @cake + + it 'should discount the extra outputs', -> + # raw ingredients: + # 8 iron ore + 1 coal =(furnace)=> 8 iron ingot (1.125) + # 3 iron ingot =(crafting table)=> 1 bucket (3.375) + # 1 milk + 1 bucket ==> 1 milk bucket (4.375) + # 1 sugar cane ==> 1 sugar (1) + # 3 wheat + 1 egg + 2 sugar + 3 milk bucket =(crafting table)=> 1 cake, 3 buckets (9) + # + # furnace: + # 8 cobblestone =(crafting table)=> 1 furnace (8) + # + # crafting table + # 1 oak wood ==> 4 oak planks (0.25) + # 4 planks ==> 1 crafting table (1) + @evaluation.computeTotalScore().should.equal 18 diff --git a/src/client/models/crafting/simple_inventory.coffee b/src/client/models/crafting/simple_inventory.coffee deleted file mode 100644 index 8c4704d78..000000000 --- a/src/client/models/crafting/simple_inventory.coffee +++ /dev/null @@ -1,212 +0,0 @@ -# -# Crafting Guide - simple_inventory.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -ItemSlug = require '../game/item_slug' -SimpleStack = require './simple_stack' - -######################################################################################################################## - -module.exports = class SimpleInventory - - constructor: (attributes={}, options={})-> - @clear() - - if options.modPack? - @modPack = options.modPack - - if options.clone? - @addInventory options.clone - - # Class Methods ################################################################################ - - @Delimiters = - Item: '.' - Stack: ':' - - # Public Methods ############################################################################### - - add: (itemSlug, quantity=1)-> - return this unless quantity > 0 - - @_add itemSlug, quantity - return this - - addInventory: (inventory)-> - inventory.each (stack)=> @_add stack.itemSlug, stack.quantity - return this - - clear: (options={})-> - @_stacks = {} - @_itemSlugs = [] - - clone: -> - inventory = new SimpleInventory - inventory.addInventory this - return inventory - - each: (callback)-> - for itemSlug in @_itemSlugs - callback @_stacks[itemSlug] - - getSlugs: -> - return @_itemSlugs[..] - - hasAtLeast: (itemSlug, quantity=1)-> - if quantity is 0 then return true - - stack = @_stacks[itemSlug] - return false unless stack? - return stack.quantity >= quantity - - localize: -> - if not @modPack? then throw new Error 'localize requires @modPack' - - changed = false - newSlugs = [] - newStacks = [] - for itemSlug in @_itemSlugs - stack = @_stacks[itemSlug] - continue unless stack? - - qualifiedSlug = if itemSlug.isQualified then itemSlug else null - if not qualifiedSlug? - qualifiedSlug = @modPack.findItem(itemSlug)?.slug - changed = qualifiedSlug? - - if qualifiedSlug? - newSlugs.push qualifiedSlug - newStacks.push new SimpleStack itemSlug:qualifiedSlug, quantity:stack.quantity - else - newSlugs.push itemSlug - newStacks.push stack - - if changed - @_itemSlugs = newSlugs - @_stacks = {} - for stack in newStacks - @_stacks[stack.itemSlug] = stack - - @_sort() - - pop: -> - itemSlug = @_itemSlugs.pop() - return null unless itemSlug? - - stack = @_stacks[itemSlug] - delete @_stacks[itemSlug] - - return stack - - quantityOf: (itemSlug)-> - stack = @_stacks[itemSlug] - return stack.quantity if stack - return 0 - - remove: (itemSlug, quantity=null)-> - stack = @_stacks[itemSlug] - return this unless stack? - - quantity ?= stack.quantity - return this unless quantity > 0 - - if stack.quantity < quantity - throw new Error "cannot remove #{quantity}: only #{stack.quantity} #{itemSlug} in this inventory" - - stack.quantity -= quantity - if stack.quantity is 0 - for currentItemSlug, index in @_itemSlugs - if ItemSlug.equal itemSlug, currentItemSlug - @_itemSlugs.splice index, 1 - break - - return this - - # Parsing Methods ############################################################################## - - parse: (data)-> - return this if not data? or data.length is 0 - - stacks = data.split SimpleInventory.Delimiters.Stack - for stackText in stacks - stackParts = stackText.split SimpleInventory.Delimiters.Item - if stackParts.length is 2 - quantity = parseInt stackParts[0], 10 - itemSlug = ItemSlug.slugify stackParts[1] - else if stackParts.length is 1 - quantity = 1 - itemSlug = ItemSlug.slugify stackParts[0] - else - throw new Error "expected #{stackText} to have 0 or 1 parts" - - if itemSlug.qualified.length > 0 - @add itemSlug, quantity - - return this - - unparse: (options={})-> - parts = [] - @each (stack)=> - slugText = stack.itemSlug.item - if @modPack? - item = @modPack.findItem ItemSlug.slugify slugText - if item? and item.slug.qualified isnt stack.itemSlug.qualified - slugText = stack.itemSlug.qualified - - if stack.quantity is 1 - parts.push slugText - else - parts.push "#{stack.quantity}#{SimpleInventory.Delimiters.Item}#{slugText}" - - return parts.join SimpleInventory.Delimiters.Stack - - # Property Methods ############################################################################# - - getIsEmpty: -> - return @_itemSlugs.length is 0 - - getTotalQuantity: -> - total = 0 - @each (stack)-> - total += stack.quantity - return total - - Object.defineProperties @prototype, - isEmpty: { get:@prototype.getIsEmpty } - totalQuantity: { get:@prototype.getTotalQuantity } - - # Object Overrides ############################################################################# - - toString: -> - result = [@constructor.name, " (", @cid, ") {items: ["] - - needsDelimiter = false - @each (stack)-> - if needsDelimiter then result.push ', ' - result.push stack.toString() - needsDelimiter = true - result.push ']' - - result.push '}' - return result.join '' - - # Private Methods ############################################################################## - - _add: (itemSlug, quantity=1)-> - return unless itemSlug? - return unless quantity > 0 - - stack = @_stacks[itemSlug] - if not stack? - stack = new SimpleStack itemSlug:itemSlug, quantity:quantity - @_stacks[itemSlug] = stack - @_itemSlugs.push itemSlug - @_sort() - else - stack.quantity += quantity - - _sort: -> - @_itemSlugs.sort (a, b)-> ItemSlug.compare a, b diff --git a/src/client/models/crafting/simple_stack.coffee b/src/client/models/crafting/simple_stack.coffee deleted file mode 100644 index 361eedb4b..000000000 --- a/src/client/models/crafting/simple_stack.coffee +++ /dev/null @@ -1,33 +0,0 @@ -# -# Crafting Guide - simple_stack.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -ItemSlug = require '../game/item_slug' - -######################################################################################################################## - -module.exports = class Stack - - constructor: (attributes={}, options={})-> - if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required' - attributes.quantity ?= 1 - - @itemSlug = attributes.itemSlug - @quantity = attributes.quantity - - # Class Methods ################################################################################ - - @compare: (a, b)-> - if a? and not b? then return -1 - if not a? and b? then return +1 - if a.quantity isnt b.quantity - return if a.quantity > b.quantity then -1 else +1 - return ItemSlug.compare a.itemSlug, b.itemSlug - - # Object Overrides ############################################################################# - - toString: -> - return "#{@quantity} #{@itemSlug}" diff --git a/src/client/models/crafting/stack.coffee b/src/client/models/crafting/stack.coffee new file mode 100644 index 000000000..aa8541cfc --- /dev/null +++ b/src/client/models/crafting/stack.coffee @@ -0,0 +1,42 @@ +# +# Crafting Guide - stack.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +######################################################################################################################## + +module.exports = class Stack + + constructor: (attributes={})-> + @item = attributes.item + @quantity = attributes.quantity + + # Properties ################################################################################### + + Object.defineProperties @prototype, + + item: + get: -> return @_item + set: (item)-> + if not item? then throw new Error "item is required" + if @_item is item then return + if @_item? then throw new Error "item cannot be reassigned" + @_item = item + + modPack: + get: -> return @_item.modPack + set: -> throw new Error "modPack cannot be replaced" + + quantity: + get: -> return @_quantity + set: (quantity)-> + quantity = parseInt "#{quantity}" + quantity = if Number.isNaN(quantity) then 0 else Math.max(0, quantity) + @_quantity = quantity + + # Object Overrides ############################################################################# + + toString: -> + return "Stack:#{@item}×#{@quantity}" \ No newline at end of file diff --git a/src/client/models/crafting/steps_evaluator.coffee b/src/client/models/crafting/steps_evaluator.coffee new file mode 100644 index 000000000..3e938cb69 --- /dev/null +++ b/src/client/models/crafting/steps_evaluator.coffee @@ -0,0 +1,43 @@ +# +# Crafting Guide - steps_evaluator.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +Evaluator = require './evaluator' + +######################################################################################################################## + +module.exports = class StepsEvaluator extends Evaluator + + # Evaluator Overrides ########################################################################## + + _computeRecipeScore: (recipe, evaluation)-> + evaluation.score = 0 + + for id, stack of recipe.inputs + inputEvaluation = @evaluateItem stack.item + if not inputEvaluation?.score? + evaluation.score = null + return + + evaluation.addBaseEvaluation inputEvaluation + evaluation.score = Math.min evaluation.score, inputEvaluation.score + 1 + + for id, item of recipe.tools + continue if evaluation.isToolIncluded item + + toolEvaluation = @evaluateItem stack.item + if not toolEvaluation?.score? + evaluation.score = null + return + + evaluation.addBaseEvaluation evaluation + evaluation.addIncludedTool item + evaluation.score += toolEvaluation.score + + return result + + _computeGatherableItemScore: (item, evaluation)-> + evaluation.score = 0 diff --git a/src/client/models/game/mod_version.coffee b/src/client/models/game/mod_version.coffee index 466361538..7d5268406 100644 --- a/src/client/models/game/mod_version.coffee +++ b/src/client/models/game/mod_version.coffee @@ -157,7 +157,7 @@ module.exports = class ModVersion extends BaseModel for recipe in primaryRecipes result.push recipe - if not options.onlyPrimary + if not options.onlyPrimary and result.length is 0 for recipe in otherRecipes result.push recipe diff --git a/src/client/models/game/mod_version.test.coffee b/src/client/models/game/mod_version.test.coffee index cba6d85d5..93a1d7305 100644 --- a/src/client/models/game/mod_version.test.coffee +++ b/src/client/models/game/mod_version.test.coffee @@ -90,12 +90,17 @@ describe 'mod_version.coffee', -> recipe:; input: Cake Slice; pattern: 000 000 000; onlyIf: item Cake Slice item: Bucket recipe:; input: Iron Ingot; pattern: ... 0.0 .0. + recipe:; input: Copper Ingot; pattern: ... 0.0 .0.; extras: Copper Nugget """ it 'finds all recipes which list item as output', -> recipes = modVersion.findRecipes ItemSlug.slugify('test__bucket') - (r.output[0].itemSlug.item for r in recipes).sort().should.eql ['bucket', 'cake', 'cake'] + (r.output[0].itemSlug.item for r in recipes).sort().should.eql ['bucket', 'bucket'] it 'skip recipes whose conditions are not met', -> recipes = modVersion.findRecipes ItemSlug.slugify('test__cake') recipes.length.should.equal 2 + + it 'finds recipes for items which are only ever extras', -> + recipes = modVersion.findRecipes ItemSlug.slugify('test__copper_nugget') + recipes.length.should.equal 1 From 69ca6b06b932ace59ee41a8134cb47918d5a2c6d Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Tue, 27 Dec 2016 15:25:33 -0800 Subject: [PATCH 2/5] wip --- Gruntfile.coffee | 2 +- src/client/models-old/base_model.coffee | 129 +++++++ src/client/models-old/converter.coffee | 45 +++ src/client/models-old/event_recorder.coffee | 36 ++ src/client/models-old/game/inventory.coffee | 243 ++++++++++++ src/client/models-old/game/item.coffee | 90 +++++ .../game/item_slug.coffee | 0 src/client/models-old/game/mod.coffee | 234 +++++++++++ src/client/models-old/game/mod_pack.coffee | 202 ++++++++++ .../game/mod_version.coffee | 0 .../game/multiblock.coffee | 0 src/client/models-old/game/recipe.coffee | 247 ++++++++++++ .../game/simple_stack.coffee | 0 src/client/models-old/game/stack.coffee | 23 ++ .../command_parser_version_base.coffee | 0 .../command_parser_version_base.test.coffee | 0 .../parsing/item_parser.coffee | 0 .../parsing/item_parser_v1.coffee | 0 .../parsing/item_parser_v1.test.coffee | 0 .../parsing/mod_parser.coffee | 0 .../parsing/mod_parser_v1.coffee | 0 .../parsing/mod_version_parser.coffee | 0 .../parsing/mod_version_parser_v1.coffee | 0 .../parsing/mod_version_parser_v1.test.coffee | 0 .../parsing/tutorial_parser.coffee | 0 .../parsing/tutorial_parser_v1.coffee | 0 .../parsing/versioned_parser_base.coffee | 0 src/client/models-old/site/tutorial.coffee | 33 ++ .../models/crafting/crafting_plan.coffee | 4 +- .../models/crafting/crafting_plan.test.coffee | 4 +- src/client/models/crafting/evaluation.coffee | 23 +- src/client/models/crafting/evaluator.coffee | 28 +- src/client/models/crafting/inventory.coffee | 90 ----- src/client/models/crafting/item.coffee | 93 ----- src/client/models/crafting/mod.coffee | 62 --- src/client/models/crafting/mod_pack.coffee | 52 --- .../models/crafting/plan_builder.test.coffee | 6 +- src/client/models/crafting/recipe.coffee | 148 ------- .../crafting/resources_evaluator.coffee | 27 +- .../crafting/resources_evaluator.test.coffee | 6 +- src/client/models/crafting/stack.coffee | 42 -- .../models/crafting/steps_evaluator.coffee | 2 - .../models/{crafting => }/fixtures.coffee | 134 ++++--- src/client/models/game/inventory.coffee | 267 +++---------- src/client/models/game/inventory.test.coffee | 213 ---------- src/client/models/game/item.coffee | 140 +++---- src/client/models/game/item_slug.test.coffee | 122 ------ src/client/models/game/mod.coffee | 252 ++---------- src/client/models/game/mod.test.coffee | 30 -- src/client/models/game/mod_pack.coffee | 209 ++-------- src/client/models/game/mod_pack.test.coffee | 103 ----- .../models/game/mod_version.test.coffee | 106 ----- src/client/models/game/multiblock.test.coffee | 85 ---- src/client/models/game/recipe.coffee | 365 +++++++----------- src/client/models/game/recipe.test.coffee | 118 +++--- src/client/models/game/stack.coffee | 37 +- .../models/parsing/mod_pack_json.test.coffee | 124 ++++++ .../parsing/mod_pack_json_formatter.coffee | 101 +++++ .../parsing/mod_pack_json_parser.coffee | 164 ++++++++ .../models/stores/mod_pack_store.coffee | 55 +++ src/client/site/site_controller.coffee | 19 +- src/common/constants.coffee | 45 +-- 62 files changed, 2274 insertions(+), 2286 deletions(-) create mode 100644 src/client/models-old/base_model.coffee create mode 100644 src/client/models-old/converter.coffee create mode 100644 src/client/models-old/event_recorder.coffee create mode 100644 src/client/models-old/game/inventory.coffee create mode 100644 src/client/models-old/game/item.coffee rename src/client/{models => models-old}/game/item_slug.coffee (100%) create mode 100644 src/client/models-old/game/mod.coffee create mode 100644 src/client/models-old/game/mod_pack.coffee rename src/client/{models => models-old}/game/mod_version.coffee (100%) rename src/client/{models => models-old}/game/multiblock.coffee (100%) create mode 100644 src/client/models-old/game/recipe.coffee rename src/client/{models => models-old}/game/simple_stack.coffee (100%) create mode 100644 src/client/models-old/game/stack.coffee rename src/client/{models => models-old}/parsing/command_parser_version_base.coffee (100%) rename src/client/{models => models-old}/parsing/command_parser_version_base.test.coffee (100%) rename src/client/{models => models-old}/parsing/item_parser.coffee (100%) rename src/client/{models => models-old}/parsing/item_parser_v1.coffee (100%) rename src/client/{models => models-old}/parsing/item_parser_v1.test.coffee (100%) rename src/client/{models => models-old}/parsing/mod_parser.coffee (100%) rename src/client/{models => models-old}/parsing/mod_parser_v1.coffee (100%) rename src/client/{models => models-old}/parsing/mod_version_parser.coffee (100%) rename src/client/{models => models-old}/parsing/mod_version_parser_v1.coffee (100%) rename src/client/{models => models-old}/parsing/mod_version_parser_v1.test.coffee (100%) rename src/client/{models => models-old}/parsing/tutorial_parser.coffee (100%) rename src/client/{models => models-old}/parsing/tutorial_parser_v1.coffee (100%) rename src/client/{models => models-old}/parsing/versioned_parser_base.coffee (100%) create mode 100644 src/client/models-old/site/tutorial.coffee delete mode 100644 src/client/models/crafting/inventory.coffee delete mode 100644 src/client/models/crafting/item.coffee delete mode 100644 src/client/models/crafting/mod.coffee delete mode 100644 src/client/models/crafting/mod_pack.coffee delete mode 100644 src/client/models/crafting/recipe.coffee delete mode 100644 src/client/models/crafting/stack.coffee rename src/client/models/{crafting => }/fixtures.coffee (77%) delete mode 100644 src/client/models/game/inventory.test.coffee delete mode 100644 src/client/models/game/item_slug.test.coffee delete mode 100644 src/client/models/game/mod.test.coffee delete mode 100644 src/client/models/game/mod_pack.test.coffee delete mode 100644 src/client/models/game/mod_version.test.coffee delete mode 100644 src/client/models/game/multiblock.test.coffee create mode 100644 src/client/models/parsing/mod_pack_json.test.coffee create mode 100644 src/client/models/parsing/mod_pack_json_formatter.coffee create mode 100644 src/client/models/parsing/mod_pack_json_parser.coffee create mode 100644 src/client/models/stores/mod_pack_store.coffee diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 42cc2b10b..ce937dacb 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -130,7 +130,7 @@ module.exports = (grunt)-> mochaTest: options: - bail: true + bail: false color: true reporter: 'list' require: [ diff --git a/src/client/models-old/base_model.coffee b/src/client/models-old/base_model.coffee new file mode 100644 index 000000000..8da42e281 --- /dev/null +++ b/src/client/models-old/base_model.coffee @@ -0,0 +1,129 @@ +# +# Crafting Guide - base_model.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +######################################################################################################################## + +module.exports = class BaseModel extends Backbone.Model + + @_loadingQueue = [] + @_isDraining = false + + constructor: (attributes={}, options={})-> + options.logEvents ?= true + super attributes, options + + makeGetter = (name)-> return -> @get name + makeSetter = (name)-> return (value)-> @set name, value + for name, value of attributes + continue if name is 'id' + Object.defineProperty this, name, get:makeGetter(name), set:makeSetter(name) + + @fileCache = options.fileCache or null + @loading = null + @logEvents = options.logEvents or false + @state = c.modelState.unloaded + + Object.defineProperties this, + isUnloaded: { get:-> @state is c.modelState.unloaded } + isLoading: { get:-> @state is c.modelState.loading } + isLoaded: { get:-> @state is c.modelState.loaded } + isError: { get:-> @state is c.modelState.error } + + # Event Methods ################################################################################ + + onLoadSucceeded: (text, status, xhr)-> + try + @set @parse text + + @state = c.modelState.loaded + @trigger c.event.change, this + @trigger c.event.sync, this + logger.info => "#{@constructor.name}.#{@cid} loaded successfully" + catch e + logger.error -> "A parsing error occured: #{e.stack}" + @onLoadFailed e.message, 'parsing failed', xhr + + onLoadFailed: (error, status, xhr)-> + @state = c.modelState.error + logger.error => "#{@constructor.name}.#{@cid} failed to load: status:#{status}, message:#{error}" + @trigger c.event.error, this, error + + # Backbone.Model Overrides ##################################################################### + + fetch: (options={})-> + options.force ?= false + return if (@isLoading or @isLoaded) and not options.force + + url = @url() + logger.info => "#{@constructor.name}.#{@cid} reading from url: #{url}" + + @state = c.modelState.loading + @trigger c.event.request, this + + loadFromServer = => + w.promise (resolve, reject)=> + $.ajax + url: url + dataType: 'text' + success: (text, status, xhr)=> resolve @onLoadSucceeded text, status, xhr + error: (xhr, status, error)=> reject @onLoadFailed error, status, xhr + + if @fileCache? + @loading = @fileCache.loading.then => + if @fileCache.hasFile url + return @_addToLoadingQueue @fileCache.getFile(url), 'success', {url:url} + else + @loading = loadFromServer() + else + @loading = loadFromServer() + + @loading.catch (e)-> # do nothing + return @loading + + parse: (text)-> + return JSON.parse text + + sync: (method, model)-> + throw new Error "#{@constructor.name}.#{@cid} is not permitted to #{method}" + + trigger: (name, model, args...)-> + if @logEvents + argText = ("#{arg}"[0..50] for arg in args).join ", " + logger.trace => "#{@constructor.name}.#{@cid} triggered event #{name} with args: #{argText}" + super + + # Object Overrides ############################################################################# + + toString: -> + return "#{@constructor.name}.#{@cid}" + + # Private Methods ############################################################################## + + _addToLoadingQueue: (text, status, xhr)-> + deferred = w.defer() + + BaseModel._loadingQueue.push resolve:deferred.resolve, func:(=> @onLoadSucceeded text, status, xhr) + @_drainLoadingQueue() + return deferred.promise + + _drainLoadingQueue: -> + return if @_isDraining + @_isDraining = true + + drainDelay = 50 + drain = => + toLoad = BaseModel._loadingQueue.shift() + if not toLoad? + @_isDraining = false + else + toLoad.func() + toLoad.resolve(true) + + _.delay drain, drainDelay + + _.delay drain, drainDelay + diff --git a/src/client/models-old/converter.coffee b/src/client/models-old/converter.coffee new file mode 100644 index 000000000..d765a13b6 --- /dev/null +++ b/src/client/models-old/converter.coffee @@ -0,0 +1,45 @@ +# +# Crafting Guide - converter.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +Item = require "../models/game/item" +Mod = require "../models/game/mod" +ModPack = require "../models/game/mod_pack" + +######################################################################################################################## + +module.exports = class Converter + + # Public Methods ############################################################################### + + convert: (id, displayName, oldModPack)-> + modSlugToIdMap = {} + itemSlugToIdMap = {} + + newModPack = new ModPack id:id, displayName:displayName + oldModPack.eachMod (oldMod)=> + newMod = new Mod id:_.uniqueId("mod-"), displayName:oldMod.name, modPack:newModPack + modSlugToIdMap[oldMod.slug.toString()] = newMod.id + + oldMod.eachItem (oldItem)=> + newItem = new Item id:_.uniqueId("item-"), displayName:oldItem.name, mod:newMod + itemSlugToIdMap[oldItem.slug.toString()] = newItem.id + + if oldItem.isGatherable? + newItem.isGatherable = oldItem.isGatherable + + oldModPack.eachMod (oldMod)=> + newMod = newModPack.mods[modSlugToIdMap[oldMod.slug.toString()]] + + + return newModPack + + # Private Methods ############################################################################## + + _convertItem: (oldItem, newMod)-> + + _convertMod: (oldMod, newModPack)-> + return newMod \ No newline at end of file diff --git a/src/client/models-old/event_recorder.coffee b/src/client/models-old/event_recorder.coffee new file mode 100644 index 000000000..507ed32cd --- /dev/null +++ b/src/client/models-old/event_recorder.coffee @@ -0,0 +1,36 @@ +# +# Crafting Guide - event_recorder.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +util = require 'util' + +######################################################################################################################## + +module.exports = class EventRecorder + + constructor: (model)-> + if not model? then throw new Error 'model is required' + + @model = model + @events = [] + + @model.on 'all', (event, model, args...)=> + logger.verbose -> "#{model?.constructor?.name}(#{model?.cid}) emitted #{event} + with args: #{util.inspect(args)}" + @events.push id:model?.cid, event:event, args:args + + # Public Methods ############################################################################### + + reset: -> + @events = [] + + # Property Methods ############################################################################# + + getNames: -> + return (e.event for e in @events) + + Object.defineProperties @prototype, + names: {get:@prototype.getNames} diff --git a/src/client/models-old/game/inventory.coffee b/src/client/models-old/game/inventory.coffee new file mode 100644 index 000000000..a024cb2d7 --- /dev/null +++ b/src/client/models-old/game/inventory.coffee @@ -0,0 +1,243 @@ +# +# Crafting Guide - inventory.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +BaseModel = require '../base_model' +ItemSlug = require './item_slug' +Stack = require './stack' + +######################################################################################################################## + +module.exports = class Inventory extends BaseModel + + constructor: (attributes={}, options={})-> + super attributes, options + attributes.modPack ?= null + @clear() + + if options.clone? + @addInventory options.clone + + # Class Methods ################################################################################ + + @Delimiters = + Item: '.' + Stack: ':' + + # Public Methods ############################################################################### + + add: (itemSlug, quantity=1, options={})-> + return this unless quantity > 0 + + @_add itemSlug, quantity, options + @trigger c.event.add, this, itemSlug, quantity + @trigger c.event.change, this + return this + + addInventory: (inventory)-> + inventory.each (stack)=> @_add stack.itemSlug, stack.quantity + + @trigger c.event.change, this + return this + + clear: (options={})-> + @_stacks = {} + @_itemSlugs = [] + + @trigger c.event.change, this + + clone: -> + inventory = new Inventory + inventory.addInventory this + return inventory + + each: (callback)-> + for itemSlug in @_itemSlugs + stack = @_stacks[itemSlug] + continue unless stack? + callback stack + + getSlugs: -> + return @_itemSlugs[..] + + hasAtLeast: (itemSlug, quantity=1)-> + if quantity is 0 then return true + + stack = @_stacks[itemSlug] + return false unless stack? + return stack.quantity >= quantity + + localize: -> + if not @modPack? then throw new Error 'localize requires @modPack' + + changed = false + newSlugs = [] + newStacks = [] + for itemSlug in @_itemSlugs + stack = @_stacks[itemSlug] + continue unless stack? + + qualifiedSlug = if itemSlug.isQualified then itemSlug else null + if not qualifiedSlug? + qualifiedSlug = @modPack.findItem(itemSlug)?.slug + changed = qualifiedSlug? + + if qualifiedSlug? + newSlugs.push qualifiedSlug + newStacks.push new Stack itemSlug:qualifiedSlug, quantity:stack.quantity + else + newSlugs.push itemSlug + newStacks.push stack + + if changed + for itemSlug, stack of @_stacks + @stopListening stack + + @_itemSlugs = newSlugs + @_stacks = {} + for stack in newStacks + @_stacks[stack.itemSlug] = stack + @listenTo stack, c.event.change, => @trigger c.event.change, this + + @_sort() + @trigger c.event.change, this + + pop: -> + itemSlug = @_itemSlugs.pop() + return null unless itemSlug? + + stack = @_stacks[itemSlug] + delete @_stacks[itemSlug] + + @trigger c.event.remove, this, stack.itemSlug, stack.quantity + @trigger c.event.change, this + return stack + + quantityOf: (itemSlug)-> + stack = @_stacks[itemSlug] + return stack.quantity if stack + return 0 + + remove: (itemSlug, quantity=null)-> + stack = @_stacks[itemSlug] + return this unless stack? + + quantity ?= stack.quantity + return this unless quantity > 0 + + if stack.quantity < quantity + throw new Error "cannot remove #{quantity}: only #{stack.quantity} #{itemSlug} in this inventory" + + stack.quantity -= quantity + if stack.quantity is 0 + @stopListening stack + delete @_stacks[itemSlug] + @_itemSlugs = (s for s in @_itemSlugs when not ItemSlug.equal(s, itemSlug)) + + @trigger c.event.remove, this, itemSlug, quantity + @trigger c.event.change, this + return this + + toDescription: -> + return null if @isEmpty + return null unless @modPack? + + item = @modPack.findItem @_itemSlugs[0] + extras = @_itemSlugs.length - 1 + + result = "#{item.name}" + if extras > 0 then result += " and #{extras} more..." + + return result + + # Parsing Methods ############################################################################## + + parse: (data)-> + return this if not data? or data.length is 0 + + stacks = data.split Inventory.Delimiters.Stack + for stackText in stacks + stackParts = stackText.split Inventory.Delimiters.Item + if stackParts.length is 2 + quantity = parseInt stackParts[0], 10 + itemSlug = ItemSlug.slugify stackParts[1] + else if stackParts.length is 1 + quantity = 1 + itemSlug = ItemSlug.slugify stackParts[0] + else + throw new Error "expected #{stackText} to have 0 or 1 parts" + + if itemSlug.qualified.length > 0 + @add itemSlug, quantity + + return this + + unparse: (options={})-> + parts = [] + @each (stack)=> + slugText = stack.itemSlug.item + if @modPack? + item = @modPack.findItem ItemSlug.slugify slugText + if item? and item.slug.qualified isnt stack.itemSlug.qualified + slugText = stack.itemSlug.qualified + + if stack.quantity is 1 + parts.push slugText + else + parts.push "#{stack.quantity}#{Inventory.Delimiters.Item}#{slugText}" + + return parts.join Inventory.Delimiters.Stack + + # Property Methods ############################################################################# + + Object.defineProperties @prototype, + isEmpty: + get: -> @_itemSlugs.length is 0 + + totalQuantity: + get: -> + total = 0 + @each (stack)-> + total += stack.quantity + return total + + # Object Overrides ############################################################################# + + toString: -> + result = [@constructor.name, " (", @cid, ") {items: ["] + + needsDelimiter = false + @each (stack)-> + if needsDelimiter then result.push ', ' + result.push stack.toString() + needsDelimiter = true + result.push ']' + + result.push '}' + return result.join '' + + # Private Methods ############################################################################## + + _add: (itemSlug, quantity=1, options={})-> + options.insert ?= false + return unless itemSlug? + return unless quantity > 0 + + stack = @_stacks[itemSlug] + if not stack? + stack = new Stack itemSlug:itemSlug, quantity:quantity + @listenTo stack, c.event.change, => @trigger c.event.change, this + @_stacks[itemSlug] = stack + if options.insert + @_itemSlugs.unshift itemSlug + else + @_itemSlugs.push itemSlug + @_sort() + else + stack.quantity += quantity + + _sort: -> + @_itemSlugs.sort (a, b)-> ItemSlug.compare a, b diff --git a/src/client/models-old/game/item.coffee b/src/client/models-old/game/item.coffee new file mode 100644 index 000000000..9ab2545aa --- /dev/null +++ b/src/client/models-old/game/item.coffee @@ -0,0 +1,90 @@ +# +# Crafting Guide - item.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +BaseModel = require '../base_model' +ItemSlug = require './item_slug' +Recipe = require './recipe' +{StringBuilder} = require 'crafting-guide-common' + +######################################################################################################################## + +module.exports = class Item extends BaseModel + + @Group = Other:'Other' + + constructor: (attributes={}, options={})-> + if not attributes.name? then throw new Error 'attributes.name is required' + + attributes.description ?= null + attributes.group ?= Item.Group.Other + attributes.ignoreDuringCrafting ?= false + attributes.isGatherable ?= false + attributes.modVersion ?= null + attributes.officialUrl ?= null + attributes.slug ?= ItemSlug.slugify attributes.name + attributes.videos ?= [] + + options.logEvents ?= false + super attributes, options + + @on c.event.change + ':modVersion', => + @_isCraftable = null + @slug.mod = @modVersion?.modSlug + + # Public Methods ############################################################################### + + compareTo: (that)-> + if this.slug isnt that.slug + return if this.slug < that.slug then -1 else +1 + if this.name isnt that.name + return if this.name < that.name then -1 else +1 + return 0 + + unparse: -> + ItemParser = require '../parsing/item_parser' # to avoid require cycles + @_parser ?= new ItemParser model:this + return @_parser.unparse() + + # Property Methods ############################################################################# + + getIsCraftable: -> + if not @_isCraftable? + @_isCraftable = false + if @modVersion? + @_isCraftable = @modVersion.hasRecipes @slug + + return @_isCraftable + + Object.defineProperties @prototype, + isCraftable: {get:@prototype.getIsCraftable} + + # Backbone.Model Overrides ##################################################################### + + parse: (text)-> + ItemParser = require '../parsing/item_parser' # to avoid require cycles + @_parser ?= new ItemParser model:this + @_parser.parse text + + return null # prevent calling `set` + + url: -> + return c.url.itemData modSlug:@slug.mod, itemSlug:@slug.item + + # Object Overrides ############################################################################# + + toString: -> + builder = new StringBuilder + return builder + .push @constructor.name, ' (', @cid, ') { ' + .push 'name:"', @name, '", ' + .push 'isCraftable:', @isCraftable, ', ' + .push 'isGatherable:', @isGatherable, ', ' + .onlyIf (@group isnt Item.Group.Other), (b)=> + b.push 'group:"', @group, '", ' + .push 'slug:"', @slug, '", ' + .push '}' + .toString() diff --git a/src/client/models/game/item_slug.coffee b/src/client/models-old/game/item_slug.coffee similarity index 100% rename from src/client/models/game/item_slug.coffee rename to src/client/models-old/game/item_slug.coffee diff --git a/src/client/models-old/game/mod.coffee b/src/client/models-old/game/mod.coffee new file mode 100644 index 000000000..e5e058564 --- /dev/null +++ b/src/client/models-old/game/mod.coffee @@ -0,0 +1,234 @@ +# +# Crafting Guide - mod.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +BaseModel = require '../base_model' + +######################################################################################################################## + +module.exports = class Mod extends BaseModel + + constructor: (attributes={}, options={})-> + if not attributes.slug? then throw new Error 'attributes.slug is required' + + attributes.author ?= '' + attributes.description ?= '' + attributes.documentationUrl ?= null + attributes.downloadUrl ?= null + attributes.homePageUrl ?= null + attributes.modPack ?= null + attributes.name ?= '' + + super attributes, options + + @_activeModVersion = null + @_activeVersion = null + @_modVersions = [] + @_tutorials = [] + + # Class Methods ################################################################################## + + @Version: Version = + None: 'none' + Latest: 'latest' + + # Public Methods ################################################################################# + + compareTo: (that)-> + thisRequired = this.slug in c.requiredMods + thatRequired = that.slug in c.requiredMods + + if thisRequired isnt thatRequired + return -1 if thisRequired + return +1 if thatRequired + else if this.slug isnt that.slug + return if this.slug < that.slug then -1 else +1 + + return 0 + + # Property Methods ############################################################################# + + Object.defineProperties @prototype, + + activeModVersion: + get: -> @_activeModVersion + + activeVersion: + get: -> + return @_activeVersion + + set: (version)-> + return if version is @_activeVersion + + version ?= Mod.Version.None + if version is Mod.Version.Latest then version = _.last(@_modVersions).version + + if version is Mod.Version.None + @_activeVersion = version + @_activateModVersion null + + @trigger c.event.change + ':activeVersion', this, @_activeVersion + @trigger c.event.change, this + else + for modVersion in @_modVersions + if version is modVersion.version + @_activateModVersion modVersion + break + + @_activeVersion = version + @trigger c.event.change + ':activeVersion', this, @_activeVersion + @trigger c.event.change, this + + enabled: + get: -> @_activeModVersion? + + modVersions: + get: -> @_modVersions[..] + + tutorials: + get: -> @getAllTutorials() + + # Item Methods ################################################################################# + + chooseRandomItem: -> + effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest + return null unless effectiveModVersion? + + return effectiveModVersion.chooseRandomItem() + + eachItem: (callback)-> + effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest + effectiveModVersion.eachItem callback + + findItem: (slug, options={})-> + options.includeDisabled ?= false + options.enableAsNeeded ?= false + + if not options.includeDisabled + return unless @_activeModVersion? + return @_activeModVersion.findItem slug + else + for modVersion in @_modVersions + modVersion.fetch() + + item = modVersion.findItem slug + if item? + if options.enableAsNeeded then @setActiveVersion modVersion.version + return item + + return null + + findItemByName: (name)-> + return unless @_activeModVersion? + @_activeModVersion.findItemByName name + + # ModVersion Methods ########################################################################### + + addModVersion: (modVersion)-> + return unless modVersion? + return if @_modVersions.indexOf(modVersion) isnt -1 + + @_modVersions.push modVersion + @listenTo modVersion, c.event.change, => @trigger c.event.change, this + modVersion.fileCache = this.fileCache + modVersion.mod = this + + @trigger c.event.add + ':modVersion', modVersion, this + @trigger c.event.change + ':version', modVersion, this + @trigger c.event.change, this + + if not @activeVersion? then @activeVersion = modVersion.version + if modVersion.version is @_activeVersion then @_activateModVersion modVersion + return this + + eachModVersion: (callback)-> + for modVersion in @_modVersions + callback modVersion + + getAllModVersions: -> + return @_modVersions[..] + + getModVersion: (version)-> + return null if version is Mod.Version.None + return @_modVersions[0] if version is Mod.Version.Latest + + for modVersion in @_modVersions + return modVersion if modVersion.version is version + + return null + + # Name Methods ################################################################################# + + eachName: (callback)-> + effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest + effectiveModVersion.eachName callback + + findName: (itemSlug)-> + return unless @_activeModVersion? + @_activeModVersion.findName itemSlug + + # Recipe Methods ############################################################################### + + eachRecipe: (callback)-> + effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest + effectiveModVersion.eachRecipe callback + + findRecipes: (itemSlug, result=[], options={})-> + options.alwaysFromOwningMod ?= false + + if @_activeModVersion? + return @_activeModVersion.findRecipes itemSlug, result, options + else if options.alwaysFromOwningMod and itemSlug.mod is @slug + return @getModVersion(Mod.Version.Latest).findRecipes itemSlug, result, options + + return null + + # Tutorial Methods ############################################################################# + + addTutorial: (tutorial)-> + return unless tutorial? + if @getTutorial(tutorial.slug)? then throw new Error "duplicate tutorial: #{tutorial.name}" + @_tutorials.push tutorial + tutorial.modSlug = @slug + + getAllTutorials: -> + return @_tutorials[..] + + getTutorial: (tutorialSlug)-> + for tutorial in @_tutorials + return tutorial if tutorial.slug is tutorialSlug + return null + + # Backbone.Model Overrides ##################################################################### + + parse: (text)-> + ModParser = require '../parsing/mod_parser' # to avoid require cycles + @_parser ?= new ModParser model:this + @_parser.parse text + + @_verifyActiveModVersion() + + return null # prevent calling `set` + + url: -> + return c.url.modData modSlug:@slug + + # Private Methods ############################################################################## + + _activateModVersion: (modVersion)-> + if @_activeModVersion? then @stopListening @_activeModVersion + @_activeModVersion = modVersion + @trigger c.event.change + ':activeModVersion', this, @_activeModVersion + + logger.verbose => "#{@slug} switched to version #{@_activeVersion}" + + if @_activeModVersion? + @listenTo @_activeModVersion, 'all', -> @trigger.apply this, arguments + + _verifyActiveModVersion: -> + if (@_activeVersion isnt Version.None) and (not @_activeModVersion?) + logger.warning => "#{@slug} no longer has a version #{@_activeVersion}, using latest instead" + @activeVersion = Version.Latest diff --git a/src/client/models-old/game/mod_pack.coffee b/src/client/models-old/game/mod_pack.coffee new file mode 100644 index 000000000..50dd10493 --- /dev/null +++ b/src/client/models-old/game/mod_pack.coffee @@ -0,0 +1,202 @@ +# +# Crafting Guide - mod_pack.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +BaseModel = require '../base_model' +Mod = require './mod' +ModVersionParser = require '../parsing/mod_version_parser' +Recipe = require './recipe' +SimpleInventory = require '../crafting/simple_inventory' + +######################################################################################################################## + +module.exports = class ModPack extends BaseModel + + constructor: (attributes={}, options={})-> + super attributes, options + + @_mods = [] + @_cache = {} + + @on c.event.change, => @_cache = {} + + # Item Methods ################################################################################# + + chooseRandomItem: -> + return null unless @_mods.length > 0 + + modIndex = Math.floor Math.random() * @_mods.length + return @_mods[modIndex].chooseRandomItem() + + findItem: (itemSlug, options={})-> + options.includeDisabled ?= false + + key = "#{itemSlug}-#{options.includeDisabled}" + @_cache.itemBySlug ?= {} + item = @_cache.itemBySlug[key] + return item if item? + + if itemSlug.isQualified + mod = @getMod itemSlug.mod + if mod? + item = mod.findItem itemSlug, options + + if not item? + for mod in @_mods + continue unless mod.enabled or options.includeDisabled + item = mod.findItem itemSlug, options + break if item? + + if item? + @_cache.itemBySlug[key] = item + + return item + + findItemByName: (name, options={})-> + options.enableAsNeeded ?= false + options.includeDisabled = true if options.enableAsNeeded + + for mod in @_mods + continue unless mod.enabled or options.includeDisabled + item = mod.findItemByName name, options + return item if item? + + return null + + findItemDisplay: (itemSlug)-> + if not itemSlug? then throw new Error 'itemSlug is required' + + result = {slug:itemSlug} + item = @findItem itemSlug, includeDisabled:true + if item? + result.itemName = item.name + result.itemSlug = item.slug.item + result.modSlug = item.slug.mod + result.modVersion = item.modVersion.version + else + result.itemName = @findName itemSlug, includeDisabled:true + result.itemSlug = itemSlug.item + result.modSlug = @_mods[0].slug + result.modVersion = @_mods[0].activeVersion + + craftingUrlInventory = new SimpleInventory modPack:this + if item?.multiblock? + craftingUrlInventory.addInventory item.multiblock.inventory + else + craftingUrlInventory.add itemSlug + + result.craftingUrl = c.url.crafting inventoryText:craftingUrlInventory.unparse() + result.iconUrl = c.url.itemIcon result + result.itemUrl = c.url.item result + result.modName = @getMod(result.modSlug).name + return result + + qualifySlug: (itemSlug)-> + return itemSlug if itemSlug.isQualified + + item = @findItem itemSlug + return item.slug if item? + return itemSlug + + # Mod Methods ################################################################################## + + addMod: (mod)-> + if not mod? then throw new Error 'mod is required' + return if @_mods.indexOf(mod) isnt -1 + + mod.modPack = this + @_mods.push mod + @listenTo mod, c.event.change, (modVersion)=> @_onModVersionLoaded modVersion + @trigger c.event.add + ':mod', mod, this + + @_mods.sort (a, b)-> a.compareTo b + @trigger c.event.sort + ':mod', this + @trigger c.event.change, this + + return this + + eachMod: (callback)-> + for mod in @_mods + callback mod + + getMod: (slug)-> + for mod in @_mods + return mod if mod.slug is slug + return null + + getAllMods: -> + return @_mods[..] + + removeMod: (mod)-> + index = @_mods.indexOf mod + return unless index >= 0 + + @_mods.splice index, 1 + + @trigger c.event.remove, this, mod.slug + @trigger c.event.change, this + + # Name Methods ################################################################################# + + findName: (slug, options={})-> + options.includeDisabled ?= false + + for mod in @_mods + continue unless mod.enabled or options.includeDisabled + name = mod.findName slug + return name if name + + return null + + # Recipe Methods ############################################################################### + + findRecipes: (itemSlug, options={})-> + options.alwaysFromOwningMod ?= false + return null unless itemSlug? + + key = "#{itemSlug}-#{options.alwaysFromOwningMod}" + @_cache.recipesBySlug ?= {} + result = @_cache.recipesBySlug[key] + return result if result? + + result = [] + for mod in @_mods + if not mod.enabled + owningMod = itemSlug.isQualified and (itemSlug.mod is mod.slug) + continue unless owningMod and options.alwaysFromOwningMod + + mod.findRecipes itemSlug, result, options + + @_cache.recipesBySlug[key] = result + return if result.length > 0 then result else null + + # Object Overrides ############################################################################# + + toString: -> + return "ModPack (#{@cid}) {modVersions:«#{@_mods.length} items»}" + + # Private Methods ############################################################################## + + _onModVersionLoaded: (modVersion)-> + mods = @getAllMods() + return true unless mods.length > 0 + + for mod in mods + if mod.isError + @removeMod mod + continue + + modVersions = mod.getAllModVersions() + return true unless modVersions.length > 0 + continue if mod.activeVersion is Mod.Version.None + + activeModVersion = mod.activeModVersion + return true unless activeModVersion? + return true if activeModVersion.isUnloaded + return true if activeModVersion.isLoading + + @trigger c.event.change, this + @trigger c.event.sync, this diff --git a/src/client/models/game/mod_version.coffee b/src/client/models-old/game/mod_version.coffee similarity index 100% rename from src/client/models/game/mod_version.coffee rename to src/client/models-old/game/mod_version.coffee diff --git a/src/client/models/game/multiblock.coffee b/src/client/models-old/game/multiblock.coffee similarity index 100% rename from src/client/models/game/multiblock.coffee rename to src/client/models-old/game/multiblock.coffee diff --git a/src/client/models-old/game/recipe.coffee b/src/client/models-old/game/recipe.coffee new file mode 100644 index 000000000..a3416a42f --- /dev/null +++ b/src/client/models-old/game/recipe.coffee @@ -0,0 +1,247 @@ +# +# Crafting Guide - recipe.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +BaseModel = require '../base_model' +ItemSlug = require './item_slug' +Stack = require './stack' +{StringBuilder} = require 'crafting-guide-common' + +######################################################################################################################## + +module.exports = class Recipe extends BaseModel + + constructor: (attributes={}, options={})-> + if not attributes.input? then throw new Error 'attributes.input is required' + if not attributes.pattern? then throw new Error 'attributes.pattern is required' + + if attributes.itemSlug? and not attributes.output? + attributes.output = [new Stack itemSlug:attributes.itemSlug, quantity:1] + else if attributes.output? and not attributes.itemSlug? + if attributes.output.length is 0 then throw new Error 'attributes.output cannot be empty' + attributes.itemSlug = attributes.output[0].itemSlug + else + throw new Error 'attributes.itemSlug or attributes.output is required' + + attributes.pattern = @_parsePattern attributes.pattern + + attributes.condition ?= null + attributes.ignoreDuringCrafting ?= false + attributes.modVersion ?= null + attributes.tools ?= [] + options.logEvents ?= false + super attributes, options + + @_computeQuantities attributes.pattern + + @on c.event.change + ':modVersion', => @_slug = null + @on c.event.change + ':pattern', => @_patternCache = null + + # Class Methods ################################################################################ + + @compareFor: (a, b, itemSlug)-> + if itemSlug? + aValue = a.itemSlug.matches itemSlug + bValue = b.itemSlug.matches itemSlug + if aValue isnt bValue + return -1 if aValue + return +1 if bValue + + aValue = a.getQuantityProduced itemSlug + bValue = b.getQuantityProduced itemSlug + if aValue isnt bValue + return if aValue > bValue then -1 else +1 + + return 0 + + # Public Methods ############################################################################### + + getStackAtSlot: (patternSlot)-> + trueIndex = 0:0, 1:1, 2:2, 3:4, 4:5, 5:6, 6:8, 7:9, 8:10 + 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 + + getQuantityProduced: (itemSlug)-> + total = 0 + for stack in @output + if stack.itemSlug.matches itemSlug + total += stack.quantity + + return total + + getQuantityRequired: (itemSlug)-> + total = 0 + for stack, index in @input + if ItemSlug.equal stack.itemSlug, itemSlug + total += @_quantities[index] * stack.quantity + + return total + + hasAllTools: (modPack)-> + modPack ?= @modVersion?.mod?.modPack + return true unless modPack? + + for stack in @tools + return false unless modPack.findItem stack.itemSlug + return true + + isConditionSatisfied: (modPack)-> + return true unless @condition? + modPack ?= @modVersion?.mod?.modPack + + result = false + if @condition.verb is 'item' + if modPack?.findItemByName(@condition.noun)? + result = true + else if @condition.verb is 'mod' + modPack.eachMod (mod)=> + if mod.name is @condition.noun + result = true + + if @condition.inverted then result = not result + return result + + isPassThroughFor: (itemSlug)-> + return @getQuantityProduced(itemSlug) is @getQuantityRequired(itemSlug) + + produces: (itemSlug)-> + if not @_produces? + @_produces = {} + + for stack in @output + actuallyProduces = not @isPassThroughFor stack.itemSlug + @_produces[stack.itemSlug.qualified] = actuallyProduces + + result = @_produces[itemSlug.qualified] or @_produces[itemSlug.item] + return result + + requires: (itemSlug)-> + for stack in @input + if stack.itemSlug.matches itemSlug + return true + return false + + requiresTool: (itemSlug)-> + for stack in @tools + if stack.itemSlug.matches itemSlug + return true + + # Property Methods ############################################################################# + + Object.defineProperties @prototype, + + slug: + get: -> + if not @_slug? + builder = new StringBuilder + delimiterNeeded = false + for stack in @input + if delimiterNeeded then builder.push ',' + delimiterNeeded = true + + if stack.quantity > 1 then builder.push stack.quantity, ' ' + builder.push stack.itemSlug.qualified + + builder.push '>' + builder.push @pattern + builder.push '>' + for stack in @tools + builder.push stack.itemSlug.qualified + builder.push '>' + + delimiterNeeded = false + for stack in @output + if delimiterNeeded then builder.push ',' + delimiterNeeded = true + + if stack.quantity > 1 then builder.push stack.quantity, ' ' + builder.push stack.itemSlug.qualified + + @_slug = builder.toString() + + return @_slug + + # Object Overrides ############################################################################# + + toString: -> + result = [@constructor.name, " (", @cid, ") { name:", @name] + + result.push ", input:[" + needsDelimiter = false + for stack in @input + if needsDelimiter then result.push ', ' + result.push @getQuantityRequired stack.itemSlug + result.push ' ' + result.push stack.itemSlug + needsDelimiter = true + result.push ']' + + result.push ", output:[" + needsDelimiter = false + for stack in @output + if needsDelimiter then result.push ', ' + result.push @getQuantityProduced stack.itemSlug + result.push ' ' + result.push stack.itemSlug + needsDelimiter = true + result.push ']' + + if @tools.length > 0 + result.push ", tools:[" + needsDelimiter = false + for stack in @tools + if needsDelimiter then result.push ', ' + result.push stack.toString() + needsDelimiter = true + result.push ']' + + result.push '}' + return result.join '' + + # Private Methods ############################################################################## + + _computeQuantities: (pattern)-> + quantityMap = {} + + index = 0 + while index < pattern.length + c = pattern[index] + index += 1 + + continue if c is '.' + continue if c is ' ' + + if quantityMap[c]? + quantityMap[c] += 1 + else + quantityMap[c] = 1 + + @_quantities = [] + for i in [0...@input.length] + @_quantities.push quantityMap["#{i}"] + + _parsePattern: (pattern)-> + return unless pattern? + + pattern = pattern.replace /\ /g, '' + return if pattern.length is 0 + + pattern = pattern.replace /[^0-9]/g, '.' + + array = pattern.split '' + array = array[0...9] + while array.length isnt 9 + array.push '.' + + pattern = array.join '' + pattern = pattern.replace /(...)(...)(...)/, '$1 $2 $3' + return pattern diff --git a/src/client/models/game/simple_stack.coffee b/src/client/models-old/game/simple_stack.coffee similarity index 100% rename from src/client/models/game/simple_stack.coffee rename to src/client/models-old/game/simple_stack.coffee diff --git a/src/client/models-old/game/stack.coffee b/src/client/models-old/game/stack.coffee new file mode 100644 index 000000000..6797330dd --- /dev/null +++ b/src/client/models-old/game/stack.coffee @@ -0,0 +1,23 @@ +# +# Crafting Guide - stack.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +BaseModel = require '../base_model' + +######################################################################################################################## + +module.exports = class Stack extends BaseModel + + constructor: (attributes={}, options={})-> + if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required' + attributes.quantity ?= 1 + options.logEvents ?= false + super attributes, options + + # Object Overrides ############################################################################# + + toString: -> + return "#{@quantity} #{@itemSlug}" diff --git a/src/client/models/parsing/command_parser_version_base.coffee b/src/client/models-old/parsing/command_parser_version_base.coffee similarity index 100% rename from src/client/models/parsing/command_parser_version_base.coffee rename to src/client/models-old/parsing/command_parser_version_base.coffee diff --git a/src/client/models/parsing/command_parser_version_base.test.coffee b/src/client/models-old/parsing/command_parser_version_base.test.coffee similarity index 100% rename from src/client/models/parsing/command_parser_version_base.test.coffee rename to src/client/models-old/parsing/command_parser_version_base.test.coffee diff --git a/src/client/models/parsing/item_parser.coffee b/src/client/models-old/parsing/item_parser.coffee similarity index 100% rename from src/client/models/parsing/item_parser.coffee rename to src/client/models-old/parsing/item_parser.coffee diff --git a/src/client/models/parsing/item_parser_v1.coffee b/src/client/models-old/parsing/item_parser_v1.coffee similarity index 100% rename from src/client/models/parsing/item_parser_v1.coffee rename to src/client/models-old/parsing/item_parser_v1.coffee diff --git a/src/client/models/parsing/item_parser_v1.test.coffee b/src/client/models-old/parsing/item_parser_v1.test.coffee similarity index 100% rename from src/client/models/parsing/item_parser_v1.test.coffee rename to src/client/models-old/parsing/item_parser_v1.test.coffee diff --git a/src/client/models/parsing/mod_parser.coffee b/src/client/models-old/parsing/mod_parser.coffee similarity index 100% rename from src/client/models/parsing/mod_parser.coffee rename to src/client/models-old/parsing/mod_parser.coffee diff --git a/src/client/models/parsing/mod_parser_v1.coffee b/src/client/models-old/parsing/mod_parser_v1.coffee similarity index 100% rename from src/client/models/parsing/mod_parser_v1.coffee rename to src/client/models-old/parsing/mod_parser_v1.coffee diff --git a/src/client/models/parsing/mod_version_parser.coffee b/src/client/models-old/parsing/mod_version_parser.coffee similarity index 100% rename from src/client/models/parsing/mod_version_parser.coffee rename to src/client/models-old/parsing/mod_version_parser.coffee diff --git a/src/client/models/parsing/mod_version_parser_v1.coffee b/src/client/models-old/parsing/mod_version_parser_v1.coffee similarity index 100% rename from src/client/models/parsing/mod_version_parser_v1.coffee rename to src/client/models-old/parsing/mod_version_parser_v1.coffee diff --git a/src/client/models/parsing/mod_version_parser_v1.test.coffee b/src/client/models-old/parsing/mod_version_parser_v1.test.coffee similarity index 100% rename from src/client/models/parsing/mod_version_parser_v1.test.coffee rename to src/client/models-old/parsing/mod_version_parser_v1.test.coffee diff --git a/src/client/models/parsing/tutorial_parser.coffee b/src/client/models-old/parsing/tutorial_parser.coffee similarity index 100% rename from src/client/models/parsing/tutorial_parser.coffee rename to src/client/models-old/parsing/tutorial_parser.coffee diff --git a/src/client/models/parsing/tutorial_parser_v1.coffee b/src/client/models-old/parsing/tutorial_parser_v1.coffee similarity index 100% rename from src/client/models/parsing/tutorial_parser_v1.coffee rename to src/client/models-old/parsing/tutorial_parser_v1.coffee diff --git a/src/client/models/parsing/versioned_parser_base.coffee b/src/client/models-old/parsing/versioned_parser_base.coffee similarity index 100% rename from src/client/models/parsing/versioned_parser_base.coffee rename to src/client/models-old/parsing/versioned_parser_base.coffee diff --git a/src/client/models-old/site/tutorial.coffee b/src/client/models-old/site/tutorial.coffee new file mode 100644 index 000000000..702f0d718 --- /dev/null +++ b/src/client/models-old/site/tutorial.coffee @@ -0,0 +1,33 @@ +# +# Crafting Guide - tutorial.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +BaseModel = require '../base_model' + +######################################################################################################################## + +module.exports = class Tutorial extends BaseModel + + constructor: (attributes={}, options={})-> + if not attributes.name?.length > 0 then throw new Error "attributes.name cannot be empty" + attributes.modSlug ?= null + attributes.officialUrl ?= null + attributes.sections ?= [] + attributes.slug ?= _.slugify attributes.name + attributes.videos ?= [] + super attributes, options + + # Backbone.Model Overrides ##################################################################### + + parse: (text)-> + TutorialParser = require '../parsing/tutorial_parser' # to avoid require cycles + @_parser ?= new TutorialParser model:this + @_parser.parse text + + return null # prevent calling `set` + + url: -> + return c.url.tutorialData modSlug:@modSlug, tutorialSlug:@slug diff --git a/src/client/models/crafting/crafting_plan.coffee b/src/client/models/crafting/crafting_plan.coffee index 98f791f49..283bb073a 100644 --- a/src/client/models/crafting/crafting_plan.coffee +++ b/src/client/models/crafting/crafting_plan.coffee @@ -5,7 +5,7 @@ # All rights reserved. # -Inventory = require "./inventory" +Inventory = require "../game/inventory" {StringBuilder} = require "crafting-guide-common" ######################################################################################################################## @@ -20,8 +20,8 @@ module.exports = class CraftingPlan @steps = attributes.steps @want = attributes.want - @_consolidateSteps() @_computeResources() + @_consolidateSteps() # Properties ################################################################################### diff --git a/src/client/models/crafting/crafting_plan.test.coffee b/src/client/models/crafting/crafting_plan.test.coffee index 83249514e..4011acd40 100644 --- a/src/client/models/crafting/crafting_plan.test.coffee +++ b/src/client/models/crafting/crafting_plan.test.coffee @@ -7,8 +7,8 @@ CraftingPlan = require './crafting_plan' CraftingPlanStep = require './crafting_plan_step' -Inventory = require './inventory' -fixtures = require './fixtures' +Inventory = require '../game/inventory' +fixtures = require '../fixtures' ######################################################################################################################## diff --git a/src/client/models/crafting/evaluation.coffee b/src/client/models/crafting/evaluation.coffee index ea4cde295..c5ff49a52 100644 --- a/src/client/models/crafting/evaluation.coffee +++ b/src/client/models/crafting/evaluation.coffee @@ -15,7 +15,7 @@ module.exports = class Evaluation @recipe = attributes.recipe if attributes.recipe? @baseScore = attributes.baseScore - @_baseEvaluations = [] + @_id = _.uniqueId "evaluation-" @_includedTools = {} @_toolScore = null @@ -23,10 +23,6 @@ module.exports = class Evaluation Object.defineProperties @prototype, - baseEvaluations: - get: -> return @_baseEvaluations - set: -> throw new Error "baseEvaluations cannot be assigned" - baseScore: get: -> return @_baseScore set: (baseScore)-> @@ -70,13 +66,15 @@ module.exports = class Evaluation # Public Methods ############################################################################### - addBaseEvaluation: (evaluation)-> - @_baseEvaluations.push evaluation - addIncludedTool: (item)-> + return if @_includedTools[item.id]? @_includedTools[item.id] = item @_toolScore = null + addIncludedToolsFrom: (evaluation)-> + for id, toolItem of evaluation.includedTools + @addIncludedTool toolItem + computeTotalScore: (quantity=1)-> if @item? multiplier = quantity @@ -86,18 +84,13 @@ module.exports = class Evaluation return @baseScore * multiplier + @toolScore isToolIncluded: (item)-> - return true if @_includedTools[item.id]? - - for baseEvaluation in @_baseEvaluations - return true if baseEvaluation.isToolIncluded item - - return false + return @_includedTools[item.id]? # Object Overrides ############################################################################# toString: -> obj = if @item? then @item else @recipe - return "#{@evaluator}=>#{obj}@#{@baseScore}" + return "#{@evaluator.constructor.name}:#{obj}@#{@baseScore}<#{@_id}>" # Private Methods ############################################################################## diff --git a/src/client/models/crafting/evaluator.coffee b/src/client/models/crafting/evaluator.coffee index 079e56384..386550090 100644 --- a/src/client/models/crafting/evaluator.coffee +++ b/src/client/models/crafting/evaluator.coffee @@ -34,8 +34,8 @@ module.exports = class Evaluator recipeEvaluation = @_findBestRecipeEvaluationFor item if recipeEvaluation? - evaluation.addBaseEvaluation recipeEvaluation evaluation.baseScore = recipeEvaluation.baseScore + evaluation.addIncludedToolsFrom recipeEvaluation else @_computeGatherableItemScore item, evaluation @@ -49,12 +49,13 @@ module.exports = class Evaluator evaluation = @_evaluations[recipe.id] = new Evaluation evaluator:this, recipe:recipe @_computeRecipeScore recipe, evaluation + for id, item of recipe.inputs + inputEvaluation = @evaluateItem item + evaluation.addIncludedToolsFrom inputEvaluation + for id, toolItem of recipe.tools evaluation.addIncludedTool toolItem - - for id, toolItem of @evaluateItem(toolItem).includedTools - evaluation.addIncludedTool toolItem - + evaluation.addIncludedToolsFrom @evaluateItem toolItem return evaluation @@ -79,24 +80,23 @@ module.exports = class Evaluator # Overrideable Methods ######################################################################### - _computeRecipeScore: (recipe, evaluation)-> - throw new Error "#{@constructor.name} must override _computeRecipeScore" - _computeGatherableItemScore: (item, evaluation)-> throw new Error "#{@constructor.name} must override _computeGatherableItemScore" + _computeRecipeScore: (recipe, evaluation)-> + throw new Error "#{@constructor.name} must override _computeRecipeScore" + # Private Methods ############################################################################## _findBestRecipeEvaluationFor: (item)-> return null if item.isGatherable result = null - for recipeMap in [item.recipes, item.recipesAsExtra] - for id, recipe of recipeMap - evaluation = @evaluateRecipe recipe - continue unless evaluation?.baseScore? + for id, recipe of item.recipes + evaluation = @evaluateRecipe recipe + continue unless evaluation?.baseScore? - if not result? then result = evaluation - if evaluation.baseScore < result.baseScore then result = evaluation + if not result? then result = evaluation + if evaluation.baseScore < result.baseScore then result = evaluation return result diff --git a/src/client/models/crafting/inventory.coffee b/src/client/models/crafting/inventory.coffee deleted file mode 100644 index 4eadaa7ba..000000000 --- a/src/client/models/crafting/inventory.coffee +++ /dev/null @@ -1,90 +0,0 @@ -# -# Crafting Guide - inventory.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Stack = require './stack' - -######################################################################################################################## - -module.exports = class Inventory - - constructor: (inventory=null)-> - @_id = _.uniqueId "inventory-" - @_stacks = {} - - if inventory? then @merge inventory - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - isEmpty: - get: -> (id for id, stack of @_stacks).length is 0 - - stacks: - get: -> return @_stacks - set: -> throw new Error "stacks cannot be replaced" - - # Public Methods ############################################################################### - - add: (item, quantity)-> - return unless item? - return if quantity is 0 - - existingStack = @_stacks[item.id] - if existingStack? - if existingStack.quantity + quantity < 0 then throw new Error "cannot have a negative quantity" - existingStack.quantity += quantity - else - if quantity < 0 then throw new Error "cannot have a negative quantity" - @_stacks[item.id] = new Stack item:item, quantity:quantity - - if @_stacks[item.id].quantity is 0 - delete @_stacks[item.id] - - clear: -> - @_stacks = {} - - contains: (item)-> - return @_stacks[item.id]? - - getQuantity: (item)-> - existingStack = @_stacks[item.id] - return 0 unless existingStack? - return existingStack.quantity - - merge: (inventory)-> - for id, stack of inventory.stacks - @add stack.item, stack.quantity - - remove: (item, quantity)-> - @add item, -1 * quantity - - # Object Overrides ############################################################################# - - toString: (options={})-> - options.full ?= false - - if options.full - result = [] - needsDelimiter = false - - stackList = (stack for itemId, stack of @_stacks) - stackList.sort (a, b)-> - if a.item.displayName isnt b.item.displayName - return if a.item.displayName < b.item.displayName then -1 else +1 - return 0 - - for stack in stackList - if needsDelimiter then result.push ", " - needsDelimiter = true - - result.push stack.quantity - result.push " " - result.push stack.item.displayName - return result.join "" - else - return "Inventory<#{@_id}>@#{(id for id, item of @_stacks).length}" diff --git a/src/client/models/crafting/item.coffee b/src/client/models/crafting/item.coffee deleted file mode 100644 index beb259cfe..000000000 --- a/src/client/models/crafting/item.coffee +++ /dev/null @@ -1,93 +0,0 @@ -# -# Crafting Guide - item.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class Item - - constructor: (attributes={})-> - @id = attributes.id - @displayName = attributes.displayName - @isGatherable = attributes.isGatherable - @mod = attributes.mod - - @_hasPrimaryRecipe = false - @_recipesAsPrimary = {} - @_recipesAsExtra = {} - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - displayName: - get: -> return @_displayName - set: (displayName)-> - if not displayName? then throw new Error "displayName is required" - @_displayName = displayName - - firstRecipe: - get: -> return recipe for id, recipe of @recipes - set: -> throw new Error "firstRecipe cannot be assigned" - - id: - get: -> return @_id - set: (id)-> - if not id? then throw new Error "id is required" - return if @_id is id - if @_id? then throw new Error "id cannot be reassigned" - @_id = id - - isGatherable: - get: -> - return true if @_isGatherable is true - return false if (id for id, recipe of @_recipesAsPrimary).length > 0 - return false if (id for id, recipe of @_recipesAsExtra).length > 0 - return true - set: (isGatherable)-> - @_isGatherable = null unless isGatherable? - @_isGatherable = !!isGatherable - - mod: - get: -> return @_mod - set: (mod)-> - if not mod? then throw new Error "mod is required" - if @_mod is mod then return - if @_mod? then throw new Error "mod cannot be reassigned" - @_mod = mod - @_mod.addItem this - - modPack: - get: -> return @_mod.modPack - set: -> throw new Error "modPack cannot be replaced" - - recipes: - get: -> return if @_hasPrimaryRecipe then @_recipesAsPrimary else @_recipesAsExtra - set: -> throw new Error "recipes cannot be assigned" - - recipesAsPrimary: - get: -> return @_recipesAsPrimary - set: -> throw new Error "recipes cannot be assigned" - - recipesAsExtra: - get: -> return @_recipesAsExtra - set: -> throw new Error "recipesAsExtra cannot be assigned" - - # Public Recipes ############################################################################### - - addRecipe: (recipe)-> - if recipe.output.item is this - @_recipesAsPrimary[recipe.id] = recipe - @_hasPrimaryRecipe = true - else if recipe.extras[this.id] is this - @_recipesAsExtra[recipe.id] = recipe - else - throw new Error "recipe<#{recipe.id}> does not produce this item<#{@id}>" - - # Object Overrides ############################################################################# - - toString: -> - return "Item:#{@displayName}<#{@id}>" diff --git a/src/client/models/crafting/mod.coffee b/src/client/models/crafting/mod.coffee deleted file mode 100644 index b4e013b86..000000000 --- a/src/client/models/crafting/mod.coffee +++ /dev/null @@ -1,62 +0,0 @@ -# -# Crafting Guide - mod.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class Mod - - constructor: (attributes={})-> - @id = attributes.id - @displayName = attributes.displayName - @modPack = attributes.modPack - - @_items = {} - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - displayName: - get: -> return @_displayName - set: (displayName)-> - if not displayName? then throw new Error "displayName is required" - return if @_displayName is displayName - @_displayName = displayName - - id: - get: -> return @_id - set: (id)-> - if not id? then throw new Error "id is required" - return if @_id is id - if @_id? then throw new Error "id cannot be reassigned" - @_id = id - - items: - get: -> return @_items - set: -> throw new Error "items cannot be replaced" - - modPack: - get: -> return @_modPack - set: (modPack)-> - if not modPack? then throw new Error "modPack is required" - if @_modPack is modPack then return - if @_modPack? then throw new Error "modPack cannot be reassigned" - @_modPack = modPack - @_modPack.addMod this - - # Public Methods ############################################################################### - - addItem: (item)-> - if not item? then return - if @_items[item.id] is item then return - @_items[item.id] = item - item.mod = this - - # Object Overrides ############################################################################# - - toString: -> - return "Mod:#{@displayName}<#{@id}>" diff --git a/src/client/models/crafting/mod_pack.coffee b/src/client/models/crafting/mod_pack.coffee deleted file mode 100644 index 2272fef3c..000000000 --- a/src/client/models/crafting/mod_pack.coffee +++ /dev/null @@ -1,52 +0,0 @@ -# -# Crafting Guide - mod_pack.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class ModPack - - constructor: (attributes={})-> - @id = attributes.id - @displayName = attributes.displayName - - @_mods = {} - @_oreDict = {} - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - displayName: - get: -> return @_displayName - set: (displayName)-> - if not displayName? then throw new Error "displayName is required" - if @_displayName is displayName then return - @_displayName = displayName - - id: - get: -> return @_id - set: (id)-> - if not id? then throw new Error "id is required" - if @_id is id then return - if @_id? then throw new Error "id cannot be reassigned" - - mods: - get: -> return @_mods - set: -> throw new Error "mods cannot be replaced" - - # Public Methods ############################################################################### - - addMod: (mod)-> - if not mod? then return - if @_mods[mod.id] is mod then return - @_mods[mod.id] = mod - mod.modPack = this - - # Object Overrides ############################################################################# - - toString: -> - return "ModPack:#{@displayName}<#{@id}>" diff --git a/src/client/models/crafting/plan_builder.test.coffee b/src/client/models/crafting/plan_builder.test.coffee index bb2224457..e374fc102 100644 --- a/src/client/models/crafting/plan_builder.test.coffee +++ b/src/client/models/crafting/plan_builder.test.coffee @@ -5,14 +5,14 @@ # All rights reserved. # -fixtures = require './fixtures' -Inventory = require './inventory' +fixtures = require '../fixtures' +Inventory = require '../game/inventory' PlanBuilder = require './plan_builder' ResourcesEvaluator = require './resources_evaluator' ######################################################################################################################## -describe.only "PlanBuilder", -> +describe "PlanBuilder", -> beforeEach -> @planner = new PlanBuilder new ResourcesEvaluator diff --git a/src/client/models/crafting/recipe.coffee b/src/client/models/crafting/recipe.coffee deleted file mode 100644 index 5c1cbe6fa..000000000 --- a/src/client/models/crafting/recipe.coffee +++ /dev/null @@ -1,148 +0,0 @@ -# -# Crafting Guide - recipe.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -{StringBuilder} = require 'crafting-guide-common' - -######################################################################################################################## - -module.exports = class Recipe - - constructor: (attributes={})-> - @id = attributes.id - @height = attributes.height - @output = attributes.output - @width = attributes.width - - @_extras = {} - @_inputs = {} - @_inputGrid = [] - @_tools = {} - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - allProducts: # an array of Stacks starting the the primary output of this recipe - get: -> return [].concat @output, (stack for id, stack of @extras) - set: -> throw new Error "allProducts cannot be assigned" - - extras: # a hash of item id to Stack of all the non-primary outputs of this recipe - get: -> return @_extras - set: -> throw new Error "extras cannot be replaced" - - id: # a string which uniquely identifies this recipe - get: -> return @_id - set: (id)-> - if not id? then throw new Error "id is required" - if @_id is id then return - if @_id? then throw new Error "id cannot be reassigned" - @_id = id - - height: # an integer specifying the number of rows needed by this recipe - get: -> return @_height - set: (height)-> - height = parseInt "#{height}" - height = if Number.isNaN(height) then 0 else Math.max(0, height) - @_height = height - - inputs: # a hash of item id to Item containing all the inputs to this recipe - get: -> return @_inputs - set: -> throw new Error "inputs cannot be replaced" - - needsTools: # a boolean indicating whether this recipe requires a tool - get: -> return (id for id, toolItem of @_tools).length > 0 - - output: # a Stack specifying the primary output of this recipe - get: -> return @_output - set: (output)-> - if not output? then throw new Error "output is required" - if @_output is output then return - if @_output? then throw new Error "output cannot be reassigned" - @_output = output - @_output.item.addRecipe this - - modPack: # the ModPack to which this recipe belongs - get: -> return @_output.modPack - set: -> throw new Error "modPack cannot be replaced" - - tools: # a hash of item id to Item of all the tools required for this recipe - get: -> return @_tools - set: -> throw new Error "tools cannot be assigned" - - width: # an integer specifying the number of columns needed by this recipe - get: -> return @_width - set: (width)-> - width = parseInt "#{width}" - width = if Number.isNaN(width) then 0 else Math.max(0, width) - @_width = width - - # Public Methods ############################################################################### - - addExtra: (stack)-> - return unless stack - @_extras[stack.item.id] = stack - - addTool: (item)-> - return unless item - @_tools[item.id] = item - - computeQuantityRequired: (item)-> - result = 0 - - for row in [0...@height] - for col in [0...@width] - stack = @_inputGrid[row]?[col] - continue unless stack? - continue unless stack.item.id is item.id - result += stack.quantity - - return result - - computeQuantityProduced: (item)-> - result = 0 - - if @_output.item.id is item.id - result += @_output.quantity - - for itemId, stack of @_extras - continue unless itemId is item.id - result += stack.quantity - - return result - - getInputAt: (row, col)-> - return @_inputGrid[row]?[col] - - setInputAt: (row, col, stack)-> - @_height = Math.max @_height, row + 1 - @_width = Math.max @_width, col + 1 - - @_inputGrid[row] ?= [] - @_inputGrid[row][col] = stack - @_inputs[stack.item.id] = stack.item - - # Object Overrides ############################################################################# - - toString: (options={})-> - options.full ?= false - - if options.full - b = new StringBuilder - b.loop (item for id, item of @inputs), delimiter:" + ", onEach:(b, item)=> - b.push @computeQuantityRequired(item), " ", item.displayName - b.push " =" - b.onlyIf @needsTools, (b)=> - b.push "(" - b.loop (toolItem for id, toolItem of @tools), onEach:(b, toolItem)-> b.push toolItem.displayName - b.push ")" - b.push "=> " - b.loop @allProducts, delimiter:" + ", onEach:(b, stack)=> - b.push stack.quantity, " ", stack.item.displayName - - return b.toString() - else - return "Recipe:#{@output}<#{@id}>" diff --git a/src/client/models/crafting/resources_evaluator.coffee b/src/client/models/crafting/resources_evaluator.coffee index 51c5b6655..55756cc0d 100644 --- a/src/client/models/crafting/resources_evaluator.coffee +++ b/src/client/models/crafting/resources_evaluator.coffee @@ -16,29 +16,24 @@ module.exports = class ResourcesEvaluator extends Evaluator _computeRecipeScore: (recipe, evaluation)-> evaluation.baseScore = 0 - for row in [0...recipe.height] - for col in [0...recipe.width] - stack = recipe.getInputAt row, col - continue unless stack? + for x in [0...recipe.width] + for y in [0...recipe.height] + for z in [0...recipe.depth] + stack = recipe.getInputAt x, y, z + continue unless stack? - inputEvaluation = @evaluateItem stack.item - if inputEvaluation?.baseScore? - evaluation.baseScore += inputEvaluation.baseScore * stack.quantity - evaluation.addBaseEvaluation inputEvaluation - else - evaluation.baseScore = null - logger.outdent() - return + inputEvaluation = @evaluateItem stack.item + if inputEvaluation?.baseScore? + evaluation.baseScore += inputEvaluation.baseScore * stack.quantity + else + evaluation.baseScore = null + return for id, extraStack of recipe.extras extraEvaluation = @evaluateItem extraStack.item continue unless extraEvaluation?.baseScore? evaluation.baseScore -= extraStack.quantity * extraEvaluation.baseScore - for id, toolItem of recipe.tools - toolEvaluation = @evaluateItem toolItem - evaluation.addBaseEvaluation toolEvaluation - evaluation.baseScore = evaluation.baseScore / recipe.output.quantity _computeGatherableItemScore: (item, evaluation)-> diff --git a/src/client/models/crafting/resources_evaluator.test.coffee b/src/client/models/crafting/resources_evaluator.test.coffee index 29b716fee..324a3087c 100644 --- a/src/client/models/crafting/resources_evaluator.test.coffee +++ b/src/client/models/crafting/resources_evaluator.test.coffee @@ -6,7 +6,7 @@ # ResourcesEvaluator = require './resources_evaluator' -fixtures = require './fixtures' +fixtures = require '../fixtures' ######################################################################################################################## @@ -123,3 +123,7 @@ describe "ResourcesEvaluator", -> # 1 oak wood ==> 4 oak planks (0.25) # 4 planks ==> 1 crafting table (1) @evaluation.computeTotalScore().should.equal 18 + + describe 'evaluating a recipe which is only ever made as an extra', -> + + it 'should still be able to come up with an evaluation' diff --git a/src/client/models/crafting/stack.coffee b/src/client/models/crafting/stack.coffee deleted file mode 100644 index aa8541cfc..000000000 --- a/src/client/models/crafting/stack.coffee +++ /dev/null @@ -1,42 +0,0 @@ -# -# Crafting Guide - stack.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class Stack - - constructor: (attributes={})-> - @item = attributes.item - @quantity = attributes.quantity - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - item: - get: -> return @_item - set: (item)-> - if not item? then throw new Error "item is required" - if @_item is item then return - if @_item? then throw new Error "item cannot be reassigned" - @_item = item - - modPack: - get: -> return @_item.modPack - set: -> throw new Error "modPack cannot be replaced" - - quantity: - get: -> return @_quantity - set: (quantity)-> - quantity = parseInt "#{quantity}" - quantity = if Number.isNaN(quantity) then 0 else Math.max(0, quantity) - @_quantity = quantity - - # Object Overrides ############################################################################# - - toString: -> - return "Stack:#{@item}×#{@quantity}" \ No newline at end of file diff --git a/src/client/models/crafting/steps_evaluator.coffee b/src/client/models/crafting/steps_evaluator.coffee index 3e938cb69..41472ce73 100644 --- a/src/client/models/crafting/steps_evaluator.coffee +++ b/src/client/models/crafting/steps_evaluator.coffee @@ -22,7 +22,6 @@ module.exports = class StepsEvaluator extends Evaluator evaluation.score = null return - evaluation.addBaseEvaluation inputEvaluation evaluation.score = Math.min evaluation.score, inputEvaluation.score + 1 for id, item of recipe.tools @@ -33,7 +32,6 @@ module.exports = class StepsEvaluator extends Evaluator evaluation.score = null return - evaluation.addBaseEvaluation evaluation evaluation.addIncludedTool item evaluation.score += toolEvaluation.score diff --git a/src/client/models/crafting/fixtures.coffee b/src/client/models/fixtures.coffee similarity index 77% rename from src/client/models/crafting/fixtures.coffee rename to src/client/models/fixtures.coffee index 7b2751c35..ebc346a09 100644 --- a/src/client/models/crafting/fixtures.coffee +++ b/src/client/models/fixtures.coffee @@ -5,11 +5,11 @@ # All rights reserved. # -Item = require "./item" -Mod = require "./mod" -ModPack = require "./mod_pack" -Recipe = require "./recipe" -Stack = require "./stack" +Item = require "./game/item" +Mod = require "./game/mod" +ModPack = require "./game/mod_pack" +Recipe = require "./game/recipe" +Stack = require "./game/stack" # Instance Creation Fixtures ########################################################################################### @@ -43,7 +43,7 @@ exports.createStack = createStack = (attributes={})-> # Item Configuration Fixtures ########################################################################################## exports.configureBucket = configureBucket = (mod)-> - bucket = mod.items["bucket"] + bucket = mod.modPack.findItem "bucket" if not bucket? ironIngot = configureIronIngot mod bucket = createItem mod:mod, id:"bucket", displayName:"Bucket" @@ -52,13 +52,13 @@ exports.configureBucket = configureBucket = (mod)-> recipe = createRecipe output:createStack item:bucket recipe.setInputAt 0, 0, createStack item:ironIngot recipe.setInputAt 1, 1, createStack item:ironIngot - recipe.setInputAt 0, 2, createStack item:ironIngot + recipe.setInputAt 2, 0, createStack item:ironIngot recipe.addTool craftingTable return bucket exports.configureCake = configureCake = (mod)-> - cake = mod.items["cake"] + cake = mod.modPack.findItem "cake" if not cake? bucket = configureBucket mod cake = createItem mod:mod, id:"cake", displayName:"Cake" @@ -70,13 +70,13 @@ exports.configureCake = configureCake = (mod)-> recipe = createRecipe output:createStack item:cake recipe.setInputAt 0, 0, createStack item:milkBucket - recipe.setInputAt 0, 1, createStack item:milkBucket - recipe.setInputAt 0, 2, createStack item:milkBucket - recipe.setInputAt 1, 0, createStack item:sugar + recipe.setInputAt 1, 0, createStack item:milkBucket + recipe.setInputAt 2, 0, createStack item:milkBucket + recipe.setInputAt 0, 1, createStack item:sugar recipe.setInputAt 1, 1, createStack item:egg - recipe.setInputAt 1, 2, createStack item:sugar - recipe.setInputAt 2, 0, createStack item:wheat - recipe.setInputAt 2, 1, createStack item:wheat + recipe.setInputAt 2, 1, createStack item:sugar + recipe.setInputAt 0, 2, createStack item:wheat + recipe.setInputAt 1, 2, createStack item:wheat recipe.setInputAt 2, 2, createStack item:wheat recipe.addTool craftingTable recipe.addExtra createStack item:bucket, quantity:3 @@ -84,39 +84,39 @@ exports.configureCake = configureCake = (mod)-> return cake exports.configureCoal = configureCoal = (mod)-> - coal = mod.items["coal"] + coal = mod.modPack.findItem "coal" if not coal? - coal = createItem mod:mod, id:"coal", displayName:"Coal", isGatherable:true + coal = createItem mod:mod, id:"coal", displayName:"Coal" return coal exports.configureCobblestone = configureCobblestone = (mod)-> - cobblestone = mod.items["cobblestone"] + cobblestone = mod.modPack.findItem "cobblestone" if not cobblestone? - cobblestone = createItem mod:mod, displayName:"Cobblestone", isGatherable:true + cobblestone = createItem mod:mod, displayName:"Cobblestone" return cobblestone exports.configureCraftingTable = configureCraftingTable = (mod)-> - craftingTable = mod.items["crafting_table"] + craftingTable = mod.modPack.findItem "crafting_table" if not craftingTable? craftingTable = createItem mod:mod, id:"crafting_table", displayName:"Crafting Table" oakPlanks = configureOakPlank mod recipe = createRecipe output:createStack item:craftingTable recipe.setInputAt 0, 0, createStack item:oakPlanks - recipe.setInputAt 0, 1, createStack item:oakPlanks recipe.setInputAt 1, 0, createStack item:oakPlanks + recipe.setInputAt 0, 1, createStack item:oakPlanks recipe.setInputAt 1, 1, createStack item:oakPlanks return craftingTable exports.configureEgg = configureEgg = (mod)-> - egg = mod.items["egg"] + egg = mod.modPack.findItem "egg" if not egg? - egg = createItem mod:mod, id:"egg", displayName:"Egg", isGatherable:true + egg = createItem mod:mod, id:"egg", displayName:"Egg" return egg exports.configureFurnace = configureFurnace = (mod)-> - furnace = mod.items["furnace"] + furnace = mod.modPack.findItem "furnace" if not furnace? cobblestone = configureCobblestone mod craftingTable = configureCraftingTable mod @@ -124,19 +124,19 @@ exports.configureFurnace = configureFurnace = (mod)-> recipe = createRecipe output:createStack item:furnace recipe.setInputAt 0, 0, createStack item:cobblestone - recipe.setInputAt 0, 1, createStack item:cobblestone - recipe.setInputAt 0, 2, createStack item:cobblestone recipe.setInputAt 1, 0, createStack item:cobblestone - recipe.setInputAt 1, 2, createStack item:cobblestone recipe.setInputAt 2, 0, createStack item:cobblestone + recipe.setInputAt 0, 1, createStack item:cobblestone recipe.setInputAt 2, 1, createStack item:cobblestone + recipe.setInputAt 0, 2, createStack item:cobblestone + recipe.setInputAt 1, 2, createStack item:cobblestone recipe.setInputAt 2, 2, createStack item:cobblestone recipe.addTool craftingTable return furnace exports.configureIronIngot = configureIronIngot = (mod)-> - ironIngot = mod.items["iron_ingot"] + ironIngot = mod.modPack.findItem "iron_ingot" if not ironIngot? coal = configureCoal mod furnace = configureFurnace mod @@ -144,23 +144,23 @@ exports.configureIronIngot = configureIronIngot = (mod)-> ironOre = configureIronOre mod recipe = createRecipe output:createStack item:ironIngot, quantity:8 - recipe.setInputAt 0, 1, createStack item:ironOre, quantity:8 - recipe.setInputAt 2, 1, createStack item:coal + recipe.setInputAt 1, 0, createStack item:ironOre, quantity:8 + recipe.setInputAt 1, 2, createStack item:coal recipe.addTool furnace return ironIngot exports.configureIronBlock = configureIronBlock = (mod)-> - ironBlock = mod.items["iron_block"] + ironBlock = mod.modPack.findItem "iron_block" if not ironBlock? craftingTable = configureCraftingTable mod ironBlock = createItem mod:mod, id:"iron_block", displayName:"Iron Block" ironIngot = configureIronIngot mod recipe = createRecipe output:createStack item:ironBlock - for row in [0..2] - for col in [0..2] - recipe.setInputAt row, col, createStack item:ironIngot + for x in [0..2] + for y in [0..2] + recipe.setInputAt x, y, createStack item:ironIngot recipe.addTool craftingTable recipe = createRecipe output:createStack item:ironIngot, quantity:9 @@ -169,7 +169,7 @@ exports.configureIronBlock = configureIronBlock = (mod)-> return ironBlock exports.configureIronSword = configureIronSword = (mod)-> - ironSword = mod.items["iron_sword"] + ironSword = mod.modPack.findItem "iron_sword" if not ironSword? craftingTable = configureCraftingTable mod ironIngot = configureIronIngot mod @@ -177,15 +177,15 @@ exports.configureIronSword = configureIronSword = (mod)-> stick = configureStick mod recipe = createRecipe output:createStack item:ironSword - recipe.setInputAt 0, 1, createStack item:ironIngot + recipe.setInputAt 1, 0, createStack item:ironIngot recipe.setInputAt 1, 1, createStack item:ironIngot - recipe.setInputAt 2, 1, createStack item:stick + recipe.setInputAt 1, 2, createStack item:stick recipe.addTool craftingTable return ironSword exports.configureIronShovel = configureIronShovel = (mod)-> - ironShovel = mod.items["iron_shovel"] + ironShovel = mod.modPack.findItem "iron_shovel" if not ironShovel? craftingTable = configureCraftingTable mod ironIngot = configureIronIngot mod @@ -193,40 +193,40 @@ exports.configureIronShovel = configureIronShovel = (mod)-> stick = configureStick mod recipe = createRecipe output:createStack item:ironShovel - recipe.setInputAt 0, 1, createStack item:ironIngot + recipe.setInputAt 1, 0, createStack item:ironIngot recipe.setInputAt 1, 1, createStack item:stick - recipe.setInputAt 2, 1, createStack item:stick + recipe.setInputAt 1, 2, createStack item:stick recipe.addTool craftingTable return ironShovel exports.configureIronOre = configureIronOre = (mod)-> - ironOre = mod.items["iron_ore"] + ironOre = mod.modPack.findItem "iron_ore" if not ironOre? - ironOre = createItem mod:mod, id:"iron_ore", displayName:"Iron Ore", isGatherable:true + ironOre = createItem mod:mod, id:"iron_ore", displayName:"Iron Ore" return ironOre exports.configureMilk = configureMilk = (mod)-> - milk = mod.items["milk"] + milk = mod.modPack.findItem "milk" if not milk? - milk = createItem mod:mod, id:"milk", displayName:"Milk", isGatherable:true + milk = createItem mod:mod, id:"milk", displayName:"Milk" return milk exports.configureMilkBucket = configureMilkBucket = (mod)-> - milkBucket = mod.items["milk_bucket"] + milkBucket = mod.modPack.findItem "milk_bucket" if not milkBucket? bucket = configureBucket mod milk = configureMilk mod milkBucket = createItem mod:mod, id:"milkBucket", displayName:"Milk Bucket" recipe = createRecipe output:createStack item:milkBucket - recipe.setInputAt 0, 1, createStack item:milk + recipe.setInputAt 1, 0, createStack item:milk recipe.setInputAt 1, 1, createStack item:bucket return milkBucket exports.configureOakPlank = configureOakPlank = (mod)-> - oakPlanks = mod.items["oak_planks"] + oakPlanks = mod.modPack.findItem "oak_planks" if not oakPlanks? oakPlanks = createItem mod:mod, id:"oak_planks", displayName:"Oak Planks" oakWood = configureOakWood mod @@ -237,31 +237,37 @@ exports.configureOakPlank = configureOakPlank = (mod)-> return oakPlanks exports.configureOakWood = configureOakWood = (mod)-> - oakWood = mod.items["oak_wood"] + oakWood = mod.modPack.findItem "oak_wood" if not oakWood? - oakWood = createItem mod:mod, id:"oak_wood", displayName:"Oak Wood", isGatherable:true + oakWood = createItem mod:mod, id:"oak_wood", displayName:"Oak Wood" return oakWood +exports.configureObsidian = configureObsidian = (mod)-> + obsidian = mod.modPack.findItem "obsidian" + if not obsidian? + obsidian = createItem mod:mod, id:"obsidian", displayName:"Obsidian" + return obsidian + exports.configureRedstoneDust = configureRedstoneDust = (mod)-> - redstoneDust = mod.items["redstone_dust"] + redstoneDust = mod.modPack.findItem "redstone_dust" if not redstoneDust? - redstoneDust = createItem mod:mod, id:"redstone_dust", displayName:"Redstone Dust", isGatherable:true + redstoneDust = createItem mod:mod, id:"redstone_dust", displayName:"Redstone Dust" return redstoneDust exports.configureStick = configureStick = (mod)-> - stick = mod.items["stick"] + stick = mod.modPack.findItem "stick" if not stick? oakPlanks = configureOakPlank mod stick = createItem mod:mod, id:"stick", displayName:"Stick" recipe = createRecipe output:createStack item:stick, quantity:4 recipe.setInputAt 0, 0, createStack item:oakPlanks - recipe.setInputAt 1, 0, createStack item:oakPlanks + recipe.setInputAt 0, 1, createStack item:oakPlanks return stick exports.configureSugar = configureSugar = (mod)-> - sugar = mod.items["sugar"] + sugar = mod.modPack.findItem "sugar" if not sugar? sugar = createItem mod:mod, id:"sugar", displayName:"Sugar" sugarCane = configureSugarCane mod @@ -272,7 +278,7 @@ exports.configureSugar = configureSugar = (mod)-> return sugar exports.configureSaw = configureSaw = (mod)-> - saw = mod.items["saw"] + saw = mod.modPack.findItem "saw" if not saw? craftingTable = configureCraftingTable mod ironBlock = configureIronBlock mod @@ -284,13 +290,13 @@ exports.configureSaw = configureSaw = (mod)-> recipe = createRecipe output:createStack item:saw recipe.setInputAt 0, 0, createStack item:oakPlank - recipe.setInputAt 0, 1, createStack item:ironIngot - recipe.setInputAt 0, 2, createStack item:oakPlank - recipe.setInputAt 1, 0, createStack item:oakPlank - recipe.setInputAt 1, 1, createStack item:ironBlock - recipe.setInputAt 1, 2, createStack item:oakPlank + recipe.setInputAt 1, 0, createStack item:ironIngot recipe.setInputAt 2, 0, createStack item:oakPlank - recipe.setInputAt 2, 1, createStack item:redstoneDust + recipe.setInputAt 0, 1, createStack item:oakPlank + recipe.setInputAt 1, 1, createStack item:ironBlock + recipe.setInputAt 2, 1, createStack item:oakPlank + recipe.setInputAt 0, 2, createStack item:oakPlank + recipe.setInputAt 1, 2, createStack item:redstoneDust recipe.setInputAt 2, 2, createStack item:oakPlank recipe.addTool craftingTable @@ -301,13 +307,13 @@ exports.configureSaw = configureSaw = (mod)-> return saw exports.configureSugarCane = configureSugarCane = (mod)-> - sugarCane = mod.items["sugar_cane"] + sugarCane = mod.modPack.findItem "sugar_cane" if not sugarCane? - sugarCane = createItem mod:mod, id:"sugar_cane", displayName:"Sugar Cane", isGatherable:true + sugarCane = createItem mod:mod, id:"sugar_cane", displayName:"Sugar Cane" return sugarCane exports.configureWheat = configureWheat = (mod)-> - wheat = mod.items["wheat"] + wheat = mod.modPack.findItem "wheat" if not wheat? - wheat = createItem mod:mod, id:"wheat", displayName:"Wheat", isGatherable:true + wheat = createItem mod:mod, id:"wheat", displayName:"Wheat" return wheat diff --git a/src/client/models/game/inventory.coffee b/src/client/models/game/inventory.coffee index a024cb2d7..4eadaa7ba 100644 --- a/src/client/models/game/inventory.coffee +++ b/src/client/models/game/inventory.coffee @@ -5,239 +5,86 @@ # All rights reserved. # -BaseModel = require '../base_model' -ItemSlug = require './item_slug' -Stack = require './stack' +Stack = require './stack' ######################################################################################################################## -module.exports = class Inventory extends BaseModel +module.exports = class Inventory - constructor: (attributes={}, options={})-> - super attributes, options - attributes.modPack ?= null - @clear() + constructor: (inventory=null)-> + @_id = _.uniqueId "inventory-" + @_stacks = {} - if options.clone? - @addInventory options.clone + if inventory? then @merge inventory - # Class Methods ################################################################################ + # Properties ################################################################################### - @Delimiters = - Item: '.' - Stack: ':' + Object.defineProperties @prototype, + + isEmpty: + get: -> (id for id, stack of @_stacks).length is 0 + + stacks: + get: -> return @_stacks + set: -> throw new Error "stacks cannot be replaced" # Public Methods ############################################################################### - add: (itemSlug, quantity=1, options={})-> - return this unless quantity > 0 + add: (item, quantity)-> + return unless item? + return if quantity is 0 - @_add itemSlug, quantity, options - @trigger c.event.add, this, itemSlug, quantity - @trigger c.event.change, this - return this + existingStack = @_stacks[item.id] + if existingStack? + if existingStack.quantity + quantity < 0 then throw new Error "cannot have a negative quantity" + existingStack.quantity += quantity + else + if quantity < 0 then throw new Error "cannot have a negative quantity" + @_stacks[item.id] = new Stack item:item, quantity:quantity - addInventory: (inventory)-> - inventory.each (stack)=> @_add stack.itemSlug, stack.quantity + if @_stacks[item.id].quantity is 0 + delete @_stacks[item.id] - @trigger c.event.change, this - return this - - clear: (options={})-> + clear: -> @_stacks = {} - @_itemSlugs = [] - @trigger c.event.change, this + contains: (item)-> + return @_stacks[item.id]? - clone: -> - inventory = new Inventory - inventory.addInventory this - return inventory + getQuantity: (item)-> + existingStack = @_stacks[item.id] + return 0 unless existingStack? + return existingStack.quantity - each: (callback)-> - for itemSlug in @_itemSlugs - stack = @_stacks[itemSlug] - continue unless stack? - callback stack + merge: (inventory)-> + for id, stack of inventory.stacks + @add stack.item, stack.quantity - getSlugs: -> - return @_itemSlugs[..] - - hasAtLeast: (itemSlug, quantity=1)-> - if quantity is 0 then return true - - stack = @_stacks[itemSlug] - return false unless stack? - return stack.quantity >= quantity - - localize: -> - if not @modPack? then throw new Error 'localize requires @modPack' - - changed = false - newSlugs = [] - newStacks = [] - for itemSlug in @_itemSlugs - stack = @_stacks[itemSlug] - continue unless stack? - - qualifiedSlug = if itemSlug.isQualified then itemSlug else null - if not qualifiedSlug? - qualifiedSlug = @modPack.findItem(itemSlug)?.slug - changed = qualifiedSlug? - - if qualifiedSlug? - newSlugs.push qualifiedSlug - newStacks.push new Stack itemSlug:qualifiedSlug, quantity:stack.quantity - else - newSlugs.push itemSlug - newStacks.push stack - - if changed - for itemSlug, stack of @_stacks - @stopListening stack - - @_itemSlugs = newSlugs - @_stacks = {} - for stack in newStacks - @_stacks[stack.itemSlug] = stack - @listenTo stack, c.event.change, => @trigger c.event.change, this - - @_sort() - @trigger c.event.change, this - - pop: -> - itemSlug = @_itemSlugs.pop() - return null unless itemSlug? - - stack = @_stacks[itemSlug] - delete @_stacks[itemSlug] - - @trigger c.event.remove, this, stack.itemSlug, stack.quantity - @trigger c.event.change, this - return stack - - quantityOf: (itemSlug)-> - stack = @_stacks[itemSlug] - return stack.quantity if stack - return 0 - - remove: (itemSlug, quantity=null)-> - stack = @_stacks[itemSlug] - return this unless stack? - - quantity ?= stack.quantity - return this unless quantity > 0 - - if stack.quantity < quantity - throw new Error "cannot remove #{quantity}: only #{stack.quantity} #{itemSlug} in this inventory" - - stack.quantity -= quantity - if stack.quantity is 0 - @stopListening stack - delete @_stacks[itemSlug] - @_itemSlugs = (s for s in @_itemSlugs when not ItemSlug.equal(s, itemSlug)) - - @trigger c.event.remove, this, itemSlug, quantity - @trigger c.event.change, this - return this - - toDescription: -> - return null if @isEmpty - return null unless @modPack? - - item = @modPack.findItem @_itemSlugs[0] - extras = @_itemSlugs.length - 1 - - result = "#{item.name}" - if extras > 0 then result += " and #{extras} more..." - - return result - - # Parsing Methods ############################################################################## - - parse: (data)-> - return this if not data? or data.length is 0 - - stacks = data.split Inventory.Delimiters.Stack - for stackText in stacks - stackParts = stackText.split Inventory.Delimiters.Item - if stackParts.length is 2 - quantity = parseInt stackParts[0], 10 - itemSlug = ItemSlug.slugify stackParts[1] - else if stackParts.length is 1 - quantity = 1 - itemSlug = ItemSlug.slugify stackParts[0] - else - throw new Error "expected #{stackText} to have 0 or 1 parts" - - if itemSlug.qualified.length > 0 - @add itemSlug, quantity - - return this - - unparse: (options={})-> - parts = [] - @each (stack)=> - slugText = stack.itemSlug.item - if @modPack? - item = @modPack.findItem ItemSlug.slugify slugText - if item? and item.slug.qualified isnt stack.itemSlug.qualified - slugText = stack.itemSlug.qualified - - if stack.quantity is 1 - parts.push slugText - else - parts.push "#{stack.quantity}#{Inventory.Delimiters.Item}#{slugText}" - - return parts.join Inventory.Delimiters.Stack - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - isEmpty: - get: -> @_itemSlugs.length is 0 - - totalQuantity: - get: -> - total = 0 - @each (stack)-> - total += stack.quantity - return total + remove: (item, quantity)-> + @add item, -1 * quantity # Object Overrides ############################################################################# - toString: -> - result = [@constructor.name, " (", @cid, ") {items: ["] + toString: (options={})-> + options.full ?= false - needsDelimiter = false - @each (stack)-> - if needsDelimiter then result.push ', ' - result.push stack.toString() - needsDelimiter = true - result.push ']' + if options.full + result = [] + needsDelimiter = false - result.push '}' - return result.join '' + stackList = (stack for itemId, stack of @_stacks) + stackList.sort (a, b)-> + if a.item.displayName isnt b.item.displayName + return if a.item.displayName < b.item.displayName then -1 else +1 + return 0 - # Private Methods ############################################################################## + for stack in stackList + if needsDelimiter then result.push ", " + needsDelimiter = true - _add: (itemSlug, quantity=1, options={})-> - options.insert ?= false - return unless itemSlug? - return unless quantity > 0 - - stack = @_stacks[itemSlug] - if not stack? - stack = new Stack itemSlug:itemSlug, quantity:quantity - @listenTo stack, c.event.change, => @trigger c.event.change, this - @_stacks[itemSlug] = stack - if options.insert - @_itemSlugs.unshift itemSlug - else - @_itemSlugs.push itemSlug - @_sort() + result.push stack.quantity + result.push " " + result.push stack.item.displayName + return result.join "" else - stack.quantity += quantity - - _sort: -> - @_itemSlugs.sort (a, b)-> ItemSlug.compare a, b + return "Inventory<#{@_id}>@#{(id for id, item of @_stacks).length}" diff --git a/src/client/models/game/inventory.test.coffee b/src/client/models/game/inventory.test.coffee deleted file mode 100644 index 78b704a0f..000000000 --- a/src/client/models/game/inventory.test.coffee +++ /dev/null @@ -1,213 +0,0 @@ -# -# Crafting Guide - inventory.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -EventRecorder = require '../event_recorder' -Inventory = require './inventory' -Item = require './item' -ItemSlug = require './item_slug' - -######################################################################################################################## - -inventory = modPack = null - -######################################################################################################################## - -describe 'inventory.coffee', -> - - beforeEach -> - inventory = new Inventory {}, silent:false - inventory.add ItemSlug.slugify('wool'), 4 - inventory.add ItemSlug.slugify('string'), 20 - inventory.add ItemSlug.slugify('boat') - - describe 'add', -> - - it 'can add to an empty inventory', -> - inventory.add ItemSlug.slugify('iron_ingot'), 4 - stack = inventory._stacks['iron_ingot'] - stack.constructor.name.should.equal 'Stack' - stack.itemSlug.qualified.should.equal 'iron_ingot' - stack.quantity.should.equal 4 - - it 'can augment quantity of existing items', -> - inventory.add ItemSlug.slugify('wool'), 2 - inventory.unparse().should.equal 'boat:20.string:6.wool' - - it 'can add zero quantity', -> - inventory.add ItemSlug.slugify('wool'), 0 - inventory.unparse().should.equal 'boat:20.string:4.wool' - - it 'emits the proper events', -> - events = new EventRecorder inventory - inventory.add ItemSlug.slugify('iron_ingot'), 10 - events.names.should.eql [c.event.add, c.event.change] - - describe 'addInventory', -> - - it 'can add to an empty inventory', -> - newInventory = new Inventory - newInventory.addInventory inventory - newInventory.unparse().should.equal 'boat:20.string:4.wool' - - it 'can add a mix of new and existing items', -> - newInventory = new Inventory - newInventory.add ItemSlug.slugify('string'), 2 - newInventory.addInventory inventory - newInventory.unparse().should.equal 'boat:22.string:4.wool' - - describe 'clone', -> - - it 'creates an empty inventory from an empty inventory', -> - a = new Inventory - b = a.clone() - b._itemSlugs.should.eql [] - - it 'faithfully copies an existing inventory', -> - copy = inventory.clone() - copy.unparse().should.equal 'boat:20.string:4.wool' - - describe 'each', -> - - it 'works with an empty inventory', -> - inventory = new Inventory - result = [] - inventory.each (item)-> result.push item.name - result.should.eql [] - - it 'works when items have only been added', -> - result = [] - inventory.each (stack)-> result.push stack.itemSlug.qualified - result.should.eql ['boat', 'string', 'wool'] - - it 'works when items have been augmented', -> - inventory.add ItemSlug.slugify 'iron_ingot' - inventory.add ItemSlug.slugify 'boat' - inventory.add ItemSlug.slugify('wool'), 2 - - result = [] - inventory.each (stack)-> result.push stack.itemSlug.qualified - result.should.eql ['boat', 'iron_ingot', 'string', 'wool'] - - describe 'hasAtLeast', -> - - it 'works when the item is completely absent', -> - answer = inventory.hasAtLeast 'chicken', 1 - answer.should.be.false - - it 'always returns true for zero quantity', -> - inventory.hasAtLeast('chicken', 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 - inventory.hasAtLeast('wool', 4).should.be.true - inventory.hasAtLeast('wool', 5).should.be.false - - describe 'localize', -> - - before -> - modPack = - modSlug: - wool: 'minecraft' - string: 'minecraft' - boat: 'minecraft' - stone_gear: 'buildcraft' - findItem: (slug)-> - return slug:new ItemSlug @modSlug[slug.item], slug.item - - it 'replaces item slugs with qualified slugs', -> - inventory.add ItemSlug.slugify 'stone_gear' - inventory.modPack = modPack - inventory.localize() - - slugs = [] - inventory.each (stack)-> slugs.push stack.itemSlug.qualified - slugs.should.eql [ - 'minecraft__boat' - 'buildcraft__stone_gear' - 'minecraft__string' - 'minecraft__wool' - ] - - it 'ignores qualified slugs', -> - inventory.add ItemSlug.slugify 'buildcraft__stone_gear' - inventory.modPack = modPack - inventory.localize() - - slugs = [] - inventory.each (stack)-> slugs.push stack.itemSlug.qualified - slugs.should.eql [ - 'minecraft__boat' - 'buildcraft__stone_gear' - 'minecraft__string' - 'minecraft__wool' - ] - - describe 'parse', -> - - beforeEach -> - inventory = new Inventory {}, silent:false - - it 'ignores an empty string', -> - result = inventory.parse '' - result.unparse().should.eql '' - - it 'can parse a single item without quantity', -> - result = inventory.parse 'wool' - result.unparse().should.equal 'wool' - - it 'can parse a single item with quantity', -> - result = inventory.parse '4.wool' - result.unparse().should.equal '4.wool' - - it 'can parse multiple mixed-type items', -> - result = inventory.parse '4.wool:10.string:boat' - result.unparse().should.equal 'boat:10.string:4.wool' - - describe 'pop', -> - - it 'returns null for an empty inventory', -> - inventory = new Inventory - result = inventory.pop() - expect(result).to.be.null - - it 'completely removes the last item', -> - stack = inventory.pop() - stack.itemSlug.qualified.should.equal 'wool' - stack.quantity.should.equal 4 - inventory.unparse().should.equal 'boat:20.string' - - it 'triggers the right events', -> - events = new EventRecorder inventory - result = inventory.pop() - events.names.should.eql [c.event.remove, c.event.change] - - describe 'remove', -> - - it 'does nothing when the item is absent', -> - before = inventory.unparse() - inventory.remove 'foo' - after = inventory.unparse() - - before.should.equal after - - it 'throws when the item has insufficient quantity', -> - expect(-> inventory.remove('wool', 10)).to.throw Error, - 'cannot remove 10: only 4 wool in this inventory' - - it 'removes all items by default', -> - inventory.remove 'wool' - expect(inventory._stacks.wool).to.be.empty - - it 'removes a quantity above 1', -> - inventory.remove 'wool', 3 - inventory._stacks.wool.quantity.should.equal 1 - - it 'emits the proper events', -> - events = new EventRecorder inventory - inventory.remove 'wool' - events.names.should.eql [c.event.change, c.event.remove, c.event.change] diff --git a/src/client/models/game/item.coffee b/src/client/models/game/item.coffee index 9ab2545aa..2e0a4796e 100644 --- a/src/client/models/game/item.coffee +++ b/src/client/models/game/item.coffee @@ -5,86 +5,92 @@ # All rights reserved. # -BaseModel = require '../base_model' -ItemSlug = require './item_slug' -Recipe = require './recipe' -{StringBuilder} = require 'crafting-guide-common' - ######################################################################################################################## -module.exports = class Item extends BaseModel +module.exports = class Item - @Group = Other:'Other' + constructor: (attributes={})-> + @id = attributes.id + @displayName = attributes.displayName + @isGatherable = attributes.isGatherable + @mod = attributes.mod - constructor: (attributes={}, options={})-> - if not attributes.name? then throw new Error 'attributes.name is required' - - attributes.description ?= null - attributes.group ?= Item.Group.Other - attributes.ignoreDuringCrafting ?= false - attributes.isGatherable ?= false - attributes.modVersion ?= null - attributes.officialUrl ?= null - attributes.slug ?= ItemSlug.slugify attributes.name - attributes.videos ?= [] - - options.logEvents ?= false - super attributes, options - - @on c.event.change + ':modVersion', => - @_isCraftable = null - @slug.mod = @modVersion?.modSlug - - # Public Methods ############################################################################### - - compareTo: (that)-> - if this.slug isnt that.slug - return if this.slug < that.slug then -1 else +1 - if this.name isnt that.name - return if this.name < that.name then -1 else +1 - return 0 - - unparse: -> - ItemParser = require '../parsing/item_parser' # to avoid require cycles - @_parser ?= new ItemParser model:this - return @_parser.unparse() + @_hasPrimaryRecipe = false + @_recipesAsPrimary = {} + @_recipesAsExtra = {} # Property Methods ############################################################################# - getIsCraftable: -> - if not @_isCraftable? - @_isCraftable = false - if @modVersion? - @_isCraftable = @modVersion.hasRecipes @slug - - return @_isCraftable - Object.defineProperties @prototype, - isCraftable: {get:@prototype.getIsCraftable} - # Backbone.Model Overrides ##################################################################### + displayName: # a string containing the user-facing name of this item + get: -> return @_displayName + set: (displayName)-> + if not displayName? then throw new Error "displayName is required" + @_displayName = displayName - parse: (text)-> - ItemParser = require '../parsing/item_parser' # to avoid require cycles - @_parser ?= new ItemParser model:this - @_parser.parse text + firstRecipe: # the first Recipe returned by iterating the `recipes` property + get: -> + recipeList = (recipe for id, recipe of @recipes) + return null unless recipeList.length > 0 + return recipeList[0] + set: -> throw new Error "firstRecipe cannot be assigned" - return null # prevent calling `set` + id: # a string containing a unique identifier for this item + get: -> return @_id + set: (id)-> + if not id? then throw new Error "id is required" + return if @_id is id + if @_id? then throw new Error "id cannot be reassigned" + @_id = id - url: -> - return c.url.itemData modSlug:@slug.mod, itemSlug:@slug.item + isGatherable: # whether this item can be gathered directly without needing to be crafted + get: -> + return true if @_isGatherable is true + return false if (id for id, recipe of @_recipesAsPrimary).length > 0 + return false if (id for id, recipe of @_recipesAsExtra).length > 0 + return true + set: (isGatherable)-> + @_isGatherable = null unless isGatherable? + @_isGatherable = !!isGatherable + + mod: # the Mod which adds this item to the game + get: -> return @_mod + set: (mod)-> + if not mod? then throw new Error "mod is required" + if @_mod is mod then return + if @_mod? then throw new Error "mod cannot be reassigned" + @_mod = mod + @_mod.addItem this + + modPack: # the ModPack containing this item + get: -> return @_mod.modPack + set: -> throw new Error "modPack cannot be assigned" + + recipes: # a hash of recipeId to Recipe containing `recipesAsPrimary` if not empty or else `recipesAsExtra` + get: -> return if @_hasPrimaryRecipe then @_recipesAsPrimary else @_recipesAsExtra + set: -> throw new Error "recipes cannot be assigned" + + recipesAsPrimary: # a hash of recipeId to Recipe where this item is the primary output + get: -> return @_recipesAsPrimary + set: -> throw new Error "recipes cannot be assigned" + + recipesAsExtra: # a hash of recipeId to Recipe where this item is an extra output + get: -> return @_recipesAsExtra + set: -> throw new Error "recipesAsExtra cannot be assigned" + + # Public Recipes ############################################################################### + + addRecipe: (recipe)-> + if recipe.output.item is this + @_recipesAsPrimary[recipe.id] = recipe + @_hasPrimaryRecipe = true + else if recipe.extras[this.id]?.item is this + @_recipesAsExtra[recipe.id] = recipe + else + throw new Error "recipe<#{recipe.id}> does not produce this item<#{@id}>" # Object Overrides ############################################################################# toString: -> - builder = new StringBuilder - return builder - .push @constructor.name, ' (', @cid, ') { ' - .push 'name:"', @name, '", ' - .push 'isCraftable:', @isCraftable, ', ' - .push 'isGatherable:', @isGatherable, ', ' - .onlyIf (@group isnt Item.Group.Other), (b)=> - b.push 'group:"', @group, '", ' - .push 'slug:"', @slug, '", ' - .push '}' - .toString() + return "Item:#{@displayName}<#{@id}>" diff --git a/src/client/models/game/item_slug.test.coffee b/src/client/models/game/item_slug.test.coffee deleted file mode 100644 index 106b8ce5a..000000000 --- a/src/client/models/game/item_slug.test.coffee +++ /dev/null @@ -1,122 +0,0 @@ -# -# Crafting Guide - item_slug.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -ItemSlug = require './item_slug' - -######################################################################################################################## - -describe 'item_slug.coffee', -> - - describe 'constructor', -> - - it 'can handle one argument', -> - slug = new ItemSlug 'alpha' - slug.item.should.equal 'alpha' - expect(slug.mod).to.be.null - slug.qualified.should.equal 'alpha' - - it 'can handle two arguments', -> - slug = new ItemSlug 'alpha', 'bravo' - slug.mod.should.equal 'alpha' - slug.item.should.equal 'bravo' - slug.qualified.should.equal 'alpha__bravo' - - it 'throws with zero arguments', -> - f = -> new ItemSlug - expect(f).to.throw Error, 'expected arguments to be "modSlug, itemSlug" or just "itemSlug"' - - it 'throws with more arguments', -> - f = -> new ItemSlug 'alpha', 'bravo', 'charlie' - expect(f).to.throw Error, 'expected arguments to be "modSlug, itemSlug" or just "itemSlug"' - - describe 'ItemSlug.compare', -> - - it 'sorts by item when both are qualified in the same mod', -> - a = new ItemSlug 'alpha', 'bravo' - b = new ItemSlug 'charlie', 'bravo' - - ItemSlug.compare(a, b).should.equal -1 - ItemSlug.compare(b, a).should.equal +1 - - it 'sorts by item when not qualified', -> - a = new ItemSlug 'alpha' - b = new ItemSlug 'bravo' - - ItemSlug.compare(a, b).should.equal -1 - ItemSlug.compare(b, a).should.equal +1 - - describe 'ItemSlug.equal', -> - - it 'requires both to have the same mod', -> - a = new ItemSlug 'alpha', 'bravo' - b = new ItemSlug 'alpha', 'charlie' - c = new ItemSlug 'alpha', 'bravo' - - ItemSlug.equal(a, b).should.be.false - ItemSlug.equal(a, c).should.be.true - - it 'requires both to have the same item', -> - a = new ItemSlug 'alpha', 'bravo' - b = new ItemSlug 'alpha', 'charlie' - c = new ItemSlug 'alpha', 'bravo' - - ItemSlug.equal(a, b).should.be.false - ItemSlug.equal(a, c).should.be.true - - describe 'ItemSlug.slugify', -> - - it 'can slugify a pure name', -> - slug = ItemSlug.slugify 'Alpha Bravo (Charlie)' - slug.item.should.equal 'alpha_bravo_charlie' - expect(slug.mod).to.be.null - - it 'can slugify a simple item slug', -> - slug = ItemSlug.slugify 'alpha_bravo_charlie' - slug.item.should.equal 'alpha_bravo_charlie' - expect(slug.mod).to.be.null - - it 'can slugify a fully-qualified slug', -> - slug = ItemSlug.slugify 'alpha_bravo__charlie_delta' - slug.mod.should.equal 'alpha_bravo' - slug.item.should.equal 'charlie_delta' - - describe 'matches', -> - - it 'ignores mod when either is unqualified', -> - a = new ItemSlug 'alpha', 'bravo' - b = new ItemSlug 'bravo' - c = new ItemSlug 'charlie' - - a.matches(b).should.be.true - a.matches(c).should.be.false - - it 'observes differences in mod when all are qualified', -> - a = new ItemSlug 'alpha', 'bravo' - b = new ItemSlug 'charlie', 'bravo' - c = new ItemSlug 'delta', 'echo' - d = new ItemSlug 'alpha', 'bravo' - - a.matches(b).should.be.false - a.matches(c).should.be.false - a.matches(d).should.be.true - - describe 'isQualified', -> - - it 'returns true only when the mod slug is set', -> - a = new ItemSlug 'alpha', 'bravo' - b = new ItemSlug 'charlie' - a.isQualified.should.be.true - b.isQualified.should.be.false - - describe '[]', -> - - it 'allows slugs as a key', -> - slug = new ItemSlug 'alpha', 'bravo' - data = {} - data[slug] = 'foo' - data['alpha__bravo'].should.equal 'foo' - data[slug].should.equal 'foo' diff --git a/src/client/models/game/mod.coffee b/src/client/models/game/mod.coffee index e5e058564..b4e013b86 100644 --- a/src/client/models/game/mod.coffee +++ b/src/client/models/game/mod.coffee @@ -5,230 +5,58 @@ # All rights reserved. # -BaseModel = require '../base_model' - ######################################################################################################################## -module.exports = class Mod extends BaseModel +module.exports = class Mod - constructor: (attributes={}, options={})-> - if not attributes.slug? then throw new Error 'attributes.slug is required' + constructor: (attributes={})-> + @id = attributes.id + @displayName = attributes.displayName + @modPack = attributes.modPack - attributes.author ?= '' - attributes.description ?= '' - attributes.documentationUrl ?= null - attributes.downloadUrl ?= null - attributes.homePageUrl ?= null - attributes.modPack ?= null - attributes.name ?= '' + @_items = {} - super attributes, options - - @_activeModVersion = null - @_activeVersion = null - @_modVersions = [] - @_tutorials = [] - - # Class Methods ################################################################################## - - @Version: Version = - None: 'none' - Latest: 'latest' - - # Public Methods ################################################################################# - - compareTo: (that)-> - thisRequired = this.slug in c.requiredMods - thatRequired = that.slug in c.requiredMods - - if thisRequired isnt thatRequired - return -1 if thisRequired - return +1 if thatRequired - else if this.slug isnt that.slug - return if this.slug < that.slug then -1 else +1 - - return 0 - - # Property Methods ############################################################################# + # Properties ################################################################################### Object.defineProperties @prototype, - activeModVersion: - get: -> @_activeModVersion + displayName: + get: -> return @_displayName + set: (displayName)-> + if not displayName? then throw new Error "displayName is required" + return if @_displayName is displayName + @_displayName = displayName - activeVersion: - get: -> - return @_activeVersion + id: + get: -> return @_id + set: (id)-> + if not id? then throw new Error "id is required" + return if @_id is id + if @_id? then throw new Error "id cannot be reassigned" + @_id = id - set: (version)-> - return if version is @_activeVersion + items: + get: -> return @_items + set: -> throw new Error "items cannot be replaced" - version ?= Mod.Version.None - if version is Mod.Version.Latest then version = _.last(@_modVersions).version + modPack: + get: -> return @_modPack + set: (modPack)-> + if not modPack? then throw new Error "modPack is required" + if @_modPack is modPack then return + if @_modPack? then throw new Error "modPack cannot be reassigned" + @_modPack = modPack + @_modPack.addMod this - if version is Mod.Version.None - @_activeVersion = version - @_activateModVersion null + # Public Methods ############################################################################### - @trigger c.event.change + ':activeVersion', this, @_activeVersion - @trigger c.event.change, this - else - for modVersion in @_modVersions - if version is modVersion.version - @_activateModVersion modVersion - break + addItem: (item)-> + if not item? then return + if @_items[item.id] is item then return + @_items[item.id] = item + item.mod = this - @_activeVersion = version - @trigger c.event.change + ':activeVersion', this, @_activeVersion - @trigger c.event.change, this + # Object Overrides ############################################################################# - enabled: - get: -> @_activeModVersion? - - modVersions: - get: -> @_modVersions[..] - - tutorials: - get: -> @getAllTutorials() - - # Item Methods ################################################################################# - - chooseRandomItem: -> - effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest - return null unless effectiveModVersion? - - return effectiveModVersion.chooseRandomItem() - - eachItem: (callback)-> - effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest - effectiveModVersion.eachItem callback - - findItem: (slug, options={})-> - options.includeDisabled ?= false - options.enableAsNeeded ?= false - - if not options.includeDisabled - return unless @_activeModVersion? - return @_activeModVersion.findItem slug - else - for modVersion in @_modVersions - modVersion.fetch() - - item = modVersion.findItem slug - if item? - if options.enableAsNeeded then @setActiveVersion modVersion.version - return item - - return null - - findItemByName: (name)-> - return unless @_activeModVersion? - @_activeModVersion.findItemByName name - - # ModVersion Methods ########################################################################### - - addModVersion: (modVersion)-> - return unless modVersion? - return if @_modVersions.indexOf(modVersion) isnt -1 - - @_modVersions.push modVersion - @listenTo modVersion, c.event.change, => @trigger c.event.change, this - modVersion.fileCache = this.fileCache - modVersion.mod = this - - @trigger c.event.add + ':modVersion', modVersion, this - @trigger c.event.change + ':version', modVersion, this - @trigger c.event.change, this - - if not @activeVersion? then @activeVersion = modVersion.version - if modVersion.version is @_activeVersion then @_activateModVersion modVersion - return this - - eachModVersion: (callback)-> - for modVersion in @_modVersions - callback modVersion - - getAllModVersions: -> - return @_modVersions[..] - - getModVersion: (version)-> - return null if version is Mod.Version.None - return @_modVersions[0] if version is Mod.Version.Latest - - for modVersion in @_modVersions - return modVersion if modVersion.version is version - - return null - - # Name Methods ################################################################################# - - eachName: (callback)-> - effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest - effectiveModVersion.eachName callback - - findName: (itemSlug)-> - return unless @_activeModVersion? - @_activeModVersion.findName itemSlug - - # Recipe Methods ############################################################################### - - eachRecipe: (callback)-> - effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest - effectiveModVersion.eachRecipe callback - - findRecipes: (itemSlug, result=[], options={})-> - options.alwaysFromOwningMod ?= false - - if @_activeModVersion? - return @_activeModVersion.findRecipes itemSlug, result, options - else if options.alwaysFromOwningMod and itemSlug.mod is @slug - return @getModVersion(Mod.Version.Latest).findRecipes itemSlug, result, options - - return null - - # Tutorial Methods ############################################################################# - - addTutorial: (tutorial)-> - return unless tutorial? - if @getTutorial(tutorial.slug)? then throw new Error "duplicate tutorial: #{tutorial.name}" - @_tutorials.push tutorial - tutorial.modSlug = @slug - - getAllTutorials: -> - return @_tutorials[..] - - getTutorial: (tutorialSlug)-> - for tutorial in @_tutorials - return tutorial if tutorial.slug is tutorialSlug - return null - - # Backbone.Model Overrides ##################################################################### - - parse: (text)-> - ModParser = require '../parsing/mod_parser' # to avoid require cycles - @_parser ?= new ModParser model:this - @_parser.parse text - - @_verifyActiveModVersion() - - return null # prevent calling `set` - - url: -> - return c.url.modData modSlug:@slug - - # Private Methods ############################################################################## - - _activateModVersion: (modVersion)-> - if @_activeModVersion? then @stopListening @_activeModVersion - @_activeModVersion = modVersion - @trigger c.event.change + ':activeModVersion', this, @_activeModVersion - - logger.verbose => "#{@slug} switched to version #{@_activeVersion}" - - if @_activeModVersion? - @listenTo @_activeModVersion, 'all', -> @trigger.apply this, arguments - - _verifyActiveModVersion: -> - if (@_activeVersion isnt Version.None) and (not @_activeModVersion?) - logger.warning => "#{@slug} no longer has a version #{@_activeVersion}, using latest instead" - @activeVersion = Version.Latest + toString: -> + return "Mod:#{@displayName}<#{@id}>" diff --git a/src/client/models/game/mod.test.coffee b/src/client/models/game/mod.test.coffee deleted file mode 100644 index bbfd66776..000000000 --- a/src/client/models/game/mod.test.coffee +++ /dev/null @@ -1,30 +0,0 @@ -# -# Crafting Guide - mod.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Mod = require './mod' - -######################################################################################################################## - -mod = null - -######################################################################################################################## - -describe 'mod.coffee', -> - - beforeEach -> mod = new Mod name:'Test', slug:'test' - - describe 'compareTo', -> - - it 'lists required mods first', -> - minecraft = new Mod name:'Minecraft', slug:'minecraft' - mod.compareTo(minecraft).should.equal +1 - minecraft.compareTo(mod).should.equal -1 - - it 'sorts by name second', -> - buildcraft = new Mod name:'Buildcraft', slug:'buildcraft' - mod.compareTo(buildcraft).should.equal +1 - buildcraft.compareTo(mod).should.equal -1 diff --git a/src/client/models/game/mod_pack.coffee b/src/client/models/game/mod_pack.coffee index 50dd10493..f7e3842a9 100644 --- a/src/client/models/game/mod_pack.coffee +++ b/src/client/models/game/mod_pack.coffee @@ -5,198 +5,55 @@ # All rights reserved. # -BaseModel = require '../base_model' -Mod = require './mod' -ModVersionParser = require '../parsing/mod_version_parser' -Recipe = require './recipe' -SimpleInventory = require '../crafting/simple_inventory' - ######################################################################################################################## -module.exports = class ModPack extends BaseModel +module.exports = class ModPack - constructor: (attributes={}, options={})-> - super attributes, options + constructor: (attributes={})-> + @id = attributes.id + @displayName = attributes.displayName - @_mods = [] - @_cache = {} + @_mods = {} - @on c.event.change, => @_cache = {} + # Property Methods ############################################################################# - # Item Methods ################################################################################# + Object.defineProperties @prototype, - chooseRandomItem: -> - return null unless @_mods.length > 0 + displayName: # a string containing the user-displayable name of this ModPack + get: -> return @_displayName + set: (displayName)-> + if not displayName? then throw new Error "displayName is required" + if @_displayName is displayName then return + @_displayName = displayName - modIndex = Math.floor Math.random() * @_mods.length - return @_mods[modIndex].chooseRandomItem() + id: # a string which uniquely identifies this ModPack + get: -> return @_id + set: (id)-> + if not id? then throw new Error "id is required" + if @_id is id then return + if @_id? then throw new Error "id cannot be reassigned" + @_id = id - findItem: (itemSlug, options={})-> - options.includeDisabled ?= false + mods: # a hash of mod id to Mod containing all the mods which are part of this ModPack + get: -> return @_mods + set: -> throw new Error "mods cannot be replaced" - key = "#{itemSlug}-#{options.includeDisabled}" - @_cache.itemBySlug ?= {} - item = @_cache.itemBySlug[key] - return item if item? + # Public Methods ############################################################################### - if itemSlug.isQualified - mod = @getMod itemSlug.mod - if mod? - item = mod.findItem itemSlug, options + addMod: (mod)-> + if not mod? then return + if @_mods[mod.id] is mod then return + @_mods[mod.id] = mod + mod.modPack = this - if not item? - for mod in @_mods - continue unless mod.enabled or options.includeDisabled - item = mod.findItem itemSlug, options - break if item? - - if item? - @_cache.itemBySlug[key] = item - - return item - - findItemByName: (name, options={})-> - options.enableAsNeeded ?= false - options.includeDisabled = true if options.enableAsNeeded - - for mod in @_mods - continue unless mod.enabled or options.includeDisabled - item = mod.findItemByName name, options + findItem: (itemId)-> + for modId, mod of @mods + item = mod.items[itemId] return item if item? return null - findItemDisplay: (itemSlug)-> - if not itemSlug? then throw new Error 'itemSlug is required' - - result = {slug:itemSlug} - item = @findItem itemSlug, includeDisabled:true - if item? - result.itemName = item.name - result.itemSlug = item.slug.item - result.modSlug = item.slug.mod - result.modVersion = item.modVersion.version - else - result.itemName = @findName itemSlug, includeDisabled:true - result.itemSlug = itemSlug.item - result.modSlug = @_mods[0].slug - result.modVersion = @_mods[0].activeVersion - - craftingUrlInventory = new SimpleInventory modPack:this - if item?.multiblock? - craftingUrlInventory.addInventory item.multiblock.inventory - else - craftingUrlInventory.add itemSlug - - result.craftingUrl = c.url.crafting inventoryText:craftingUrlInventory.unparse() - result.iconUrl = c.url.itemIcon result - result.itemUrl = c.url.item result - result.modName = @getMod(result.modSlug).name - return result - - qualifySlug: (itemSlug)-> - return itemSlug if itemSlug.isQualified - - item = @findItem itemSlug - return item.slug if item? - return itemSlug - - # Mod Methods ################################################################################## - - addMod: (mod)-> - if not mod? then throw new Error 'mod is required' - return if @_mods.indexOf(mod) isnt -1 - - mod.modPack = this - @_mods.push mod - @listenTo mod, c.event.change, (modVersion)=> @_onModVersionLoaded modVersion - @trigger c.event.add + ':mod', mod, this - - @_mods.sort (a, b)-> a.compareTo b - @trigger c.event.sort + ':mod', this - @trigger c.event.change, this - - return this - - eachMod: (callback)-> - for mod in @_mods - callback mod - - getMod: (slug)-> - for mod in @_mods - return mod if mod.slug is slug - return null - - getAllMods: -> - return @_mods[..] - - removeMod: (mod)-> - index = @_mods.indexOf mod - return unless index >= 0 - - @_mods.splice index, 1 - - @trigger c.event.remove, this, mod.slug - @trigger c.event.change, this - - # Name Methods ################################################################################# - - findName: (slug, options={})-> - options.includeDisabled ?= false - - for mod in @_mods - continue unless mod.enabled or options.includeDisabled - name = mod.findName slug - return name if name - - return null - - # Recipe Methods ############################################################################### - - findRecipes: (itemSlug, options={})-> - options.alwaysFromOwningMod ?= false - return null unless itemSlug? - - key = "#{itemSlug}-#{options.alwaysFromOwningMod}" - @_cache.recipesBySlug ?= {} - result = @_cache.recipesBySlug[key] - return result if result? - - result = [] - for mod in @_mods - if not mod.enabled - owningMod = itemSlug.isQualified and (itemSlug.mod is mod.slug) - continue unless owningMod and options.alwaysFromOwningMod - - mod.findRecipes itemSlug, result, options - - @_cache.recipesBySlug[key] = result - return if result.length > 0 then result else null - # Object Overrides ############################################################################# toString: -> - return "ModPack (#{@cid}) {modVersions:«#{@_mods.length} items»}" - - # Private Methods ############################################################################## - - _onModVersionLoaded: (modVersion)-> - mods = @getAllMods() - return true unless mods.length > 0 - - for mod in mods - if mod.isError - @removeMod mod - continue - - modVersions = mod.getAllModVersions() - return true unless modVersions.length > 0 - continue if mod.activeVersion is Mod.Version.None - - activeModVersion = mod.activeModVersion - return true unless activeModVersion? - return true if activeModVersion.isUnloaded - return true if activeModVersion.isLoading - - @trigger c.event.change, this - @trigger c.event.sync, this + return "ModPack:#{@displayName}<#{@id}>" diff --git a/src/client/models/game/mod_pack.test.coffee b/src/client/models/game/mod_pack.test.coffee deleted file mode 100644 index ade40cfc5..000000000 --- a/src/client/models/game/mod_pack.test.coffee +++ /dev/null @@ -1,103 +0,0 @@ -# -# Crafting Guide - mod_pack.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Item = require './item' -ItemSlug = require './item_slug' -Mod = require './mod' -ModPack = require './mod_pack' -ModVersion = require './mod_version' - -######################################################################################################################## - -buildcraft = industrialCraft = minecraft = modPack = null - -######################################################################################################################## - -describe 'mod_pack.coffee', -> - - beforeEach -> - minecraft = new Mod slug:'minecraft', name:'Minecraft' - minecraft.addModVersion new ModVersion modSlug:minecraft.slug, version:'1.7.10' - minecraft.activeModVersion.addItem new Item name:'Wool' - minecraft.activeModVersion.addItem new Item name:'Bed', recipes:[''] - minecraft.activeModVersion.registerName ItemSlug.slugify('iron_chestplate'), 'Iron Chestplate' - - buildcraft = new Mod slug:'buildcraft', name:'Buildcraft' - buildcraft.addModVersion new ModVersion modSlug:buildcraft.slug, version:'6.2.6' - buildcraft.activeModVersion.addItem new Item name:'Stone Gear', recipes:[''] - buildcraft.activeModVersion.addItem new Item name:'Wrench', recipes:[''] - buildcraft.activeVersion = Mod.Version.None - - industrialCraft = new Mod slug:'industrial_craft', name:'Industrial Craft' - industrialCraft.addModVersion new ModVersion modSlug:industrialCraft.slug, version:'2.0' - industrialCraft.activeModVersion.addItem new Item name:'Resin' - industrialCraft.activeModVersion.addItem new Item name:'Rubber' - industrialCraft.activeModVersion.addItem new Item name:'Wrench', recipes:[''] - industrialCraft.activeVersion = Mod.Version.None - - modPack = new ModPack - modPack.addMod minecraft - modPack.addMod buildcraft - modPack.addMod industrialCraft - - describe 'findItem', -> - - it 'can find an item by partial slug', -> - item = modPack.findItem ItemSlug.slugify 'wool' - item.slug.qualified.should.equal 'minecraft__wool' - - it 'can find an item by full slug', -> - item = modPack.findItem ItemSlug.slugify 'minecraft__wool' - item.name.should.equal 'Wool' - - it 'can find an ambiguous item by full slug', -> - buildcraft.activeVersion = Mod.Version.Latest - industrialCraft.activeVersion = Mod.Version.Latest - item = modPack.findItem ItemSlug.slugify 'industrial_craft__wrench' - item.name.should.equal 'Wrench' - item.modVersion.mod.name.should.equal 'Industrial Craft' - - it 'can find an ambiguous item by partial slug', -> - buildcraft.activeVersion = Mod.Version.Latest - industrialCraft.activeVersion = Mod.Version.Latest - item = modPack.findItem ItemSlug.slugify 'wrench' - item.name.should.equal 'Wrench' - item.modVersion.mod.name.should.equal 'Buildcraft' - - describe 'findItemByName', -> - - it 'finds the requested item', -> - item = modPack.findItemByName 'Bed' - item.name.should.equal 'Bed' - - it 'ignores disabled mod versions', -> - item = modPack.findItemByName 'Stone Gear' - expect(item).to.be.null - - describe 'findItemDisplay', -> - - it 'returns all data for a regular Minecraft item', -> - display = modPack.findItemDisplay ItemSlug.slugify 'bed' - display.iconUrl.should.equal '/data/minecraft/items/bed/icon.png' - display.itemUrl.should.equal '/browse/minecraft/bed/' - display.itemName.should.equal 'Bed' - display.modSlug.should.equal 'minecraft' - - it 'returns all data for an item in an enabled mod', -> - buildcraft.activeVersion = '6.2.6' - display = modPack.findItemDisplay ItemSlug.slugify 'stone_gear' - display.iconUrl.should.equal '/data/buildcraft/items/stone_gear/icon.png' - display.itemUrl.should.equal '/browse/buildcraft/stone_gear/' - display.itemName.should.equal 'Stone Gear' - display.modSlug.should.equal 'buildcraft' - - it 'assumes an unfound item is from Minecraft', -> - display = modPack.findItemDisplay ItemSlug.slugify 'iron_chestplate' - display.iconUrl.should.equal '/data/minecraft/items/iron_chestplate/icon.png' - display.itemUrl.should.equal '/browse/minecraft/iron_chestplate/' - display.itemName.should.equal 'Iron Chestplate' - display.modSlug.should.equal 'minecraft' diff --git a/src/client/models/game/mod_version.test.coffee b/src/client/models/game/mod_version.test.coffee deleted file mode 100644 index 93a1d7305..000000000 --- a/src/client/models/game/mod_version.test.coffee +++ /dev/null @@ -1,106 +0,0 @@ -# -# Crafting Guide - mod_version.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Item = require './item' -ItemSlug = require './item_slug' -ModVersion = require './mod_version' - -######################################################################################################################## - -modVersion = null - -######################################################################################################################## - -describe 'mod_version.coffee', -> - - beforeEach -> - modVersion = new ModVersion modSlug:'test', version:'0.0' - modVersion.addItem new Item name:'underscore', group:'punctuation' - modVersion.addItem new Item name:'bravo', group:'letter' - modVersion.addItem new Item name:'alpha', group:'letter' - modVersion.addItem new Item name:'one', group:'number' - modVersion.addItem new Item name:'two', group:'number' - - describe 'constructor', -> - - it 'requires a mod slug', -> - expect(-> new ModVersion version:'0.0').to.throw Error, 'attributes.modSlug is required' - - it 'requires a mod version', -> - expect(-> new ModVersion modSlug:'test').to.throw Error, 'attributes.version is required' - - describe 'addItem', -> - - it 'refuses to add duplicates', -> - modVersion.addItem new Item name:'Wool' - expect(-> modVersion.addItem new Item name:'Wool').to.throw Error, 'duplicate item for 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 'eachGroup', -> - - it 'returns all the groups in order', -> - groupNames = [] - modVersion.eachGroup (groupName)-> groupNames.push groupName - groupNames.should.eql ['letter', 'number', 'punctuation'] - - describe 'eachItemInGroup', -> - - it 'returns immediately for unknown group', -> - slugs = [] - modVersion.eachItemInGroup 'foobar', (item)-> slugs.push item.slug.qualified - slugs.should.eql [] - - it 'calls callback for exactly the items in a group in order', -> - slugs = [] - modVersion.eachItemInGroup 'letter', (item)-> slugs.push item.slug.qualified - slugs.should.eql ['test__alpha', 'test__bravo'] - - slugs = [] - modVersion.eachItemInGroup 'number', (item)-> slugs.push item.slug.qualified - slugs.should.eql ['test__one', 'test__two'] - - - describe 'findItemByName', -> - - it 'locates items by slugified name', -> - modVersion.addItem new Item name:'Crafting Table' - modVersion.findItemByName('Crafting Table').slug.qualified.should.equal 'test__crafting_table' - - describe 'findRecipes', -> - - beforeEach -> - modVersion = new ModVersion modSlug:'test', version:'1.0' - modVersion.parse """ - schema:1 - - item: Cake - recipe:; input: Milk, Sugar, Egg, Wheat; pattern: 000 121 333; extras: 3 Bucket - recipe:; input: Milk, Cocoa Beans, Egg, Wheat; pattern: 000 121 333; extras: 3 Bucket - recipe:; input: Cake Slice; pattern: 000 000 000; onlyIf: item Cake Slice - item: Bucket - recipe:; input: Iron Ingot; pattern: ... 0.0 .0. - recipe:; input: Copper Ingot; pattern: ... 0.0 .0.; extras: Copper Nugget - """ - - it 'finds all recipes which list item as output', -> - recipes = modVersion.findRecipes ItemSlug.slugify('test__bucket') - (r.output[0].itemSlug.item for r in recipes).sort().should.eql ['bucket', 'bucket'] - - it 'skip recipes whose conditions are not met', -> - recipes = modVersion.findRecipes ItemSlug.slugify('test__cake') - recipes.length.should.equal 2 - - it 'finds recipes for items which are only ever extras', -> - recipes = modVersion.findRecipes ItemSlug.slugify('test__copper_nugget') - recipes.length.should.equal 1 diff --git a/src/client/models/game/multiblock.test.coffee b/src/client/models/game/multiblock.test.coffee deleted file mode 100644 index 777db1baa..000000000 --- a/src/client/models/game/multiblock.test.coffee +++ /dev/null @@ -1,85 +0,0 @@ -# -# Crafting Guide - multiblock.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -ItemSlug = require './item_slug' -Mod = require './mod' -Multiblock = require './multiblock' -Stack = require './stack' - -######################################################################################################################## - -describe 'multiblock.coffee', -> - - describe 'with a single block type', -> - - beforeEach -> - @input = [ new Stack itemSlug:ItemSlug.slugify('cobblestone'), quantity:1 ] - - it 'correctly parses a 1x1x1 cube', -> - model = new Multiblock input:@input, layers:['0'] - model.depth.should.equal 1 - model.height.should.equal 1 - model.width.should.equal 1 - model.getStackAt(0, 0, 0).toString().should.equal '1 cobblestone' - - it 'correctly parses a solid 3x3x3 cube', -> - model = new Multiblock input:@input, layers:['000 000 000', '000 000 000', '000 000 000'] - model.depth.should.equal 3 - model.height.should.equal 3 - model.width.should.equal 3 - - for x in [0..2] - for y in [0..2] - for z in [0..2] - model.getStackAt(x, y, z).toString().should.equal '1 cobblestone' - - it 'correctly parses a hollow 3x3x3 cube', -> - model = new Multiblock input:@input, layers:['000 000 000', '000 0.0 000', '000 000 000'] - model.depth.should.equal 3 - model.height.should.equal 3 - model.width.should.equal 3 - - for x in [0..2] - for y in [0..2] - for z in [0..2] - if x isnt 1 or y isnt 1 or z isnt 1 - model.getStackAt(x, y, z).toString().should.equal '1 cobblestone' - else - expect(model.getStackAt(x, y, z)).to.be.null - - it 'correctly parses a 3x2x3 pyramid', -> - model = new Multiblock input:@input, layers:['000 000 000', '.. .0 ..'] - model.depth.should.equal 3 - model.height.should.equal 2 - model.width.should.equal 3 - - for y in [0..1] - for z in [0..2] - for x in [0..2] - if y is 1 and (x isnt 1 or z isnt 1) - expect(model.getStackAt(x, y, z)).to.be.null - else - model.getStackAt(x, y, z).toString().should.equal '1 cobblestone' - - describe 'with multiple block types', -> - - beforeEach -> - @input = [ - new Stack itemSlug:ItemSlug.slugify('cobblestone'), quantity:1 - new Stack itemSlug:ItemSlug.slugify('stone'), quantity:1 - new Stack itemSlug:ItemSlug.slugify('oak wood'), quantity:1 - ] - - it 'correctly parses a 1x3x1 column of different types', -> - model = new Multiblock input:@input, layers:['0', '1', '2'] - model.depth.should.equal 1 - model.height.should.equal 3 - model.width.should.equal 1 - - model.getStackAt(0, 0, 0).toString().should.equal '1 cobblestone' - model.getStackAt(0, 1, 0).toString().should.equal '1 stone' - model.getStackAt(0, 2, 0).toString().should.equal '1 oak_wood' diff --git a/src/client/models/game/recipe.coffee b/src/client/models/game/recipe.coffee index a3416a42f..8cb220542 100644 --- a/src/client/models/game/recipe.coffee +++ b/src/client/models/game/recipe.coffee @@ -5,243 +5,170 @@ # All rights reserved. # -BaseModel = require '../base_model' -ItemSlug = require './item_slug' -Stack = require './stack' {StringBuilder} = require 'crafting-guide-common' ######################################################################################################################## -module.exports = class Recipe extends BaseModel +module.exports = class Recipe - constructor: (attributes={}, options={})-> - if not attributes.input? then throw new Error 'attributes.input is required' - if not attributes.pattern? then throw new Error 'attributes.pattern is required' + constructor: (attributes={})-> + @depth = attributes.depth + @height = attributes.height + @id = attributes.id + @output = attributes.output + @width = attributes.width - if attributes.itemSlug? and not attributes.output? - attributes.output = [new Stack itemSlug:attributes.itemSlug, quantity:1] - else if attributes.output? and not attributes.itemSlug? - if attributes.output.length is 0 then throw new Error 'attributes.output cannot be empty' - attributes.itemSlug = attributes.output[0].itemSlug - else - throw new Error 'attributes.itemSlug or attributes.output is required' + @_extras = {} + @_inputs = {} + @_inputGrid = [] + @_tools = {} - attributes.pattern = @_parsePattern attributes.pattern - - attributes.condition ?= null - attributes.ignoreDuringCrafting ?= false - attributes.modVersion ?= null - attributes.tools ?= [] - options.logEvents ?= false - super attributes, options - - @_computeQuantities attributes.pattern - - @on c.event.change + ':modVersion', => @_slug = null - @on c.event.change + ':pattern', => @_patternCache = null - - # Class Methods ################################################################################ - - @compareFor: (a, b, itemSlug)-> - if itemSlug? - aValue = a.itemSlug.matches itemSlug - bValue = b.itemSlug.matches itemSlug - if aValue isnt bValue - return -1 if aValue - return +1 if bValue - - aValue = a.getQuantityProduced itemSlug - bValue = b.getQuantityProduced itemSlug - if aValue isnt bValue - return if aValue > bValue then -1 else +1 - - return 0 - - # Public Methods ############################################################################### - - getStackAtSlot: (patternSlot)-> - trueIndex = 0:0, 1:1, 2:2, 3:4, 4:5, 5:6, 6:8, 7:9, 8:10 - 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 - - getQuantityProduced: (itemSlug)-> - total = 0 - for stack in @output - if stack.itemSlug.matches itemSlug - total += stack.quantity - - return total - - getQuantityRequired: (itemSlug)-> - total = 0 - for stack, index in @input - if ItemSlug.equal stack.itemSlug, itemSlug - total += @_quantities[index] * stack.quantity - - return total - - hasAllTools: (modPack)-> - modPack ?= @modVersion?.mod?.modPack - return true unless modPack? - - for stack in @tools - return false unless modPack.findItem stack.itemSlug - return true - - isConditionSatisfied: (modPack)-> - return true unless @condition? - modPack ?= @modVersion?.mod?.modPack - - result = false - if @condition.verb is 'item' - if modPack?.findItemByName(@condition.noun)? - result = true - else if @condition.verb is 'mod' - modPack.eachMod (mod)=> - if mod.name is @condition.noun - result = true - - if @condition.inverted then result = not result - return result - - isPassThroughFor: (itemSlug)-> - return @getQuantityProduced(itemSlug) is @getQuantityRequired(itemSlug) - - produces: (itemSlug)-> - if not @_produces? - @_produces = {} - - for stack in @output - actuallyProduces = not @isPassThroughFor stack.itemSlug - @_produces[stack.itemSlug.qualified] = actuallyProduces - - result = @_produces[itemSlug.qualified] or @_produces[itemSlug.item] - return result - - requires: (itemSlug)-> - for stack in @input - if stack.itemSlug.matches itemSlug - return true - return false - - requiresTool: (itemSlug)-> - for stack in @tools - if stack.itemSlug.matches itemSlug - return true - - # Property Methods ############################################################################# + # Properties ################################################################################### Object.defineProperties @prototype, - slug: - get: -> - if not @_slug? - builder = new StringBuilder - delimiterNeeded = false - for stack in @input - if delimiterNeeded then builder.push ',' - delimiterNeeded = true + allProducts: # an array of Stacks starting the the primary output of this recipe + get: -> return [].concat @output, (stack for id, stack of @extras) + set: -> throw new Error "allProducts cannot be assigned" - if stack.quantity > 1 then builder.push stack.quantity, ' ' - builder.push stack.itemSlug.qualified + depth: # an integer specifying the number of layers to this recipe + get: -> return @_depth + set: (depth)-> + depth = parseInt "#{depth}" + depth = if Number.isNaN(depth) then 0 else Math.max(0, depth) + @_depth = depth - builder.push '>' - builder.push @pattern - builder.push '>' - for stack in @tools - builder.push stack.itemSlug.qualified - builder.push '>' + extras: # a hash of item id to Stack of all the non-primary outputs of this recipe + get: -> return @_extras + set: -> throw new Error "extras cannot be replaced" - delimiterNeeded = false - for stack in @output - if delimiterNeeded then builder.push ',' - delimiterNeeded = true + id: # a string which uniquely identifies this recipe + get: -> return @_id + set: (id)-> + if not id? then throw new Error "id is required" + if @_id is id then return + if @_id? then throw new Error "id cannot be reassigned" + @_id = id - if stack.quantity > 1 then builder.push stack.quantity, ' ' - builder.push stack.itemSlug.qualified + height: # an integer specifying the number of rows needed by this recipe + get: -> return @_height + set: (height)-> + height = parseInt "#{height}" + height = if Number.isNaN(height) then 0 else Math.max(0, height) + @_height = height - @_slug = builder.toString() + inputs: # a hash of item id to Item containing all the inputs to this recipe + get: -> return @_inputs + set: -> throw new Error "inputs cannot be replaced" - return @_slug + needsTools: # a boolean indicating whether this recipe requires a tool + get: -> return (id for id, toolItem of @_tools).length > 0 + + output: # a Stack specifying the primary output of this recipe + get: -> return @_output + set: (output)-> + if not output? then throw new Error "output is required" + if @_output is output then return + if @_output? then throw new Error "output cannot be reassigned" + @_output = output + @_output.item.addRecipe this + + modPack: # the ModPack to which this recipe belongs + get: -> return @_output.modPack + set: -> throw new Error "modPack cannot be replaced" + + tools: # a hash of item id to Item of all the tools required for this recipe + get: -> return @_tools + set: -> throw new Error "tools cannot be assigned" + + width: # an integer specifying the number of columns needed by this recipe + get: -> return @_width + set: (width)-> + width = parseInt "#{width}" + width = if Number.isNaN(width) then 0 else Math.max(0, width) + @_width = width + + # Public Methods ############################################################################### + + addExtra: (stack)-> + return unless stack + return if @_extras[stack.item.id] is stack + @_extras[stack.item.id] = stack + stack.item.addRecipe this + + addTool: (item)-> + return unless item + @_tools[item.id] = item + + computeQuantityRequired: (item)-> + result = 0 + + for x in [0...@width] + for y in [0...@height] + for z in [0...@depth] + stack = @getInputAt x, y, z + continue unless stack? + continue unless stack.item.id is item.id + result += stack.quantity + + return result + + computeQuantityProduced: (item)-> + result = 0 + + if @_output.item.id is item.id + result += @_output.quantity + + for itemId, stack of @_extras + continue unless itemId is item.id + result += stack.quantity + + return result + + getInputAt: -> + [x, y, z] = [0, 0, 0] + if arguments.length is 3 + [x, y, z] = arguments + else + [x, y] = arguments + + return @_inputGrid[x]?[y]?[z] or null + + setInputAt: -> + [x, y, z, stack] = [0, 0, 0, null] + if arguments.length is 4 + [x, y, z, stack] = arguments + else + [x, y, stack] = arguments + + @_depth = Math.max @_depth, z + 1 + @_height = Math.max @_height, y + 1 + @_width = Math.max @_width, x + 1 + + @_inputGrid[x] ?= [] + @_inputGrid[x][y] ?= [] + @_inputGrid[x][y][z] = stack + + if stack?.item? then @_inputs[stack.item.id] = stack.item # Object Overrides ############################################################################# - toString: -> - result = [@constructor.name, " (", @cid, ") { name:", @name] + toString: (options={})-> + options.full ?= false - result.push ", input:[" - needsDelimiter = false - for stack in @input - if needsDelimiter then result.push ', ' - result.push @getQuantityRequired stack.itemSlug - result.push ' ' - result.push stack.itemSlug - needsDelimiter = true - result.push ']' + if options.full + b = new StringBuilder + b.loop (item for id, item of @inputs), delimiter:" + ", onEach:(b, item)=> + b.push @computeQuantityRequired(item), " ", item.displayName + b.push " =" + b.onlyIf @needsTools, (b)=> + b.push "(" + b.loop (toolItem for id, toolItem of @tools), onEach:(b, toolItem)-> b.push toolItem.displayName + b.push ")" + b.push "=> " + b.loop @allProducts, delimiter:" + ", onEach:(b, stack)=> + b.push stack.quantity, " ", stack.item.displayName - result.push ", output:[" - needsDelimiter = false - for stack in @output - if needsDelimiter then result.push ', ' - result.push @getQuantityProduced stack.itemSlug - result.push ' ' - result.push stack.itemSlug - needsDelimiter = true - result.push ']' - - if @tools.length > 0 - result.push ", tools:[" - needsDelimiter = false - for stack in @tools - if needsDelimiter then result.push ', ' - result.push stack.toString() - needsDelimiter = true - result.push ']' - - result.push '}' - return result.join '' - - # Private Methods ############################################################################## - - _computeQuantities: (pattern)-> - quantityMap = {} - - index = 0 - while index < pattern.length - c = pattern[index] - index += 1 - - continue if c is '.' - continue if c is ' ' - - if quantityMap[c]? - quantityMap[c] += 1 - else - quantityMap[c] = 1 - - @_quantities = [] - for i in [0...@input.length] - @_quantities.push quantityMap["#{i}"] - - _parsePattern: (pattern)-> - return unless pattern? - - pattern = pattern.replace /\ /g, '' - return if pattern.length is 0 - - pattern = pattern.replace /[^0-9]/g, '.' - - array = pattern.split '' - array = array[0...9] - while array.length isnt 9 - array.push '.' - - pattern = array.join '' - pattern = pattern.replace /(...)(...)(...)/, '$1 $2 $3' - return pattern + return b.toString() + else + return "Recipe:#{@output}<#{@id}>" diff --git a/src/client/models/game/recipe.test.coffee b/src/client/models/game/recipe.test.coffee index 38daf33de..ef9bffda0 100644 --- a/src/client/models/game/recipe.test.coffee +++ b/src/client/models/game/recipe.test.coffee @@ -5,86 +5,68 @@ # All rights reserved. # -Item = require './item' -ItemSlug = require './item_slug' -Recipe = require './recipe' -Stack = require './stack' +fixtures = require "../fixtures" +Item = require "./item" +Recipe = require "./recipe" +Stack = require "./stack" ######################################################################################################################## -input = output = pattern = recipe = null +describe "Recipe", -> -######################################################################################################################## + beforeEach -> + @mod = fixtures.createMod() + @stick = fixtures.configureStick @mod + @ironIngot = fixtures.configureIronIngot @mod + @obsidian = fixtures.configureObsidian @mod -describe 'recipe.coffee', -> + @ironSword = new Item id:"iron_sword", displayName:"Iron Sword", mod:@mod + @obsidianBox = new Item id:"obsidian_box", displayName:"Obsidian Box", mod:@mod - describe 'constructor', -> + describe "getting & setting inputs", -> - beforeEach -> - input = [ - new Stack(itemSlug:new ItemSlug('iron_gear')), - new Stack(itemSlug:new ItemSlug('gold_ingot'), quantity:4) - ] - pattern = '.1. 101 .1.' + describe "for 2D recipes", -> - it 'requires input', -> - expect(-> new Recipe slug:'gold_gear', pattern:pattern).to.throw Error, 'attributes.input is required' + beforeEach -> + @recipe = new Recipe id:"test1", output:new Stack item:@ironSword + @recipe.setInputAt 1, 0, new Stack item:@ironIngot + @recipe.setInputAt 1, 1, new Stack item:@ironIngot + @recipe.setInputAt 1, 2, new Stack item:@stick - it 'requires a pattern', -> - expect(-> new Recipe slug:'gold_gear', input:input).to.throw Error, 'attributes.pattern is required' + it "returns assigned values as expected", -> + expect(@recipe.getInputAt(0, 0)).to.equal null + @recipe.getInputAt(1, 0).item.displayName.should.equal "Iron Ingot" + @recipe.getInputAt(1, 1).item.displayName.should.equal "Iron Ingot" + @recipe.getInputAt(1, 2).item.displayName.should.equal "Stick" + expect(@recipe.getInputAt(2, 2)).to.equal null - it 'requires either outputs or a slug', -> - f = -> new Recipe input:input, pattern:pattern - expect(f).to.throw 'attributes.itemSlug or attributes.output is required' + it "determines the correct dimentions", -> + @recipe.depth.should.equal 1 + @recipe.height.should.equal 3 + @recipe.width.should.equal 2 - it 'creates default output', -> - recipe = new Recipe itemSlug:ItemSlug.slugify('gold_gear'), input:input, pattern:pattern - recipe.output.length.should.equal 1 - recipe.output[0].itemSlug.qualified.should.equal 'gold_gear' - recipe.output[0].quantity.should.equal 1 + describe "for 3D recipes", -> - it 'assigns a default slug', -> - recipe = new Recipe input:input, pattern:pattern, output:[new Stack itemSlug:ItemSlug.slugify('gold_gear')] - recipe.itemSlug.qualified.should.equal 'gold_gear' + beforeEach -> + @recipe = new Recipe id:"test2", output:new Stack item:@obsidianBox + for x in [0..2] + for y in [0..2] + for z in [0..2] + continue if x is 1 and y is 1 + continue if x is 1 and z is 1 + continue if y is 1 and z is 1 - describe 'getStackAtSlot', -> + @recipe.setInputAt x, y, z, new Stack item:@obsidian - beforeEach -> - input = [ - new Stack itemSlug:ItemSlug.slugify('iron_gear') - new Stack itemSlug:ItemSlug.slugify('gold_ingot'), quantity:4 - ] - recipe = new Recipe itemSlug:'gold_gear', input:input, pattern:'.1. 101 .1.' + it "returns assigned values as expected", -> + @recipe.getInputAt(0, 0, 0).item.displayName.should.equal "Obsidian" + @recipe.getInputAt(2, 0, 0).item.displayName.should.equal "Obsidian" + @recipe.getInputAt(0, 2, 0).item.displayName.should.equal "Obsidian" + @recipe.getInputAt(2, 2, 2).item.displayName.should.equal "Obsidian" + expect(@recipe.getInputAt(1, 1, 1)).to.equal null + expect(@recipe.getInputAt(1, 1, 0)).to.equal null - it 'returns the proper item for an early slot', -> - stack = recipe.getStackAtSlot(1) - stack.itemSlug.qualified.should.equal 'gold_ingot' - stack.quantity.should.equal 4 - - it 'returns the proper item for a late slot', -> - stack = recipe.getStackAtSlot(4) - stack.itemSlug.qualified.should.equal 'iron_gear' - stack.quantity.should.equal 1 - - it 'returns null for an invalid slot', -> - expect(recipe.getStackAtSlot(12)).to.be.null - - describe '_parsePattern', -> - - beforeEach -> - recipe = new Recipe - itemSlug: 'oak_wood_planks', - input: [new Stack itemSlug:new ItemSlug('oak_wood')], - pattern:'... .0. ...' - - it 'normalizes invalid characters', -> - recipe._parsePattern('$$0 #() 010').should.equal '..0 ... 010' - - it 'removes extra characters', -> - recipe._parsePattern('000 000 000 000').should.equal '000 000 000' - - it 'fills in missing characters', -> - recipe._parsePattern('000000').should.equal '000 000 ...' - - it 'fills in spaces', -> - recipe._parsePattern('000000000').should.equal '000 000 000' + it "determines the correct dimentions", -> + @recipe.depth.should.equal 3 + @recipe.height.should.equal 3 + @recipe.width.should.equal 3 \ No newline at end of file diff --git a/src/client/models/game/stack.coffee b/src/client/models/game/stack.coffee index 6797330dd..aa8541cfc 100644 --- a/src/client/models/game/stack.coffee +++ b/src/client/models/game/stack.coffee @@ -5,19 +5,38 @@ # All rights reserved. # -BaseModel = require '../base_model' - ######################################################################################################################## -module.exports = class Stack extends BaseModel +module.exports = class Stack - constructor: (attributes={}, options={})-> - if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required' - attributes.quantity ?= 1 - options.logEvents ?= false - super attributes, options + constructor: (attributes={})-> + @item = attributes.item + @quantity = attributes.quantity + + # Properties ################################################################################### + + Object.defineProperties @prototype, + + item: + get: -> return @_item + set: (item)-> + if not item? then throw new Error "item is required" + if @_item is item then return + if @_item? then throw new Error "item cannot be reassigned" + @_item = item + + modPack: + get: -> return @_item.modPack + set: -> throw new Error "modPack cannot be replaced" + + quantity: + get: -> return @_quantity + set: (quantity)-> + quantity = parseInt "#{quantity}" + quantity = if Number.isNaN(quantity) then 0 else Math.max(0, quantity) + @_quantity = quantity # Object Overrides ############################################################################# toString: -> - return "#{@quantity} #{@itemSlug}" + return "Stack:#{@item}×#{@quantity}" \ No newline at end of file diff --git a/src/client/models/parsing/mod_pack_json.test.coffee b/src/client/models/parsing/mod_pack_json.test.coffee new file mode 100644 index 000000000..6053ed7ad --- /dev/null +++ b/src/client/models/parsing/mod_pack_json.test.coffee @@ -0,0 +1,124 @@ +# +# Crafting Guide - mod_pack_json.test.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +fixtures = require "../fixtures" +ModPackJsonFormatter = require "./mod_pack_json_formatter" +ModPackJsonParser = require "./mod_pack_json_parser" + +######################################################################################################################## + +describe "ModPackJsonParser & ModPackJsonFormatter", -> + + beforeEach -> + @formatter = new ModPackJsonFormatter + @parser = new ModPackJsonParser + + @modPack = fixtures.createModPack id:"alpha", displayName:"ALPHA" + + @runTest = => + @string1 = @formatter.format @modPack + @string2 = @formatter.format @parser.parse @string1 + @result = @parser.parse @string2 + + describe "an empty modpack", -> + + beforeEach -> @runTest() + + it "can survive a round trip", -> + @string1.should.equal @string2 + + it "contains the modpack's own properties", -> + @result.id.should.equal @modPack.id + @result.displayName.should.equal @modPack.displayName + + it "doesn't contain a mods list", -> + expect(@result.mods).to.beUndefined + + describe "a modpack with a single mod", -> + + beforeEach -> + @mod = fixtures.createMod modPack:@modPack, id:"bravo", displayName:"BRAVO" + + describe "containing only a gatherable item", -> + + beforeEach -> + @oakWood = fixtures.configureOakWood @mod + @runTest() + + it "can survive a round trip", -> + @string1.should.equal @string2 + + it "contains the correct mod", -> + mod = @result.mods[@mod.id] + mod.displayName.should.equal @mod.displayName + + it "contains the item", -> + item = @result.mods[@mod.id].items[@oakWood.id] + item.displayName.should.equal @oakWood.displayName + + describe "containing a multi-step item & it's requirements", -> + + beforeEach -> + @craftingTable = fixtures.configureCraftingTable @mod + @oakPlank = fixtures.configureOakPlank @mod + @runTest() + + it "can survive a round trip", -> + @string1.should.equal @string2 + + it "contains oak planks", -> + item = @result.mods[@mod.id].items[@oakPlank.id] + item.displayName.should.equal @oakPlank.displayName + + it "has the recipe for a crafting table", -> + recipe = @result.mods[@mod.id].items[@craftingTable.id].firstRecipe + recipe.getInputAt(0, 0).item.id.should.equal @oakPlank.id + recipe.getInputAt(0, 1).item.id.should.equal @oakPlank.id + recipe.getInputAt(1, 0).item.id.should.equal @oakPlank.id + recipe.getInputAt(1, 1).item.id.should.equal @oakPlank.id + recipe.output.quantity.should.equal 1 + + describe "containing a complex item which needs tools & it's requirements", -> + + beforeEach -> + @cake = fixtures.configureCake @mod + @runTest() + + it "can survive a round trip", -> + @string1.should.equal @string2 + + it "has the recipe for a cake", -> + recipe = @result.mods[@mod.id].items[@cake.id].firstRecipe + + describe "a modpack with multiple mods", -> + + beforeEach -> + @modA = fixtures.createMod modPack:@modPack, id:"bravo", displayName:"BRAVO" + @modB = fixtures.createMod modPack:@modPack, id:"charlie", displayName:"CHARLIE" + + describe "where items are used in recipes crossing mods", -> + + beforeEach -> + @stick = fixtures.configureStick @modA + @ironIngot = fixtures.configureIronIngot @modA + @ironSword = fixtures.configureIronSword @modB + @runTest() + + it "can survive a round trip", -> + @string1.should.equal @string2 + + it "has each item in the correct mod", -> + @result.mods[@modA.id].items[@ironIngot.id].displayName.should.equal @ironIngot.displayName + @result.mods[@modB.id].items[@ironSword.id].displayName.should.equal @ironSword.displayName + + it "has the correct recipe for an iron sword", -> + recipe = @result.mods[@modB.id].items[@ironSword.id].firstRecipe + recipe.output.item.id.should.equal @ironSword.id + recipe.output.quantity.should.equal 1 + recipe.getInputAt(1, 0).item.id.should.equal @ironIngot.id + recipe.getInputAt(1, 1).item.id.should.equal @ironIngot.id + recipe.getInputAt(1, 2).item.id.should.equal @stick.id diff --git a/src/client/models/parsing/mod_pack_json_formatter.coffee b/src/client/models/parsing/mod_pack_json_formatter.coffee new file mode 100644 index 000000000..f0032a1de --- /dev/null +++ b/src/client/models/parsing/mod_pack_json_formatter.coffee @@ -0,0 +1,101 @@ +# +# Crafting Guide - mod_pack_json_formatter.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +######################################################################################################################## + +module.exports = class ModPackJsonParser + + constructor: -> + @_reset() + + # Public Methods ############################################################################### + + format: (modPack)-> + @_reset() + return JSON.stringify @_formatModPack modPack + + # Private Methods ############################################################################## + + _formatItem: (item)-> + result = {} + result.id = item.id + result.displayName = item.displayName + + if item.isGatherable and item.firstRecipe? + result.gatherable = true + + return result + + _formatMod: (mod)-> + result = {} + result.id = mod.id + result.displayName = mod.displayName + + for itemId, item of mod.items + result.items ?= [] + @_itemIndexById[item.id] = @_itemIndex++ + result.items.push @_formatItem item + + return result + + _formatModPack: (modPack)-> + result = {} + result.id = modPack.id + result.displayName = modPack.displayName + + for modId, mod of modPack.mods + result.mods ?= [] + result.mods.push @_formatMod mod + + if result.mods? + for modResult in result.mods + for itemResult in modResult.items + item = modPack.mods[modResult.id].items[itemResult.id] + for recipeId, recipe of item.recipesAsPrimary + itemResult.recipes ?= [] + itemResult.recipes.push @_formatRecipe recipe + + return result + + _formatRecipe: (recipe)-> + result = {} + result.id = recipe.id + + if recipe.output.quantity > 1 + result.quantity = recipe.output.quantity + + result.width = recipe.width + result.height = recipe.height + result.depth = recipe.depth if recipe.depth > 1 + result.inputs = [] + + for x in [0...recipe.width] + for y in [0...recipe.height] + for z in [0...recipe.depth] + inputStack = @_formatStack recipe.getInputAt x, y, z + result.inputs.push inputStack + + for itemId, stack of recipe.extras + result.extras ?= [] + result.extras.push @_formatStack stack + + for itemId, item of recipe.tools + result.tools ?= [] + result.tools.push @_itemIndexById[itemId] + + return result + + _formatStack: (stack)-> + return null unless stack? + + itemIndex = @_itemIndexById[stack.item.id] + if stack.quantity is 1 then return itemIndex + return [itemIndex, stack.quantity] + + _reset: -> + @_itemIndex = 0 + @_itemIndexById = {} diff --git a/src/client/models/parsing/mod_pack_json_parser.coffee b/src/client/models/parsing/mod_pack_json_parser.coffee new file mode 100644 index 000000000..e0aa25f74 --- /dev/null +++ b/src/client/models/parsing/mod_pack_json_parser.coffee @@ -0,0 +1,164 @@ +# +# Crafting Guide - mod_pack_json_parser.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +Item = require "../game/item" +Mod = require "../game/mod" +ModPack = require "../game/mod_pack" +Recipe = require "../game/recipe" +Stack = require "../game/stack" + +######################################################################################################################## + +module.exports = class ModPackJsonParser + + constructor: -> + @_reset() + + # Public Methods ############################################################################### + + parse: (arg, fileName=null)-> + @_reset() + @_fileName = fileName + + if _.isString arg + @_parseText arg + else + @_parseObject arg + + return @_modPack + + # Private Methods ############################################################################## + + _parseInteger: (text, defaultValue)-> + result = parseInt "#{text}" + result = if Number.isNaN result then defaultValue else result + return result + + _parseText: (text)-> + try + obj = JSON.parse text + catch error + @_throwError "could not parse JSON: #{error}" + + @_parseObject obj + + _parseObject: (obj)-> + @_data = obj + @_parseModPack() + @_parseMods() + @_parseItems() + @_parseRecipes() + + _parseModPack: -> + if not @_data? then @_throwError "there is no valid data" + if not @_data.id? then @_throwError "modPack requires an id" + if not @_data.displayName? then @_throwError "modPack requires a displayName" + + @_modPack = new ModPack id:@_data.id, displayName:@_data.displayName + + _parseMods: -> + return unless @_data.mods? + + for modData, index in @_data.mods + @_location = "mods[#{index}]" + if not modData.id? then @_throwError "mod requires an id" + if not modData.displayName? then @_throwError "mod requires a displayName" + + new Mod modPack:@_modPack, id:modData.id, displayName:modData.displayName + + _parseItems: -> + return unless @_data.mods? + + for modData in @_data.mods + continue unless modData.items? + + mod = @_modPack.mods[modData.id] + for itemData, index in modData.items + @_location = "<#{mod.id}>.items[#{index}]" + if not itemData.id? then @_throwError "item requires an id" + if not itemData.displayName? then @_throwError "item requires a displayName" + + item = new Item mod:mod, id:itemData.id, displayName:itemData.displayName + item.gatherable = itemData.gatherable if itemData.gatherable? + @_items.push item + + _parseRecipes: -> + return unless @_data.mods? + + for modData in @_data.mods + continue unless modData.items? + + mod = @_modPack.mods[modData.id] + for itemData, index in modData.items + continue unless itemData.recipes? + + item = mod.items[itemData.id] + for recipeData, index in itemData.recipes + @_location = "<#{itemData.id}>.recipes[#{index}]" + if not recipeData.id? then @_throwError "recipe requires id" + if not recipeData.inputs? then @_throwError "recipe requires inputs" + + quantity = @_parseInteger recipeData.quantity, 1 + outputStack = new Stack item:item, quantity:quantity + recipe = new Recipe id:recipeData.id, output:outputStack + + depth = @_parseInteger recipeData.depth, 1 + height = @_parseInteger recipeData.height, 3 + width = @_parseInteger recipeData.width, 3 + + index = 0 + for x in [0...width] + for y in [0...height] + for z in [0...depth] + stack = @_parseStack recipeData.inputs[index] + if stack? then recipe.setInputAt x, y, z, stack + index++ + + if recipeData.extras + for stackData in recipeData.extras + recipe.addExtra @_parseStack stackData + + if recipeData.tools + for index in recipeData.tools + toolItem = @_items[index] + if not toolItem? then @_throwError "there is no item #{index}" + recipe.addTool toolItem + + _parseStack: (stackData)-> + return null unless stackData? + if _.isArray(stackData) + if stackData.length isnt 2 then @_throwError "input stacks must have an item index and a quantity" + index = stackData[0] + quantity = stackData[1] + else + index = stackData + quantity = 1 + + item = @_items[index] + if not item? then @_throwError "there is no item #{index}" + + return new Stack item:item, quantity:quantity + + _reset: -> + @_data = null + @_fileName = null + @_items = [] + @_location = null + @_modPack = null + + _throwError: (message, cause=null)-> + if @_location? then message = "#{@_location}: #{message}" + if @_fileName? and @_location? then message = "@#{message}" + if @_fileName? then message = "#{@_fileName}#{message}" + if cause? then message = "#{message}: #{cause}" + + error = new Error message + error.cause = cause if cause? + error.fileName = @_fileName if @_fileName? + error.location = @_location if @_location? + + throw error diff --git a/src/client/models/stores/mod_pack_store.coffee b/src/client/models/stores/mod_pack_store.coffee new file mode 100644 index 000000000..4ddcfe86e --- /dev/null +++ b/src/client/models/stores/mod_pack_store.coffee @@ -0,0 +1,55 @@ +# +# Crafting Guide - mod_pack_store.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +ModPackJsonParser = require "../parsing/mod_pack_json_parser" + +######################################################################################################################## + +module.exports = class ModPackStore + + constructor: -> + @_data = {} + @_loading = {} + @_parser = new ModPackJsonParser + + # Class Methods ################################################################################ + + Object.defineProperties ModPackStore, + instance: + get: -> + @_instance ?= new ModPackStore + return @_instance + set: -> + throw new Error "cannot assign instance" + + # Public Methods ############################################################################### + + get: (modPackId)-> + return @_data[modPackId] + + load: (modPackId)-> + if not @_loading[modPackId]? + @_loading[modPackId] = w.promise (resolve, reject)-> + url = c.url.modPackArchive modPackId:modPackId + + onError = (xhr, status, message)=> + logger.error "failed to load mod pack #{modPackId}: #{status} — #{message}" + reject new Error message + + onSuccess = (data, status, xhr)=> + logger.info "laoded mod pack: #{modPackId}" + try + @_parser.reset() + modPack = @_parser.parse data, url + @_data[modPack.id] = modPack + resolve modPack + catch error + reject error + + $.ajax dataType: "text", error: onError, success: onSuccess, url: url + + return @_loading[modPackId] diff --git a/src/client/site/site_controller.coffee b/src/client/site/site_controller.coffee index 0c95e6353..5c51d3a7a 100644 --- a/src/client/site/site_controller.coffee +++ b/src/client/site/site_controller.coffee @@ -15,6 +15,7 @@ HeaderController = require './header/header_controller' ImageLoader = require './image_loader' Mod = require '../models/game/mod' ModPack = require '../models/game/mod_pack' +ModPackStore = require '../models/store/mod_pack_store' Router = require './router' ######################################################################################################################## @@ -30,7 +31,6 @@ module.exports = class SiteController extends BaseController @client = options.client @fileCache = new FileCache c.url.modpackArchive() @imageLoader = new ImageLoader defaultUrl:'/images/unknown.png' - @modPack = new ModPack {}, fileCache:@fileCache @router = new Router this @storage = options.storage @@ -41,23 +41,6 @@ module.exports = class SiteController extends BaseController # Public Methods ############################################################################### - loadDefaultModPack: -> - makeResponder = (m)-> return -> - m.activeModVersion.fetch() if m.activeModVersion? - - for modSlug, modData of c.defaultMods - mod = new Mod {slug:modSlug}, {fileCache:@fileCache} - mod.on c.event.change + ':activeModVersion', makeResponder mod - @storage.register "mod:#{mod.slug}", mod, 'activeVersion', modData.defaultVersion - mod.fetch() - - @modPack.addMod mod - - if global.env isnt 'prerender' - @modPack.once c.event.sync, => - @$pageContent.removeClass 'hidden' - @$pageContentLoading.addClass 'hidden' - loadCurrentUser: -> @client.getCurrentUser() .then (response)=> diff --git a/src/common/constants.coffee b/src/common/constants.coffee index e14156acf..78d35d37a 100644 --- a/src/common/constants.coffee +++ b/src/common/constants.coffee @@ -29,46 +29,6 @@ adsense.skyscraper.margin = 24 # px adsense.skyscraper.slotIds = ['7613920409', '9574673605', '3388539204'] adsense.skyscraper.width = 160 # px -exports.defaultMods = defaultMods = {} -defaultMods.minecraft = { defaultVersion: '1.7.10' } # Minecraft must be first - -defaultMods.advanced_solar_panels = { defaultVersion: '3.5.1' } -defaultMods.agricraft = { defaultVersion: '1.4.6' } -defaultMods.applied_energistics_2 = { defaultVersion: 'rv1-stable-1' } -defaultMods.big_reactors = { defaultVersion: '0.4.2A2' } -defaultMods.buildcraft = { defaultVersion: '1.7.18' } -defaultMods.computercraft = { defaultVersion: '1.74' } -defaultMods.draconic_evolution = { defaultVersion: '1.0.2h' } -defaultMods.ender_storage = { defaultVersion: '1.4.5.29' } -defaultMods.enderio = { defaultVersion: '2.2.7.325' } -defaultMods.extra_cells = { defaultVersion: '2.2.73b129' } -defaultMods.extra_utilities = { defaultVersion: '1.2.2' } -defaultMods.forestry = { defaultVersion: '3.4.0.7' } -defaultMods.forge_multipart = { defaultVersion: '1.2.0.345' } -defaultMods.galacticraft = { defaultVersion: '3.0.12.404' } -defaultMods.hydraulicraft = { defaultVersion: '2.1.242' } -defaultMods.ic2_classic = { defaultVersion: 'none' } -defaultMods.industrial_craft_2 = { defaultVersion: '2.2.663' } -defaultMods.iron_chests = { defaultVersion: '6.0.62.742' } -defaultMods.jabba = { defaultVersion: '1.2.1a' } -defaultMods.logistics_pipes = { defaultVersion: '0.9.3.100' } -defaultMods.mekanism = { defaultVersion: '7.1.1.127' } -defaultMods.minefactory_reloaded = { defaultVersion: '2.8.0RC8-86' } -defaultMods.modular_powersuits = { defaultVersion: '0.11.0-300-thermal-expansion' } -defaultMods.opencomputers = { defaultVersion: '1.5.22' } -defaultMods.quantum_flux = { defaultVersion: '1.3.4' } -defaultMods.project_red = { defaultVersion: '4.5.16.77' } -defaultMods.redstone_arsenal = { defaultVersion: '9.5.0' } -defaultMods.railcraft = { defaultVersion: '9.5.0' } -defaultMods.simply_jetpacks = { defaultVersion: '1.4.1' } -defaultMods.solar_expansion = { defaultVersion: '1.6a' } -defaultMods.solar_flux = { defaultVersion: '0.5b' } -defaultMods.storage_drawers = { defaultVersion: '1.7.10-1.6.2' } -defaultMods.thermal_dynamics = { defaultVersion: '1.7.10r1.2.0' } -defaultMods.thermal_expansion = { defaultVersion: '1.7.10r4.1.4' } -defaultMods.thermal_foundation = { defaultVersion: '1.7.10r1.2.5' } -defaultMods.tinkers_construct = { defaultVersion: '1.7.10-1.8.8' } - exports.duration = duration = {} duration.snap = 100 duration.fast = 200 @@ -119,6 +79,9 @@ login.clientIds = 'staging': '3d75ed772ce5004180d6' 'production': 'ce71be7f66926ff6ff38' +exports.modpack = modpack = {} +modpack.default = "crafting-guide-default" + exports.modelState = modelState = {} modelState.unloaded = 'unloaded' modelState.loading = 'loading' @@ -186,7 +149,7 @@ url.login = _.template "/login" url.mod = _.template "/browse/<%= modSlug %>/" url.modData = _.template "/data/<%= modSlug %>/mod.cg" url.modIcon = _.template "/data/<%= modSlug %>/icon.png" -url.modpackArchive = _.template "/data/modpack.cg" +url.modPackData = _.template "/data/<%= modPackId %>/modpack.json" url.modVersionData = _.template "/data/<%= modSlug %>/versions/<%= modVersion %>/mod-version.cg" url.root = _.template "/" url.tutorial = _.template "/browse/<%= modSlug %>/tutorials/<%= tutorialSlug %>/" From bd96bc65751f845701f735ab0c2de0ac59f47e6e Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Tue, 2 May 2017 18:34:53 -0700 Subject: [PATCH 3/5] Move model files to crafting-guide-common --- Gruntfile.coffee | 35 -- package.json | 3 +- src/client/client.coffee | 4 +- src/client/models-old/base_model.coffee | 129 ------ src/client/models-old/converter.coffee | 45 --- src/client/models-old/event_recorder.coffee | 36 -- src/client/models-old/game/inventory.coffee | 243 ----------- src/client/models-old/game/item.coffee | 90 ----- src/client/models-old/game/item_slug.coffee | 96 ----- src/client/models-old/game/mod.coffee | 234 ----------- src/client/models-old/game/mod_pack.coffee | 202 ---------- src/client/models-old/game/mod_version.coffee | 200 --------- src/client/models-old/game/multiblock.coffee | 85 ---- src/client/models-old/game/recipe.coffee | 247 ------------ .../models-old/game/simple_stack.coffee | 33 -- src/client/models-old/game/stack.coffee | 23 -- .../command_parser_version_base.coffee | 138 ------- .../command_parser_version_base.test.coffee | 48 --- .../models-old/parsing/item_parser.coffee | 19 - .../models-old/parsing/item_parser_v1.coffee | 72 ---- .../parsing/item_parser_v1.test.coffee | 99 ----- .../models-old/parsing/mod_parser.coffee | 19 - .../models-old/parsing/mod_parser_v1.coffee | 94 ----- .../parsing/mod_version_parser.coffee | 19 - .../parsing/mod_version_parser_v1.coffee | 380 ------------------ .../parsing/mod_version_parser_v1.test.coffee | 344 ---------------- .../models-old/parsing/tutorial_parser.coffee | 19 - .../parsing/tutorial_parser_v1.coffee | 65 --- .../parsing/versioned_parser_base.coffee | 51 --- src/client/models-old/site/tutorial.coffee | 33 -- src/client/models/base_model.coffee | 129 ------ .../models/crafting/crafting_plan.coffee | 144 ------- .../models/crafting/crafting_plan.test.coffee | 99 ----- .../models/crafting/crafting_plan_step.coffee | 39 -- src/client/models/crafting/evaluation.coffee | 105 ----- src/client/models/crafting/evaluator.coffee | 102 ----- .../models/crafting/plan_builder.coffee | 92 ----- .../models/crafting/plan_builder.test.coffee | 158 -------- .../crafting/resources_evaluator.coffee | 40 -- .../crafting/resources_evaluator.test.coffee | 129 ------ .../models/crafting/steps_evaluator.coffee | 41 -- src/client/models/event_recorder.coffee | 36 -- src/client/models/fixtures.coffee | 319 --------------- src/client/models/game/inventory.coffee | 90 ----- src/client/models/game/item.coffee | 96 ----- src/client/models/game/mod.coffee | 62 --- src/client/models/game/mod_pack.coffee | 59 --- src/client/models/game/recipe.coffee | 174 -------- src/client/models/game/recipe.test.coffee | 72 ---- src/client/models/game/stack.coffee | 42 -- .../models/parsing/mod_pack_json.test.coffee | 124 ------ .../parsing/mod_pack_json_formatter.coffee | 101 ----- .../parsing/mod_pack_json_parser.coffee | 164 -------- src/client/models/site/craft_page.coffee | 10 +- src/client/models/site/editable_file.coffee | 3 +- src/client/models/site/file_cache.coffee | 2 + src/client/models/site/file_cache.test.coffee | 6 + src/client/models/site/github_user.coffee | 2 +- src/client/models/site/item_page.coffee | 2 +- src/client/models/site/item_selector.coffee | 4 +- .../models/site/markdown_image_list.coffee | 2 +- src/client/models/site/tutorial.coffee | 33 -- .../models/stores/mod_pack_store.coffee | 1 + .../browse_page/browse_page_controller.coffee | 2 +- .../common/adsense/adsense_controller.coffee | 2 +- .../item_selector_controller.coffee | 1 + .../common/recipe/recipe_controller.coffee | 2 +- .../craft_page/craft_page_controller.coffee | 5 +- .../craftsman_working_controller.coffee | 2 +- .../craft_page/step/step_controller.coffee | 2 +- .../site/feedback/feedback_controller.coffee | 1 + .../item_page/item_page_controller.coffee | 5 +- .../recipe_detail_controller.coffee | 4 +- .../login_page/login_page_controller.coffee | 2 +- .../site/mod_page/mod_page_controller.coffee | 6 +- src/client/site/router.coffee | 18 +- src/client/site/site_controller.coffee | 23 +- src/client/tracker.coffee | 2 + src/common/constants.coffee | 91 ++--- src/common/underscore.coffee | 31 +- src/index.coffee | 11 +- src/server/crafting_guide_server.coffee | 1 + src/server/server.coffee | 6 - src/test_helper.coffee | 6 +- src/underscore.coffee | 10 + 85 files changed, 134 insertions(+), 5686 deletions(-) delete mode 100644 src/client/models-old/base_model.coffee delete mode 100644 src/client/models-old/converter.coffee delete mode 100644 src/client/models-old/event_recorder.coffee delete mode 100644 src/client/models-old/game/inventory.coffee delete mode 100644 src/client/models-old/game/item.coffee delete mode 100644 src/client/models-old/game/item_slug.coffee delete mode 100644 src/client/models-old/game/mod.coffee delete mode 100644 src/client/models-old/game/mod_pack.coffee delete mode 100644 src/client/models-old/game/mod_version.coffee delete mode 100644 src/client/models-old/game/multiblock.coffee delete mode 100644 src/client/models-old/game/recipe.coffee delete mode 100644 src/client/models-old/game/simple_stack.coffee delete mode 100644 src/client/models-old/game/stack.coffee delete mode 100644 src/client/models-old/parsing/command_parser_version_base.coffee delete mode 100644 src/client/models-old/parsing/command_parser_version_base.test.coffee delete mode 100644 src/client/models-old/parsing/item_parser.coffee delete mode 100644 src/client/models-old/parsing/item_parser_v1.coffee delete mode 100644 src/client/models-old/parsing/item_parser_v1.test.coffee delete mode 100644 src/client/models-old/parsing/mod_parser.coffee delete mode 100644 src/client/models-old/parsing/mod_parser_v1.coffee delete mode 100644 src/client/models-old/parsing/mod_version_parser.coffee delete mode 100644 src/client/models-old/parsing/mod_version_parser_v1.coffee delete mode 100644 src/client/models-old/parsing/mod_version_parser_v1.test.coffee delete mode 100644 src/client/models-old/parsing/tutorial_parser.coffee delete mode 100644 src/client/models-old/parsing/tutorial_parser_v1.coffee delete mode 100644 src/client/models-old/parsing/versioned_parser_base.coffee delete mode 100644 src/client/models-old/site/tutorial.coffee delete mode 100644 src/client/models/base_model.coffee delete mode 100644 src/client/models/crafting/crafting_plan.coffee delete mode 100644 src/client/models/crafting/crafting_plan.test.coffee delete mode 100644 src/client/models/crafting/crafting_plan_step.coffee delete mode 100644 src/client/models/crafting/evaluation.coffee delete mode 100644 src/client/models/crafting/evaluator.coffee delete mode 100644 src/client/models/crafting/plan_builder.coffee delete mode 100644 src/client/models/crafting/plan_builder.test.coffee delete mode 100644 src/client/models/crafting/resources_evaluator.coffee delete mode 100644 src/client/models/crafting/resources_evaluator.test.coffee delete mode 100644 src/client/models/crafting/steps_evaluator.coffee delete mode 100644 src/client/models/event_recorder.coffee delete mode 100644 src/client/models/fixtures.coffee delete mode 100644 src/client/models/game/inventory.coffee delete mode 100644 src/client/models/game/item.coffee delete mode 100644 src/client/models/game/mod.coffee delete mode 100644 src/client/models/game/mod_pack.coffee delete mode 100644 src/client/models/game/recipe.coffee delete mode 100644 src/client/models/game/recipe.test.coffee delete mode 100644 src/client/models/game/stack.coffee delete mode 100644 src/client/models/parsing/mod_pack_json.test.coffee delete mode 100644 src/client/models/parsing/mod_pack_json_formatter.coffee delete mode 100644 src/client/models/parsing/mod_pack_json_parser.coffee delete mode 100644 src/client/models/site/tutorial.coffee create mode 100644 src/underscore.coffee diff --git a/Gruntfile.coffee b/Gruntfile.coffee index ce937dacb..1f09793e0 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -17,29 +17,6 @@ EXTERNAL_LIBS = [ './vendor/email.js:emailjs' ] -PUBLISHED_FILES = [ - './client/models/base_model.coffee' - './client/models/game/inventory.coffee' - './client/models/game/item.coffee' - './client/models/game/item_slug.coffee' - './client/models/game/mod.coffee' - './client/models/game/mod_version.coffee' - './client/models/game/multiblock.coffee' - './client/models/game/recipe.coffee' - './client/models/game/stack.coffee' - './client/models/game/simple_stack.coffee' - './client/models/parsing/command_parser_version_base.coffee' - './client/models/parsing/mod_parser.coffee' - './client/models/parsing/mod_parser_v1.coffee' - './client/models/parsing/mod_version_parser.coffee' - './client/models/parsing/mod_version_parser_v1.coffee' - './client/models/parsing/versioned_parser_base.coffee' - './client/models/site/tutorial.coffee' - './common/constants.coffee' - './common/underscore.coffee' - './index.coffee' -] - ######################################################################################################################## module.exports = (grunt)-> @@ -61,15 +38,6 @@ module.exports = (grunt)-> {expand: true, cwd:'./build/static', src:'**/*.txt', dest:'./dist/'} ] - coffee: - npm_package: - options: { - bare: true - } - files: [ - {expand:true, cwd:'./src', src:PUBLISHED_FILES, dest:'./dist', ext:'.js'} - ] - copy: assets_build: files: [ @@ -216,9 +184,6 @@ module.exports = (grunt)-> grunt.registerTask 'dist', 'build the project to be run from Amazon S3', ['build', 'copy:build_to_dist', 'uglify', 'compress'] - grunt.registerTask 'prepublish', 'build the project to be published to NPM as shared code', - ['clean', 'coffee'] - grunt.registerTask 'publish', 'publish the project to NPM', ['prepublish', 'script:publish', 'clean', 'build'] diff --git a/package.json b/package.json index 4f7606cef..9617daca9 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "repository": "http://github.com/andrewminer/crafting-guide", "scripts": { "build": "grunt build", - "prepublish": "grunt prepublish", "start": "grunt start", "test": "grunt test" }, @@ -37,7 +36,7 @@ "body-parser": "^1.13.3", "client-sessions": "^0.7.0", "cookie-parser": "^1.3.5", - "crafting-guide-common": "^2.0.0", + "crafting-guide-common": "^3.2.0", "dotenv": "^2.0.0", "express": "^4.13.3", "express-session": "^1.11.3", diff --git a/src/client/client.coffee b/src/client/client.coffee index 87b1f114d..2b417c1a9 100644 --- a/src/client/client.coffee +++ b/src/client/client.coffee @@ -18,7 +18,7 @@ global.π = Math.PI global.ε = 0.0001 global.w = require 'when' -{Logger} = require 'crafting-guide-common' +{Logger} = require('crafting-guide-common').util global.logger = new Logger Tracker = require './tracker' @@ -63,7 +63,7 @@ storage = new Storage storage:global.localStorage tracker.trackPageView() -{CraftingGuideClient} = require 'crafting-guide-common' +{CraftingGuideClient} = require('crafting-guide-common').api client = _(new CraftingGuideClient(baseUrl:apiBaseUrl)).extend Backbone.Events client.onStatusChanged = (client, oldStatus, newStatus)-> logger.info "Crafting Guide server status changed from #{oldStatus} to #{newStatus}" diff --git a/src/client/models-old/base_model.coffee b/src/client/models-old/base_model.coffee deleted file mode 100644 index 8da42e281..000000000 --- a/src/client/models-old/base_model.coffee +++ /dev/null @@ -1,129 +0,0 @@ -# -# Crafting Guide - base_model.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class BaseModel extends Backbone.Model - - @_loadingQueue = [] - @_isDraining = false - - constructor: (attributes={}, options={})-> - options.logEvents ?= true - super attributes, options - - makeGetter = (name)-> return -> @get name - makeSetter = (name)-> return (value)-> @set name, value - for name, value of attributes - continue if name is 'id' - Object.defineProperty this, name, get:makeGetter(name), set:makeSetter(name) - - @fileCache = options.fileCache or null - @loading = null - @logEvents = options.logEvents or false - @state = c.modelState.unloaded - - Object.defineProperties this, - isUnloaded: { get:-> @state is c.modelState.unloaded } - isLoading: { get:-> @state is c.modelState.loading } - isLoaded: { get:-> @state is c.modelState.loaded } - isError: { get:-> @state is c.modelState.error } - - # Event Methods ################################################################################ - - onLoadSucceeded: (text, status, xhr)-> - try - @set @parse text - - @state = c.modelState.loaded - @trigger c.event.change, this - @trigger c.event.sync, this - logger.info => "#{@constructor.name}.#{@cid} loaded successfully" - catch e - logger.error -> "A parsing error occured: #{e.stack}" - @onLoadFailed e.message, 'parsing failed', xhr - - onLoadFailed: (error, status, xhr)-> - @state = c.modelState.error - logger.error => "#{@constructor.name}.#{@cid} failed to load: status:#{status}, message:#{error}" - @trigger c.event.error, this, error - - # Backbone.Model Overrides ##################################################################### - - fetch: (options={})-> - options.force ?= false - return if (@isLoading or @isLoaded) and not options.force - - url = @url() - logger.info => "#{@constructor.name}.#{@cid} reading from url: #{url}" - - @state = c.modelState.loading - @trigger c.event.request, this - - loadFromServer = => - w.promise (resolve, reject)=> - $.ajax - url: url - dataType: 'text' - success: (text, status, xhr)=> resolve @onLoadSucceeded text, status, xhr - error: (xhr, status, error)=> reject @onLoadFailed error, status, xhr - - if @fileCache? - @loading = @fileCache.loading.then => - if @fileCache.hasFile url - return @_addToLoadingQueue @fileCache.getFile(url), 'success', {url:url} - else - @loading = loadFromServer() - else - @loading = loadFromServer() - - @loading.catch (e)-> # do nothing - return @loading - - parse: (text)-> - return JSON.parse text - - sync: (method, model)-> - throw new Error "#{@constructor.name}.#{@cid} is not permitted to #{method}" - - trigger: (name, model, args...)-> - if @logEvents - argText = ("#{arg}"[0..50] for arg in args).join ", " - logger.trace => "#{@constructor.name}.#{@cid} triggered event #{name} with args: #{argText}" - super - - # Object Overrides ############################################################################# - - toString: -> - return "#{@constructor.name}.#{@cid}" - - # Private Methods ############################################################################## - - _addToLoadingQueue: (text, status, xhr)-> - deferred = w.defer() - - BaseModel._loadingQueue.push resolve:deferred.resolve, func:(=> @onLoadSucceeded text, status, xhr) - @_drainLoadingQueue() - return deferred.promise - - _drainLoadingQueue: -> - return if @_isDraining - @_isDraining = true - - drainDelay = 50 - drain = => - toLoad = BaseModel._loadingQueue.shift() - if not toLoad? - @_isDraining = false - else - toLoad.func() - toLoad.resolve(true) - - _.delay drain, drainDelay - - _.delay drain, drainDelay - diff --git a/src/client/models-old/converter.coffee b/src/client/models-old/converter.coffee deleted file mode 100644 index d765a13b6..000000000 --- a/src/client/models-old/converter.coffee +++ /dev/null @@ -1,45 +0,0 @@ -# -# Crafting Guide - converter.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Item = require "../models/game/item" -Mod = require "../models/game/mod" -ModPack = require "../models/game/mod_pack" - -######################################################################################################################## - -module.exports = class Converter - - # Public Methods ############################################################################### - - convert: (id, displayName, oldModPack)-> - modSlugToIdMap = {} - itemSlugToIdMap = {} - - newModPack = new ModPack id:id, displayName:displayName - oldModPack.eachMod (oldMod)=> - newMod = new Mod id:_.uniqueId("mod-"), displayName:oldMod.name, modPack:newModPack - modSlugToIdMap[oldMod.slug.toString()] = newMod.id - - oldMod.eachItem (oldItem)=> - newItem = new Item id:_.uniqueId("item-"), displayName:oldItem.name, mod:newMod - itemSlugToIdMap[oldItem.slug.toString()] = newItem.id - - if oldItem.isGatherable? - newItem.isGatherable = oldItem.isGatherable - - oldModPack.eachMod (oldMod)=> - newMod = newModPack.mods[modSlugToIdMap[oldMod.slug.toString()]] - - - return newModPack - - # Private Methods ############################################################################## - - _convertItem: (oldItem, newMod)-> - - _convertMod: (oldMod, newModPack)-> - return newMod \ No newline at end of file diff --git a/src/client/models-old/event_recorder.coffee b/src/client/models-old/event_recorder.coffee deleted file mode 100644 index 507ed32cd..000000000 --- a/src/client/models-old/event_recorder.coffee +++ /dev/null @@ -1,36 +0,0 @@ -# -# Crafting Guide - event_recorder.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -util = require 'util' - -######################################################################################################################## - -module.exports = class EventRecorder - - constructor: (model)-> - if not model? then throw new Error 'model is required' - - @model = model - @events = [] - - @model.on 'all', (event, model, args...)=> - logger.verbose -> "#{model?.constructor?.name}(#{model?.cid}) emitted #{event} - with args: #{util.inspect(args)}" - @events.push id:model?.cid, event:event, args:args - - # Public Methods ############################################################################### - - reset: -> - @events = [] - - # Property Methods ############################################################################# - - getNames: -> - return (e.event for e in @events) - - Object.defineProperties @prototype, - names: {get:@prototype.getNames} diff --git a/src/client/models-old/game/inventory.coffee b/src/client/models-old/game/inventory.coffee deleted file mode 100644 index a024cb2d7..000000000 --- a/src/client/models-old/game/inventory.coffee +++ /dev/null @@ -1,243 +0,0 @@ -# -# Crafting Guide - inventory.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -BaseModel = require '../base_model' -ItemSlug = require './item_slug' -Stack = require './stack' - -######################################################################################################################## - -module.exports = class Inventory extends BaseModel - - constructor: (attributes={}, options={})-> - super attributes, options - attributes.modPack ?= null - @clear() - - if options.clone? - @addInventory options.clone - - # Class Methods ################################################################################ - - @Delimiters = - Item: '.' - Stack: ':' - - # Public Methods ############################################################################### - - add: (itemSlug, quantity=1, options={})-> - return this unless quantity > 0 - - @_add itemSlug, quantity, options - @trigger c.event.add, this, itemSlug, quantity - @trigger c.event.change, this - return this - - addInventory: (inventory)-> - inventory.each (stack)=> @_add stack.itemSlug, stack.quantity - - @trigger c.event.change, this - return this - - clear: (options={})-> - @_stacks = {} - @_itemSlugs = [] - - @trigger c.event.change, this - - clone: -> - inventory = new Inventory - inventory.addInventory this - return inventory - - each: (callback)-> - for itemSlug in @_itemSlugs - stack = @_stacks[itemSlug] - continue unless stack? - callback stack - - getSlugs: -> - return @_itemSlugs[..] - - hasAtLeast: (itemSlug, quantity=1)-> - if quantity is 0 then return true - - stack = @_stacks[itemSlug] - return false unless stack? - return stack.quantity >= quantity - - localize: -> - if not @modPack? then throw new Error 'localize requires @modPack' - - changed = false - newSlugs = [] - newStacks = [] - for itemSlug in @_itemSlugs - stack = @_stacks[itemSlug] - continue unless stack? - - qualifiedSlug = if itemSlug.isQualified then itemSlug else null - if not qualifiedSlug? - qualifiedSlug = @modPack.findItem(itemSlug)?.slug - changed = qualifiedSlug? - - if qualifiedSlug? - newSlugs.push qualifiedSlug - newStacks.push new Stack itemSlug:qualifiedSlug, quantity:stack.quantity - else - newSlugs.push itemSlug - newStacks.push stack - - if changed - for itemSlug, stack of @_stacks - @stopListening stack - - @_itemSlugs = newSlugs - @_stacks = {} - for stack in newStacks - @_stacks[stack.itemSlug] = stack - @listenTo stack, c.event.change, => @trigger c.event.change, this - - @_sort() - @trigger c.event.change, this - - pop: -> - itemSlug = @_itemSlugs.pop() - return null unless itemSlug? - - stack = @_stacks[itemSlug] - delete @_stacks[itemSlug] - - @trigger c.event.remove, this, stack.itemSlug, stack.quantity - @trigger c.event.change, this - return stack - - quantityOf: (itemSlug)-> - stack = @_stacks[itemSlug] - return stack.quantity if stack - return 0 - - remove: (itemSlug, quantity=null)-> - stack = @_stacks[itemSlug] - return this unless stack? - - quantity ?= stack.quantity - return this unless quantity > 0 - - if stack.quantity < quantity - throw new Error "cannot remove #{quantity}: only #{stack.quantity} #{itemSlug} in this inventory" - - stack.quantity -= quantity - if stack.quantity is 0 - @stopListening stack - delete @_stacks[itemSlug] - @_itemSlugs = (s for s in @_itemSlugs when not ItemSlug.equal(s, itemSlug)) - - @trigger c.event.remove, this, itemSlug, quantity - @trigger c.event.change, this - return this - - toDescription: -> - return null if @isEmpty - return null unless @modPack? - - item = @modPack.findItem @_itemSlugs[0] - extras = @_itemSlugs.length - 1 - - result = "#{item.name}" - if extras > 0 then result += " and #{extras} more..." - - return result - - # Parsing Methods ############################################################################## - - parse: (data)-> - return this if not data? or data.length is 0 - - stacks = data.split Inventory.Delimiters.Stack - for stackText in stacks - stackParts = stackText.split Inventory.Delimiters.Item - if stackParts.length is 2 - quantity = parseInt stackParts[0], 10 - itemSlug = ItemSlug.slugify stackParts[1] - else if stackParts.length is 1 - quantity = 1 - itemSlug = ItemSlug.slugify stackParts[0] - else - throw new Error "expected #{stackText} to have 0 or 1 parts" - - if itemSlug.qualified.length > 0 - @add itemSlug, quantity - - return this - - unparse: (options={})-> - parts = [] - @each (stack)=> - slugText = stack.itemSlug.item - if @modPack? - item = @modPack.findItem ItemSlug.slugify slugText - if item? and item.slug.qualified isnt stack.itemSlug.qualified - slugText = stack.itemSlug.qualified - - if stack.quantity is 1 - parts.push slugText - else - parts.push "#{stack.quantity}#{Inventory.Delimiters.Item}#{slugText}" - - return parts.join Inventory.Delimiters.Stack - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - isEmpty: - get: -> @_itemSlugs.length is 0 - - totalQuantity: - get: -> - total = 0 - @each (stack)-> - total += stack.quantity - return total - - # Object Overrides ############################################################################# - - toString: -> - result = [@constructor.name, " (", @cid, ") {items: ["] - - needsDelimiter = false - @each (stack)-> - if needsDelimiter then result.push ', ' - result.push stack.toString() - needsDelimiter = true - result.push ']' - - result.push '}' - return result.join '' - - # Private Methods ############################################################################## - - _add: (itemSlug, quantity=1, options={})-> - options.insert ?= false - return unless itemSlug? - return unless quantity > 0 - - stack = @_stacks[itemSlug] - if not stack? - stack = new Stack itemSlug:itemSlug, quantity:quantity - @listenTo stack, c.event.change, => @trigger c.event.change, this - @_stacks[itemSlug] = stack - if options.insert - @_itemSlugs.unshift itemSlug - else - @_itemSlugs.push itemSlug - @_sort() - else - stack.quantity += quantity - - _sort: -> - @_itemSlugs.sort (a, b)-> ItemSlug.compare a, b diff --git a/src/client/models-old/game/item.coffee b/src/client/models-old/game/item.coffee deleted file mode 100644 index 9ab2545aa..000000000 --- a/src/client/models-old/game/item.coffee +++ /dev/null @@ -1,90 +0,0 @@ -# -# Crafting Guide - item.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -BaseModel = require '../base_model' -ItemSlug = require './item_slug' -Recipe = require './recipe' -{StringBuilder} = require 'crafting-guide-common' - -######################################################################################################################## - -module.exports = class Item extends BaseModel - - @Group = Other:'Other' - - constructor: (attributes={}, options={})-> - if not attributes.name? then throw new Error 'attributes.name is required' - - attributes.description ?= null - attributes.group ?= Item.Group.Other - attributes.ignoreDuringCrafting ?= false - attributes.isGatherable ?= false - attributes.modVersion ?= null - attributes.officialUrl ?= null - attributes.slug ?= ItemSlug.slugify attributes.name - attributes.videos ?= [] - - options.logEvents ?= false - super attributes, options - - @on c.event.change + ':modVersion', => - @_isCraftable = null - @slug.mod = @modVersion?.modSlug - - # Public Methods ############################################################################### - - compareTo: (that)-> - if this.slug isnt that.slug - return if this.slug < that.slug then -1 else +1 - if this.name isnt that.name - return if this.name < that.name then -1 else +1 - return 0 - - unparse: -> - ItemParser = require '../parsing/item_parser' # to avoid require cycles - @_parser ?= new ItemParser model:this - return @_parser.unparse() - - # Property Methods ############################################################################# - - getIsCraftable: -> - if not @_isCraftable? - @_isCraftable = false - if @modVersion? - @_isCraftable = @modVersion.hasRecipes @slug - - return @_isCraftable - - Object.defineProperties @prototype, - isCraftable: {get:@prototype.getIsCraftable} - - # Backbone.Model Overrides ##################################################################### - - parse: (text)-> - ItemParser = require '../parsing/item_parser' # to avoid require cycles - @_parser ?= new ItemParser model:this - @_parser.parse text - - return null # prevent calling `set` - - url: -> - return c.url.itemData modSlug:@slug.mod, itemSlug:@slug.item - - # Object Overrides ############################################################################# - - toString: -> - builder = new StringBuilder - return builder - .push @constructor.name, ' (', @cid, ') { ' - .push 'name:"', @name, '", ' - .push 'isCraftable:', @isCraftable, ', ' - .push 'isGatherable:', @isGatherable, ', ' - .onlyIf (@group isnt Item.Group.Other), (b)=> - b.push 'group:"', @group, '", ' - .push 'slug:"', @slug, '", ' - .push '}' - .toString() diff --git a/src/client/models-old/game/item_slug.coffee b/src/client/models-old/game/item_slug.coffee deleted file mode 100644 index 78e2ecb1f..000000000 --- a/src/client/models-old/game/item_slug.coffee +++ /dev/null @@ -1,96 +0,0 @@ -# -# Crafting Guide - item_slug.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class ItemSlug - - constructor: -> - @_item = @_mod = null - - if arguments.length is 1 - parts = _.decomposeSlug arguments[0] - @_mod = _.slugify parts[0] - @item = _.slugify parts[1] - else if arguments.length is 2 - @_mod = arguments[0] - @item = arguments[1] - else - throw new Error 'expected arguments to be "modSlug, itemSlug" or just "itemSlug"' - - # Class Methods ################################################################################ - - @compare: (a, b)-> - if a.item isnt b.item - return if a.item < b.item then -1 else +1 - if a.mod isnt b.mod - return if a.mod < b.mod then -1 else +1 - return 0 - - @equal: (a, b)-> - return true if not a? and not b? - return false unless a? and b? - return false unless a.mod is b.mod - return false unless a.item is b.item - return true - - @slugify: (arg)-> - return arg if arg?.constructor?.name is 'ItemSlug' - - [modSlug, itemSlug] = _.decomposeSlug arg - itemSlug = _.slugify itemSlug - - if modSlug? - return new ItemSlug modSlug, itemSlug - else - return new ItemSlug itemSlug - - # Public Methods ############################################################################### - - compareTo: (that)-> - return ItemSlug.compare this, that - - matches: (slug, options={exact:false})-> - return false unless slug? - return false unless typeof(slug.matches) is 'function' - - if slug.isQualified and this.isQualified - return slug.qualified is this.qualified - else - return slug.item is this.item - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - isQualified: - get: -> @_mod? - - item: - get: -> @_item - - set: (newItem)-> - if not newItem? then throw new Error 'item is required' - @_item = newItem - @mod = @mod # reset @_qualified - - mod: - get: -> @_mod - - set: (newMod)-> - @_mod = newMod - @_qualified = if @_mod? then _.composeSlugs(@_mod, @_item) else @_item - - qualified: - get: -> @_qualified - - # Object Overrides ############################################################################# - - toString: -> - return @_qualified - - valueOf: -> - return @_qualified.valueOf() diff --git a/src/client/models-old/game/mod.coffee b/src/client/models-old/game/mod.coffee deleted file mode 100644 index e5e058564..000000000 --- a/src/client/models-old/game/mod.coffee +++ /dev/null @@ -1,234 +0,0 @@ -# -# Crafting Guide - mod.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -BaseModel = require '../base_model' - -######################################################################################################################## - -module.exports = class Mod extends BaseModel - - constructor: (attributes={}, options={})-> - if not attributes.slug? then throw new Error 'attributes.slug is required' - - attributes.author ?= '' - attributes.description ?= '' - attributes.documentationUrl ?= null - attributes.downloadUrl ?= null - attributes.homePageUrl ?= null - attributes.modPack ?= null - attributes.name ?= '' - - super attributes, options - - @_activeModVersion = null - @_activeVersion = null - @_modVersions = [] - @_tutorials = [] - - # Class Methods ################################################################################## - - @Version: Version = - None: 'none' - Latest: 'latest' - - # Public Methods ################################################################################# - - compareTo: (that)-> - thisRequired = this.slug in c.requiredMods - thatRequired = that.slug in c.requiredMods - - if thisRequired isnt thatRequired - return -1 if thisRequired - return +1 if thatRequired - else if this.slug isnt that.slug - return if this.slug < that.slug then -1 else +1 - - return 0 - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - activeModVersion: - get: -> @_activeModVersion - - activeVersion: - get: -> - return @_activeVersion - - set: (version)-> - return if version is @_activeVersion - - version ?= Mod.Version.None - if version is Mod.Version.Latest then version = _.last(@_modVersions).version - - if version is Mod.Version.None - @_activeVersion = version - @_activateModVersion null - - @trigger c.event.change + ':activeVersion', this, @_activeVersion - @trigger c.event.change, this - else - for modVersion in @_modVersions - if version is modVersion.version - @_activateModVersion modVersion - break - - @_activeVersion = version - @trigger c.event.change + ':activeVersion', this, @_activeVersion - @trigger c.event.change, this - - enabled: - get: -> @_activeModVersion? - - modVersions: - get: -> @_modVersions[..] - - tutorials: - get: -> @getAllTutorials() - - # Item Methods ################################################################################# - - chooseRandomItem: -> - effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest - return null unless effectiveModVersion? - - return effectiveModVersion.chooseRandomItem() - - eachItem: (callback)-> - effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest - effectiveModVersion.eachItem callback - - findItem: (slug, options={})-> - options.includeDisabled ?= false - options.enableAsNeeded ?= false - - if not options.includeDisabled - return unless @_activeModVersion? - return @_activeModVersion.findItem slug - else - for modVersion in @_modVersions - modVersion.fetch() - - item = modVersion.findItem slug - if item? - if options.enableAsNeeded then @setActiveVersion modVersion.version - return item - - return null - - findItemByName: (name)-> - return unless @_activeModVersion? - @_activeModVersion.findItemByName name - - # ModVersion Methods ########################################################################### - - addModVersion: (modVersion)-> - return unless modVersion? - return if @_modVersions.indexOf(modVersion) isnt -1 - - @_modVersions.push modVersion - @listenTo modVersion, c.event.change, => @trigger c.event.change, this - modVersion.fileCache = this.fileCache - modVersion.mod = this - - @trigger c.event.add + ':modVersion', modVersion, this - @trigger c.event.change + ':version', modVersion, this - @trigger c.event.change, this - - if not @activeVersion? then @activeVersion = modVersion.version - if modVersion.version is @_activeVersion then @_activateModVersion modVersion - return this - - eachModVersion: (callback)-> - for modVersion in @_modVersions - callback modVersion - - getAllModVersions: -> - return @_modVersions[..] - - getModVersion: (version)-> - return null if version is Mod.Version.None - return @_modVersions[0] if version is Mod.Version.Latest - - for modVersion in @_modVersions - return modVersion if modVersion.version is version - - return null - - # Name Methods ################################################################################# - - eachName: (callback)-> - effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest - effectiveModVersion.eachName callback - - findName: (itemSlug)-> - return unless @_activeModVersion? - @_activeModVersion.findName itemSlug - - # Recipe Methods ############################################################################### - - eachRecipe: (callback)-> - effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest - effectiveModVersion.eachRecipe callback - - findRecipes: (itemSlug, result=[], options={})-> - options.alwaysFromOwningMod ?= false - - if @_activeModVersion? - return @_activeModVersion.findRecipes itemSlug, result, options - else if options.alwaysFromOwningMod and itemSlug.mod is @slug - return @getModVersion(Mod.Version.Latest).findRecipes itemSlug, result, options - - return null - - # Tutorial Methods ############################################################################# - - addTutorial: (tutorial)-> - return unless tutorial? - if @getTutorial(tutorial.slug)? then throw new Error "duplicate tutorial: #{tutorial.name}" - @_tutorials.push tutorial - tutorial.modSlug = @slug - - getAllTutorials: -> - return @_tutorials[..] - - getTutorial: (tutorialSlug)-> - for tutorial in @_tutorials - return tutorial if tutorial.slug is tutorialSlug - return null - - # Backbone.Model Overrides ##################################################################### - - parse: (text)-> - ModParser = require '../parsing/mod_parser' # to avoid require cycles - @_parser ?= new ModParser model:this - @_parser.parse text - - @_verifyActiveModVersion() - - return null # prevent calling `set` - - url: -> - return c.url.modData modSlug:@slug - - # Private Methods ############################################################################## - - _activateModVersion: (modVersion)-> - if @_activeModVersion? then @stopListening @_activeModVersion - @_activeModVersion = modVersion - @trigger c.event.change + ':activeModVersion', this, @_activeModVersion - - logger.verbose => "#{@slug} switched to version #{@_activeVersion}" - - if @_activeModVersion? - @listenTo @_activeModVersion, 'all', -> @trigger.apply this, arguments - - _verifyActiveModVersion: -> - if (@_activeVersion isnt Version.None) and (not @_activeModVersion?) - logger.warning => "#{@slug} no longer has a version #{@_activeVersion}, using latest instead" - @activeVersion = Version.Latest diff --git a/src/client/models-old/game/mod_pack.coffee b/src/client/models-old/game/mod_pack.coffee deleted file mode 100644 index 50dd10493..000000000 --- a/src/client/models-old/game/mod_pack.coffee +++ /dev/null @@ -1,202 +0,0 @@ -# -# Crafting Guide - mod_pack.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -BaseModel = require '../base_model' -Mod = require './mod' -ModVersionParser = require '../parsing/mod_version_parser' -Recipe = require './recipe' -SimpleInventory = require '../crafting/simple_inventory' - -######################################################################################################################## - -module.exports = class ModPack extends BaseModel - - constructor: (attributes={}, options={})-> - super attributes, options - - @_mods = [] - @_cache = {} - - @on c.event.change, => @_cache = {} - - # Item Methods ################################################################################# - - chooseRandomItem: -> - return null unless @_mods.length > 0 - - modIndex = Math.floor Math.random() * @_mods.length - return @_mods[modIndex].chooseRandomItem() - - findItem: (itemSlug, options={})-> - options.includeDisabled ?= false - - key = "#{itemSlug}-#{options.includeDisabled}" - @_cache.itemBySlug ?= {} - item = @_cache.itemBySlug[key] - return item if item? - - if itemSlug.isQualified - mod = @getMod itemSlug.mod - if mod? - item = mod.findItem itemSlug, options - - if not item? - for mod in @_mods - continue unless mod.enabled or options.includeDisabled - item = mod.findItem itemSlug, options - break if item? - - if item? - @_cache.itemBySlug[key] = item - - return item - - findItemByName: (name, options={})-> - options.enableAsNeeded ?= false - options.includeDisabled = true if options.enableAsNeeded - - for mod in @_mods - continue unless mod.enabled or options.includeDisabled - item = mod.findItemByName name, options - return item if item? - - return null - - findItemDisplay: (itemSlug)-> - if not itemSlug? then throw new Error 'itemSlug is required' - - result = {slug:itemSlug} - item = @findItem itemSlug, includeDisabled:true - if item? - result.itemName = item.name - result.itemSlug = item.slug.item - result.modSlug = item.slug.mod - result.modVersion = item.modVersion.version - else - result.itemName = @findName itemSlug, includeDisabled:true - result.itemSlug = itemSlug.item - result.modSlug = @_mods[0].slug - result.modVersion = @_mods[0].activeVersion - - craftingUrlInventory = new SimpleInventory modPack:this - if item?.multiblock? - craftingUrlInventory.addInventory item.multiblock.inventory - else - craftingUrlInventory.add itemSlug - - result.craftingUrl = c.url.crafting inventoryText:craftingUrlInventory.unparse() - result.iconUrl = c.url.itemIcon result - result.itemUrl = c.url.item result - result.modName = @getMod(result.modSlug).name - return result - - qualifySlug: (itemSlug)-> - return itemSlug if itemSlug.isQualified - - item = @findItem itemSlug - return item.slug if item? - return itemSlug - - # Mod Methods ################################################################################## - - addMod: (mod)-> - if not mod? then throw new Error 'mod is required' - return if @_mods.indexOf(mod) isnt -1 - - mod.modPack = this - @_mods.push mod - @listenTo mod, c.event.change, (modVersion)=> @_onModVersionLoaded modVersion - @trigger c.event.add + ':mod', mod, this - - @_mods.sort (a, b)-> a.compareTo b - @trigger c.event.sort + ':mod', this - @trigger c.event.change, this - - return this - - eachMod: (callback)-> - for mod in @_mods - callback mod - - getMod: (slug)-> - for mod in @_mods - return mod if mod.slug is slug - return null - - getAllMods: -> - return @_mods[..] - - removeMod: (mod)-> - index = @_mods.indexOf mod - return unless index >= 0 - - @_mods.splice index, 1 - - @trigger c.event.remove, this, mod.slug - @trigger c.event.change, this - - # Name Methods ################################################################################# - - findName: (slug, options={})-> - options.includeDisabled ?= false - - for mod in @_mods - continue unless mod.enabled or options.includeDisabled - name = mod.findName slug - return name if name - - return null - - # Recipe Methods ############################################################################### - - findRecipes: (itemSlug, options={})-> - options.alwaysFromOwningMod ?= false - return null unless itemSlug? - - key = "#{itemSlug}-#{options.alwaysFromOwningMod}" - @_cache.recipesBySlug ?= {} - result = @_cache.recipesBySlug[key] - return result if result? - - result = [] - for mod in @_mods - if not mod.enabled - owningMod = itemSlug.isQualified and (itemSlug.mod is mod.slug) - continue unless owningMod and options.alwaysFromOwningMod - - mod.findRecipes itemSlug, result, options - - @_cache.recipesBySlug[key] = result - return if result.length > 0 then result else null - - # Object Overrides ############################################################################# - - toString: -> - return "ModPack (#{@cid}) {modVersions:«#{@_mods.length} items»}" - - # Private Methods ############################################################################## - - _onModVersionLoaded: (modVersion)-> - mods = @getAllMods() - return true unless mods.length > 0 - - for mod in mods - if mod.isError - @removeMod mod - continue - - modVersions = mod.getAllModVersions() - return true unless modVersions.length > 0 - continue if mod.activeVersion is Mod.Version.None - - activeModVersion = mod.activeModVersion - return true unless activeModVersion? - return true if activeModVersion.isUnloaded - return true if activeModVersion.isLoading - - @trigger c.event.change, this - @trigger c.event.sync, this diff --git a/src/client/models-old/game/mod_version.coffee b/src/client/models-old/game/mod_version.coffee deleted file mode 100644 index 7d5268406..000000000 --- a/src/client/models-old/game/mod_version.coffee +++ /dev/null @@ -1,200 +0,0 @@ -# -# Crafting Guide - mod_version.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -BaseModel = require '../base_model' -Item = require './item' -ItemSlug = require './item_slug' -Recipe = require './recipe' - -######################################################################################################################## - -module.exports = class ModVersion extends BaseModel - - constructor: (attributes={}, options={})-> - if not attributes.modSlug? then throw new Error 'attributes.modSlug is required' - if not attributes.version? then throw new Error 'attributes.version is required' - attributes.mod ?= null - super attributes, options - - @_groups = {} - @_items = {} - @_names = {} - @_recipes = {} - @_slugs = [] - - # Public Methods ############################################################################### - - compareTo: (that)-> - if this.mod? and that.mod? - return this.mod.compareTo that.mod - - if this.modSlug isnt that.modSlug - return if this.modSlug < that.modSlug then -1 else +1 - - return 0 - - sort: -> - @_slugs.sort (a, b)-> ItemSlug.compare a, b - - # Item Methods ################################################################################# - - addItem: (item)-> - if @findItem(item.slug)? then throw new Error "duplicate item for #{item.name}" - - item.modVersion = this - @_items[item.slug.item] = item - @_groups[item.group] ?= {} - @_groups[item.group][item.slug.item] = item - - @registerName item.slug, item.name - return this - - allItemsInGroup: (group)-> - result = [] - @eachItemInGroup group, (item)-> result.push item - return null if result.length is 0 - return result - - chooseRandomItem: -> - itemIndex = Math.floor Math.random() * @_slugs.length - return @_slugs[itemIndex] - - eachItem: (callback)-> - for slug in @_slugs - item = @findItem slug - continue unless item? - callback item - return this - - eachItemInGroup: (group, callback)-> - itemMap = @_groups[group] - return unless itemMap? - - items = _.values(itemMap).sort (a, b)-> a.compareTo b - for item in items - callback item - - findItem: (itemSlug)-> - return @_items[itemSlug.item] - - findItemByName: (name)-> - for itemSlug, item of @_items - return item if item.name is name - return null - - # Group Methods ################################################################################ - - getAllGroups: -> - result = [] - @eachGroup (group)-> result.push group - return result - - eachGroup: (callback)-> - groupNames = _.keys @_groups - groupNames.sort (a, b)-> - if a is b then return 0 - if a is Item.Group.Other then return -1 - if b is Item.Group.Other then return +1 - return if a < b then -1 else +1 - - for groupName in groupNames - callback groupName - - # Name Methods ################################################################################# - - eachName: (callback)-> - for slug in @_slugs - callback @_names[slug.item], slug - return this - - findName: (itemSlug)-> - return @_names[itemSlug.item] - - registerName: (itemSlug, name)-> - return if @_names[itemSlug.item] - @_names[itemSlug.item] = name - @_slugs.push itemSlug - return this - - # Recipe Methods ############################################################################### - - addRecipe: (recipe)-> - return unless recipe? - - recipe.modVersion = this - if @_recipes[recipe.slug]? then throw new Error "duplicate recipe: #{recipe.slug}" - - @_recipes[recipe.slug] = recipe - return this - - eachRecipe: (callback)-> - recipes = _.values(@_recipes).sort (a, b)-> Recipe.compareFor a, b - for recipe in recipes - callback recipe - return this - - findRecipes: (itemSlug, result=[], options={})-> - options.onlyPrimary ?= false - options.forCrafting ?= false - - primaryRecipes = [] - otherRecipes = [] - - for recipe in _.values @_recipes - continue unless recipe.isConditionSatisfied() - continue unless recipe.hasAllTools() - continue if options.forCrafting and recipe.ignoreDuringCrafting - - if recipe.itemSlug.matches itemSlug - primaryRecipes.push recipe - else if recipe.produces itemSlug - otherRecipes.push recipe - - for recipe in primaryRecipes - result.push recipe - - if not options.onlyPrimary and result.length is 0 - for recipe in otherRecipes - result.push recipe - - return result - - findExternalRecipes: -> - result = {} - for k, recipe of @_recipes - continue if recipe.itemSlug.isQualified - - recipeList = result[recipe.itemSlug] - if not recipeList then recipeList = result[recipe.itemSlug] = [] - - recipeList.push recipe - - return result - - hasRecipes: (itemSlug)-> - for k, recipe of @_recipes - return true if recipe.produces itemSlug - return false - - # Backbone.Model Overrides ##################################################################### - - parse: (text)-> - ModVersionParser = require '../parsing/mod_version_parser' # to avoid require cycles - @_parser ?= new ModVersionParser model:this - @_parser.parse text - - return null # prevent calling `set` - - url: -> - return c.url.modVersionData modSlug:@modSlug, modVersion:@version - - # Object Overrides ############################################################################# - - toString: -> - return "ModVersion (#{@cid}) { - modSlug:#{@modSlug}, version:#{@version}, items:«#{@_slugs.length} items» - }" diff --git a/src/client/models-old/game/multiblock.coffee b/src/client/models-old/game/multiblock.coffee deleted file mode 100644 index 273dfd702..000000000 --- a/src/client/models-old/game/multiblock.coffee +++ /dev/null @@ -1,85 +0,0 @@ -# -# Crafting Guide - multiblock.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -BaseModel = require '../base_model' -Inventory = require './inventory' - -######################################################################################################################## - -module.exports = class Multiblock extends BaseModel - - constructor: (attributes={}, options={})-> - if not attributes.input? then throw new Error 'attributes.input is required' - if not attributes.layers? then throw new Error 'attributes.layers is required' - if attributes.layers.length < 1 then throw new Error 'attributes.layers.length must be >= 1' - super attributes, options - - @_analyzePattern() - - # Public Methods ############################################################################### - - getStackAt: (x, y, z)-> - value = @_stackCache[y]?[z]?[x] - return null if value is undefined - return value - - getLayerInventory: (y)-> - @_layerInventories ?= [] - result = @_layerInventories[y] - if not result? then result = @_layerInventories[y] = new Inventory - return result - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - depth: - get: -> @_depth - - height: - get: -> @_height - - inventory: - get: -> @_inventory - - width: - get: -> @_width - - # Private Methods ############################################################################## - - _analyzeLayer: (layer, y, stackCacheLayer)-> - rows = layer.split ' ' - @_depth = Math.max @_depth, rows.length - - for row in rows - @_width = Math.max @_width, row.length - stackCacheRow = [] - stackCacheLayer.push stackCacheRow - @_analyzeRow row, stackCacheRow, @getLayerInventory(y) - - _analyzePattern: -> - @_depth = @_height = @_width = 0 - @_inventory = new Inventory - @_stackCache = [] - - for layer, y in @layers - stackCacheLayer = [] - @_stackCache.push stackCacheLayer - @_analyzeLayer layer, y, stackCacheLayer - - @_height = @layers.length - - _analyzeRow: (row, stackCacheRow, layerInventory)-> - for cell, x in row.split '' - index = parseInt cell - stack = null - - if not _.isNaN index - stack = @input[index] - @_inventory.add stack.itemSlug, stack.quantity - layerInventory.add stack.itemSlug, stack.quantity - - stackCacheRow.push stack diff --git a/src/client/models-old/game/recipe.coffee b/src/client/models-old/game/recipe.coffee deleted file mode 100644 index a3416a42f..000000000 --- a/src/client/models-old/game/recipe.coffee +++ /dev/null @@ -1,247 +0,0 @@ -# -# Crafting Guide - recipe.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -BaseModel = require '../base_model' -ItemSlug = require './item_slug' -Stack = require './stack' -{StringBuilder} = require 'crafting-guide-common' - -######################################################################################################################## - -module.exports = class Recipe extends BaseModel - - constructor: (attributes={}, options={})-> - if not attributes.input? then throw new Error 'attributes.input is required' - if not attributes.pattern? then throw new Error 'attributes.pattern is required' - - if attributes.itemSlug? and not attributes.output? - attributes.output = [new Stack itemSlug:attributes.itemSlug, quantity:1] - else if attributes.output? and not attributes.itemSlug? - if attributes.output.length is 0 then throw new Error 'attributes.output cannot be empty' - attributes.itemSlug = attributes.output[0].itemSlug - else - throw new Error 'attributes.itemSlug or attributes.output is required' - - attributes.pattern = @_parsePattern attributes.pattern - - attributes.condition ?= null - attributes.ignoreDuringCrafting ?= false - attributes.modVersion ?= null - attributes.tools ?= [] - options.logEvents ?= false - super attributes, options - - @_computeQuantities attributes.pattern - - @on c.event.change + ':modVersion', => @_slug = null - @on c.event.change + ':pattern', => @_patternCache = null - - # Class Methods ################################################################################ - - @compareFor: (a, b, itemSlug)-> - if itemSlug? - aValue = a.itemSlug.matches itemSlug - bValue = b.itemSlug.matches itemSlug - if aValue isnt bValue - return -1 if aValue - return +1 if bValue - - aValue = a.getQuantityProduced itemSlug - bValue = b.getQuantityProduced itemSlug - if aValue isnt bValue - return if aValue > bValue then -1 else +1 - - return 0 - - # Public Methods ############################################################################### - - getStackAtSlot: (patternSlot)-> - trueIndex = 0:0, 1:1, 2:2, 3:4, 4:5, 5:6, 6:8, 7:9, 8:10 - 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 - - getQuantityProduced: (itemSlug)-> - total = 0 - for stack in @output - if stack.itemSlug.matches itemSlug - total += stack.quantity - - return total - - getQuantityRequired: (itemSlug)-> - total = 0 - for stack, index in @input - if ItemSlug.equal stack.itemSlug, itemSlug - total += @_quantities[index] * stack.quantity - - return total - - hasAllTools: (modPack)-> - modPack ?= @modVersion?.mod?.modPack - return true unless modPack? - - for stack in @tools - return false unless modPack.findItem stack.itemSlug - return true - - isConditionSatisfied: (modPack)-> - return true unless @condition? - modPack ?= @modVersion?.mod?.modPack - - result = false - if @condition.verb is 'item' - if modPack?.findItemByName(@condition.noun)? - result = true - else if @condition.verb is 'mod' - modPack.eachMod (mod)=> - if mod.name is @condition.noun - result = true - - if @condition.inverted then result = not result - return result - - isPassThroughFor: (itemSlug)-> - return @getQuantityProduced(itemSlug) is @getQuantityRequired(itemSlug) - - produces: (itemSlug)-> - if not @_produces? - @_produces = {} - - for stack in @output - actuallyProduces = not @isPassThroughFor stack.itemSlug - @_produces[stack.itemSlug.qualified] = actuallyProduces - - result = @_produces[itemSlug.qualified] or @_produces[itemSlug.item] - return result - - requires: (itemSlug)-> - for stack in @input - if stack.itemSlug.matches itemSlug - return true - return false - - requiresTool: (itemSlug)-> - for stack in @tools - if stack.itemSlug.matches itemSlug - return true - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - slug: - get: -> - if not @_slug? - builder = new StringBuilder - delimiterNeeded = false - for stack in @input - if delimiterNeeded then builder.push ',' - delimiterNeeded = true - - if stack.quantity > 1 then builder.push stack.quantity, ' ' - builder.push stack.itemSlug.qualified - - builder.push '>' - builder.push @pattern - builder.push '>' - for stack in @tools - builder.push stack.itemSlug.qualified - builder.push '>' - - delimiterNeeded = false - for stack in @output - if delimiterNeeded then builder.push ',' - delimiterNeeded = true - - if stack.quantity > 1 then builder.push stack.quantity, ' ' - builder.push stack.itemSlug.qualified - - @_slug = builder.toString() - - return @_slug - - # Object Overrides ############################################################################# - - toString: -> - result = [@constructor.name, " (", @cid, ") { name:", @name] - - result.push ", input:[" - needsDelimiter = false - for stack in @input - if needsDelimiter then result.push ', ' - result.push @getQuantityRequired stack.itemSlug - result.push ' ' - result.push stack.itemSlug - needsDelimiter = true - result.push ']' - - result.push ", output:[" - needsDelimiter = false - for stack in @output - if needsDelimiter then result.push ', ' - result.push @getQuantityProduced stack.itemSlug - result.push ' ' - result.push stack.itemSlug - needsDelimiter = true - result.push ']' - - if @tools.length > 0 - result.push ", tools:[" - needsDelimiter = false - for stack in @tools - if needsDelimiter then result.push ', ' - result.push stack.toString() - needsDelimiter = true - result.push ']' - - result.push '}' - return result.join '' - - # Private Methods ############################################################################## - - _computeQuantities: (pattern)-> - quantityMap = {} - - index = 0 - while index < pattern.length - c = pattern[index] - index += 1 - - continue if c is '.' - continue if c is ' ' - - if quantityMap[c]? - quantityMap[c] += 1 - else - quantityMap[c] = 1 - - @_quantities = [] - for i in [0...@input.length] - @_quantities.push quantityMap["#{i}"] - - _parsePattern: (pattern)-> - return unless pattern? - - pattern = pattern.replace /\ /g, '' - return if pattern.length is 0 - - pattern = pattern.replace /[^0-9]/g, '.' - - array = pattern.split '' - array = array[0...9] - while array.length isnt 9 - array.push '.' - - pattern = array.join '' - pattern = pattern.replace /(...)(...)(...)/, '$1 $2 $3' - return pattern diff --git a/src/client/models-old/game/simple_stack.coffee b/src/client/models-old/game/simple_stack.coffee deleted file mode 100644 index d077f4c58..000000000 --- a/src/client/models-old/game/simple_stack.coffee +++ /dev/null @@ -1,33 +0,0 @@ -# -# Crafting Guide - simple_stack.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -ItemSlug = require './item_slug' - -######################################################################################################################## - -module.exports = class Stack - - constructor: (attributes={}, options={})-> - if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required' - attributes.quantity ?= 1 - - @itemSlug = attributes.itemSlug - @quantity = attributes.quantity - - # Class Methods ################################################################################ - - @compare: (a, b)-> - if a? and not b? then return -1 - if not a? and b? then return +1 - if a.quantity isnt b.quantity - return if a.quantity > b.quantity then -1 else +1 - return ItemSlug.compare a.itemSlug, b.itemSlug - - # Object Overrides ############################################################################# - - toString: -> - return "#{@quantity} #{@itemSlug}" diff --git a/src/client/models-old/game/stack.coffee b/src/client/models-old/game/stack.coffee deleted file mode 100644 index 6797330dd..000000000 --- a/src/client/models-old/game/stack.coffee +++ /dev/null @@ -1,23 +0,0 @@ -# -# Crafting Guide - stack.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -BaseModel = require '../base_model' - -######################################################################################################################## - -module.exports = class Stack extends BaseModel - - constructor: (attributes={}, options={})-> - if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required' - attributes.quantity ?= 1 - options.logEvents ?= false - super attributes, options - - # Object Overrides ############################################################################# - - toString: -> - return "#{@quantity} #{@itemSlug}" diff --git a/src/client/models-old/parsing/command_parser_version_base.coffee b/src/client/models-old/parsing/command_parser_version_base.coffee deleted file mode 100644 index 00e0e0ab0..000000000 --- a/src/client/models-old/parsing/command_parser_version_base.coffee +++ /dev/null @@ -1,138 +0,0 @@ -# -# Crafting Guide - command_parser_version_base.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -{StringBuilder} = require 'crafting-guide-common' - -######################################################################################################################## - -module.exports = class CommandParserVersionBase - - constructor: (options={})-> - if not options.model? then throw new Error 'options.model is required' - options.showAllErrors ?= false - - @_model = options.model - @_showAllErrors = options.showAllErrors - @errors = [] - - # Class Methods ################################################################################ - - @COMMAND = /\ *([^:]*):?(.*)/ - - @COMMENT = /([^\\]?)#.*/ - - @simplify: (text)-> - text = text.trim() - text = text.replace /\ */g , ' ' - text = text.replace /\n/g, ';' - text = text.replace /; */g, ';' - text = text.replace /;;*/g, ';' - text = text.replace /: /g, ':' - return text - - # Public Methods ############################################################################### - - parse: (text)-> - @_rawData = {} - @_lineNumber = 1 - @errors = [] - - @_lines = text.split '\n' - @_lineNumber = 0 - while @_lineNumber < @_lines.length - @_lineNumber += 1 - commands = @_parseLine @_lines[@_lineNumber - 1] - 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' - - _command_schema: -> # do nothing - - # 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 CommandParserVersionBase.COMMENT, '$1' - line = line.trim() - return [] if line.length is 0 - - [line, hereDoc] = @_parseHereDoc line - - lineParts = (part.trim() for part in line.split(';')) - commands = [] - for linePart in lineParts - continue if linePart.length is 0 - - match = CommandParserVersionBase.COMMAND.exec linePart - if not match? then throw new Error "Expected : , but found: \"#{linePart}\"" - - args = [] - args = (s for s in match[2].split(',') when s.length > 0) if match[2]? - args = (s.trim() for s in args) - args = (s for s in args when s.length > 0) - args.push hereDoc if hereDoc? - 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 - @errors.push e - logger.error -> e.message - - _parseHereDoc: (line)-> - hereDocIndex = line.indexOf '<<-' - return [line, null] unless hereDocIndex isnt -1 - - hereDocStopText = line[hereDocIndex+3...line.length] - line = line[0...hereDocIndex] - - hereDocLines = [] - while true - @_lineNumber += 1 - break if @_lineNumber >= @_lines.length - - nextLine = @_lines[@_lineNumber-1] - break if nextLine.trim() is hereDocStopText - hereDocLines.push nextLine - - shortestIndent = Number.MAX_VALUE - for hereDocLine in hereDocLines - continue if hereDocLine.trim().length is 0 - shortestIndent = Math.min hereDocLine.match(/( *).*/)[1].length, shortestIndent - - for i in [0...hereDocLines.length] - hereDocLines[i] = hereDocLines[i][shortestIndent..] - - return [line, null] unless hereDocLines.length > 0 - return [line, hereDocLines.join('\n')] diff --git a/src/client/models-old/parsing/command_parser_version_base.test.coffee b/src/client/models-old/parsing/command_parser_version_base.test.coffee deleted file mode 100644 index 903e5adef..000000000 --- a/src/client/models-old/parsing/command_parser_version_base.test.coffee +++ /dev/null @@ -1,48 +0,0 @@ -# -# Crafting Guide - command_parser_version_base.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CommandParserVersionBase = require './command_parser_version_base' - -######################################################################################################################## - -parser = null - -######################################################################################################################## - -describe 'command_parser_version_base.coffee', -> - - beforeEach -> parser = new CommandParserVersionBase model:{} - - describe '_parseHereDoc', -> - - it 'returns null for non-heredoc lines', -> - result = parser._parseHereDoc 'foobar: baz' - expect(result[1]).to.be.null - - it 'identifies the right text for a real heredoc', -> - parser._lines = ['command: <<-END', 'alpha', 'bravo', 'charlie', 'END', 'command1: arg2'] - parser._lineNumber = 1 - - result = parser._parseHereDoc parser._lines[0] - result[0].should.equal 'command: ' - result[1].should.equal 'alpha\nbravo\ncharlie' - - it 'identifies an empty heredoc', -> - parser._lines = ['command: <<-END', 'END'] - parser._lineNumber = 1 - - result = parser._parseHereDoc parser._lines[0] - result[0].should.equal 'command: ' - expect(result[1]).to.be.null - - it 'trims smallest leading whitespace', -> - parser._lines = ['command: <<-END', ' alpha', ' bravo', '', ' charlie', 'END', 'command1: arg2'] - parser._lineNumber = 1 - - result = parser._parseHereDoc parser._lines[0] - result[0].should.equal 'command: ' - result[1].should.equal 'alpha\n bravo\n\ncharlie' diff --git a/src/client/models-old/parsing/item_parser.coffee b/src/client/models-old/parsing/item_parser.coffee deleted file mode 100644 index eb0d5cfa0..000000000 --- a/src/client/models-old/parsing/item_parser.coffee +++ /dev/null @@ -1,19 +0,0 @@ -# -# Crafting Guide - item_parser.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -VersionedParserBase = require './versioned_parser_base' -ItemParserV1 = require './item_parser_v1' - -######################################################################################################################## - -module.exports = class ItemParser extends VersionedParserBase - - # VersionedParserBase Overrides ################################################################ - - _createParsers: (options)-> - return result = - '1': new ItemParserV1 options diff --git a/src/client/models-old/parsing/item_parser_v1.coffee b/src/client/models-old/parsing/item_parser_v1.coffee deleted file mode 100644 index 32c9a6a95..000000000 --- a/src/client/models-old/parsing/item_parser_v1.coffee +++ /dev/null @@ -1,72 +0,0 @@ -# -# Crafting Guide - item_parser_v1.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CommandParserVersionBase = require './command_parser_version_base' - -######################################################################################################################## - -module.exports = class ItemParserV1 extends CommandParserVersionBase - - # CommandParserVersionBase Overrides ########################################################### - - _buildModel: (rawData, model)-> - @_buildItem rawData, model - - _unparseModel: (builder, model)-> - builder.line 'schema: ', 1 - builder.line() - - @_unparseItem builder, model - - # Command Methods ############################################################################## - - _command_description: (textParts...)-> - if not @_rawData.description? - @_rawData.description = '' - else - @_rawData.description += '\n' - @_rawData.description += textParts.join ', ' - - _command_officialUrl: (officialUrl)-> - if @_rawData.officialUrl? then throw new Error 'duplicate declaration of "officialUrl"' - if not officialUrl? or (officialUrl.length is 0) then throw new Error 'officialUrl cannot be empty' - @_rawData.officialUrl = officialUrl - - _command_video: (youTubeId, nameParts...)-> - if not youTubeId?.length then throw new Error 'video declaration requires a YouTubeID' - name = nameParts.join ', ' - if not name?.length then throw new Error 'video declaration requires a name' - - @_rawData.videos ?= [] - @_rawData.videos.push youTubeId:youTubeId, name:name - - # Object Building Methods ###################################################################### - - _buildItem: (rawData, model)-> - model.description = rawData.description if rawData.description? - model.officialUrl = rawData.officialUrl if rawData.officialUrl? - model.videos = rawData.videos if rawData.videos? - - # Un-parsing Methods ########################################################################### - - _unparseItem: (builder, model)-> - if model.officialUrl? - builder.line 'officialUrl: ', model.officialUrl - builder.line() - - if model.description? - if model.description.indexOf('\n') isnt -1 - builder.line 'description: <<-END' - builder.line model.description - builder.line 'END' - else - builder.line 'description: ', model.description - builder.line() - - for video in model.videos - builder.line 'video: ', video.youTubeId, ', ', video.name - builder.line() diff --git a/src/client/models-old/parsing/item_parser_v1.test.coffee b/src/client/models-old/parsing/item_parser_v1.test.coffee deleted file mode 100644 index 25f6e7f5c..000000000 --- a/src/client/models-old/parsing/item_parser_v1.test.coffee +++ /dev/null @@ -1,99 +0,0 @@ -# -# Crafting Guide - item_parser_v1.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Item = require '../game/item' -ItemParserV1 = require './item_parser_v1' - -######################################################################################################################## - -baseText = item = parser = null - -######################################################################################################################## - -describe 'item_parser_v1.coffee', -> - - beforeEach -> - item = new Item name:'alpha' - parser = new ItemParserV1 model:item - - describe 'officialUrl', -> - - it 'may be omitted', -> - parser.parse 'schema: 1\nvideo: youtubeid, Video Alpha\ndescription: Bravo, Charlie' - expect(item.officialUrl).to.be.null - - it 'is assigned properly when given', -> - parser.parse 'schema: 1\nofficialUrl: http://testurl.com' - item.officialUrl.should.equal 'http://testurl.com' - - it 'does not allow duplicate declarations', -> - func = -> parser.parse 'schema: 1\nofficialUrl: http://testurl.com\nofficialUrl: http://testurl2.com' - expect(func).to.throw Error, 'duplicate' - - it 'does not allow an empty value if given', -> - func = -> parser.parse 'schema: 1\nofficialUrl:' - expect(func).to.throw Error, 'empty' - - describe 'description', -> - - it 'may be omitted', -> - parser.parse 'schema: 1\nvideo: youTubeId, Video Alpha\nofficialUrl: http://testurl.com' - expect(item.description).to.be.null - - it 'is assigned properly when given', -> - parser.parse 'schema: 1\ndescription: Alpha Bravo Charlie' - item.description.should.equal 'Alpha Bravo Charlie' - - it 'can be a heredoc', -> - parser.parse 'schema: 1\ndescription: <<-END\nAlpha\nBravo\nCharlie\nEND' - item.description.should.equal 'Alpha\nBravo\nCharlie' - - it 'concatenates multiple declarations', -> - parser.parse 'schema: 1\ndescription: Alpha\ndescription: Bravo' - item.description.should.equal 'Alpha\nBravo' - - describe 'video', -> - - it 'may be omitted', -> - parser.parse 'schema: 1\nofficialUrl: http://testurl.com\ndescription: Alpha Bravo Charlie' - item.videos.should.eql [] - - it 'is assigned properly when given', -> - parser.parse 'schema: 1\nvideo: youtubeid, Alpha Bravo' - item.videos[0].should.eql youTubeId:'youtubeid', name:'Alpha Bravo' - item.videos.length.should.equal 1 - - it 'may be included multiple times', -> - parser.parse 'schema: 1\nvideo: youtubeid1, Alpha\nvideo: youtubeid2, Bravo' - item.videos[0].should.eql youTubeId:'youtubeid1', name:'Alpha' - item.videos[1].should.eql youTubeId:'youtubeid2', name:'Bravo' - item.videos.length.should.equal 2 - - it 'requires a YouTubeId and name', -> - func = -> parser.parse 'schema: 1\nvideo: alpha' - expect(func).to.throw Error, 'requires a name' - - describe 'unparsing', -> - - it 'can round-trip a fully described item', -> - text = """ - schema: 1 - - officialUrl: http://testurl.com - - description: <<-END - Alpha - Bravo - END - - video: youtubeid1, Alpha Bravo - video: youtubeid2, Charlie Delta - - - """ - parser.parse text - parser.unparse().should.equal text diff --git a/src/client/models-old/parsing/mod_parser.coffee b/src/client/models-old/parsing/mod_parser.coffee deleted file mode 100644 index 03f368b96..000000000 --- a/src/client/models-old/parsing/mod_parser.coffee +++ /dev/null @@ -1,19 +0,0 @@ -# -# Crafting Guide - mod_parser.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -VersionedParserBase = require './versioned_parser_base' -ModParserV1 = require './mod_parser_v1' - -######################################################################################################################## - -module.exports = class ModParser extends VersionedParserBase - - # VersionedParserBase Overrides ################################################################ - - _createParsers: (options)-> - return result = - '1': new ModParserV1 options diff --git a/src/client/models-old/parsing/mod_parser_v1.coffee b/src/client/models-old/parsing/mod_parser_v1.coffee deleted file mode 100644 index f0f9ab5b8..000000000 --- a/src/client/models-old/parsing/mod_parser_v1.coffee +++ /dev/null @@ -1,94 +0,0 @@ -# -# Crafting Guide - mod_parser_v1.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CommandParserVersionBase = require './command_parser_version_base' -Mod = require '../game/mod' -ModVersion = require '../game/mod_version' -Tutorial = require '../site/tutorial' - -######################################################################################################################## - -module.exports = class ModParserV1 extends CommandParserVersionBase - - # CommandParserVersionBase Overrides ########################################################### - - _buildModel: (rawData, model)-> - @_buildMod rawData, model - - _unparseModel: (builder, model)-> - @_unparseMod builder, model - - # Command Methods ############################################################################## - - _command_author: (authorParts...)-> - if @_rawData.author? then throw new Error 'duplicate declaration of "author"' - author = authorParts.join ', ' - if author.length is 0 then throw new Error '"author" cannot be empty, but may be omitted' - - @_rawData.author = author - - _command_description: (descriptionParts...)-> - if @_rawData.description? then throw new Error 'duplicate declaration of "description"' - description = descriptionParts.join ', ' - if description.length is 0 then throw new Error '"description" cannot be empty, but may be omitted' - - @_rawData.description = description - - _command_documentationUrl: (documentationUrl)-> - documentationUrl ?= '' - if @_rawData.documentationUrl? then throw new Error 'duplicate declaration of "documentationUrl"' - if documentationUrl.length is 0 then throw new Error 'documentationUrl cannot be empty (omit it instead)' - @_rawData.documentationUrl = documentationUrl - - _command_downloadUrl: (downloadUrl)-> - if @_rawData.downloadUrl? then throw new Error 'duplicate declaration of "downloadUrl"' - if downloadUrl.length is 0 then throw new Error 'downloadUrl cannot be empty (omit it instead)' - @_rawData.downloadUrl = downloadUrl - - _command_homePageUrl: (homePageUrl='')-> - if @_rawData.homePageUrl? then throw new Error 'duplicate declaration of "homePageUrl"' - if homePageUrl.length is 0 then throw new Error 'homePageUrl cannot be empty' - - @_rawData.homePageUrl = homePageUrl - - _command_name: (name)-> - if @_rawData.name? then throw new Error 'duplicate declaration of "name"' - if name.length is 0 then throw new Error '"name" cannot be empty' - @_rawData.name = name - - _command_tutorial: (nameParts...)-> - name = nameParts.join(', ').trim() - if name.length is 0 then throw new Error '"name" cannot be empty' - @_rawData.tutorialNames ?= [] - @_rawData.tutorialNames.push name - - _command_version: (version='')-> - if version.length is 0 then throw new Error 'version cannot be empty' - - @_rawData.versions ?= [] - @_rawData.versions.push version - - # Object Building Methods ###################################################################### - - _buildMod: (rawData, model)-> - if not rawData.name? then throw new Error 'the "name" declaration is required' - if not rawData.homePageUrl? then throw new Error 'the "homePageUrl" declaration is required' - if not rawData.versions? then throw new Error 'at least one "version" declaration is required' - - model.author = rawData.author if rawData.author? - model.description = rawData.description if rawData.description? - model.documentationUrl = rawData.documentationUrl if rawData.documentationUrl? - model.downloadUrl = rawData.downloadUrl if rawData.downloadUrl? - model.name = rawData.name - model.homePageUrl = rawData.homePageUrl - - if rawData.tutorialNames? - for tutorialName in rawData.tutorialNames - model.addTutorial new Tutorial name:tutorialName - - for version in rawData.versions - model.addModVersion new ModVersion modSlug:model.slug, version:version diff --git a/src/client/models-old/parsing/mod_version_parser.coffee b/src/client/models-old/parsing/mod_version_parser.coffee deleted file mode 100644 index 2ba90d6c7..000000000 --- a/src/client/models-old/parsing/mod_version_parser.coffee +++ /dev/null @@ -1,19 +0,0 @@ -# -# Crafting Guide - mod_version_parser.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -VersionedParserBase = require './versioned_parser_base' -ModVersionParserV1 = require './mod_version_parser_v1' - -######################################################################################################################## - -module.exports = class ModVersionParser extends VersionedParserBase - - # VersionedParserBase Overrides ################################################################ - - _createParsers: (options)-> - return result = - '1': new ModVersionParserV1 options diff --git a/src/client/models-old/parsing/mod_version_parser_v1.coffee b/src/client/models-old/parsing/mod_version_parser_v1.coffee deleted file mode 100644 index 9248b6ad8..000000000 --- a/src/client/models-old/parsing/mod_version_parser_v1.coffee +++ /dev/null @@ -1,380 +0,0 @@ -# -# Crafting Guide - mod_version_parser_v1.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CommandParserVersionBase = require './command_parser_version_base' -Item = require '../game/item' -ItemSlug = require '../game/item_slug' -ModVersion = require '../game/mod_version' -Multiblock = require '../game/multiblock' -Recipe = require '../game/recipe' -Stack = require '../game/simple_stack' -{StringBuilder} = require 'crafting-guide-common' - -######################################################################################################################## - -module.exports = class ModVersionParserV1 extends CommandParserVersionBase - - # Class Methods ################################################################################ - - @INTEGER = /[0-9]+/ - - @PATTERN = /^[0-9.]{3} ?[0-9.]{3} ?[0-9.]{3}$/ - - @STACK = /^([0-9]+) +(.*)$/ - - # CommandParserVersionBase Overrides ########################################################### - - _buildModel: (rawData, model)-> - @_buildModVersion rawData, model - - _unparseModel: (builder, model)-> - @_unparseModVersion builder, model - - # Command Methods ############################################################################## - - _command_extras: (extraTerms...)-> - if not @_recipeData? then throw new Error 'cannot declare "extras" before "recipe"' - if @_recipeData.output.length isnt 1 then throw new Error 'duplicate declaration of "extras"' - - for term in extraTerms - @_recipeData.output.push @_parseStack term - - _command_gatherable: (gatherable)-> - if not @_itemData? then throw new Error 'cannot declare "gatherable" before "item"' - if @_itemData.gatherable? then throw new Error 'duplicate declaration of "gatherable"' - if not (gatherable in ['yes', 'no']) then throw new Error 'gatherable must be either "yes" or "no"' - - @_itemData.gatherable = (gatherable is 'yes') - - _command_group: (group)-> - if group.length is 0 then throw new Error 'a group name cannot be empty' - @_rawData.group = group - - _command_item: (name='')-> - if not name.length > 0 then throw new Error 'the item name cannot be empty' - - @_itemData = name:name, line:@_lineNumber, group:@_rawData.group, type:'new' - @_rawData.items ?= {} - @_rawData.items[name] = @_itemData - - @_recipeData = null - - _command_ignoreDuringCrafting: (value)-> - if not @_recipeData? then throw new Error 'cannot declare "ignoreDuringCrafting" before "recipe"' - if @_recipeData.ignoreDuringCrafting? then throw new Error 'duplicate declaration of "ignoreDuringCrafting"' - if not (value in ['yes', 'no']) then throw new Error 'ignoreDuringCrafting must be either "yes" or "no"' - - @_recipeData.ignoreDuringCrafting = (value is 'yes') - - _command_input: (stackDescriptions...)-> - activeData = if @_recipeData? then @_recipeData else @_multiblockData - - if not activeData? then throw new Error 'cannot declare "input" before "recipe" or "multiblock"' - if activeData.input.length isnt 0 then throw new Error 'duplicate declaration of "input"' - - for stackDescription in stackDescriptions - activeData.input.push @_parseStack stackDescription - - _command_layer: (layerText)-> - if not @_multiblockData? then throw new Error 'cannot declare "layer" before "multiblock"' - if not layerText? then throw new Error 'cannot have an empty layer' - if layerText.length is 0 then throw new Error 'cannot have an empty layer' - - @_multiblockData.layers.push layerText - - _command_multiblock: -> - if not @_itemData? then throw new Error 'cannot declare "multiblock" before "item"' - if @_itemData.multiblockData? then throw new Error 'duplicate declaration of "multiblock"' - - @_recipeData = null - @_multiblockData = input:[], layers:[], line:@_lineNumber - @_itemData.multiblockData = @_multiblockData - - _command_onlyIf: (condition)-> - words = condition.split ' ' - if words.length < 2 then throw new Error 'condition must include a verb followed by a noun' - - inverted = false - if words[0] is 'not' - inverted = true - words.shift() - - verb = words[0] - noun = words[1..].join ' ' - - if not (verb in ['item', 'mod']) then throw new Error "unknown verb: #{verb}" - @_recipeData.condition = verb:verb, noun:noun, inverted:inverted - - _command_pattern: (pattern='')-> - if not @_recipeData? then throw new Error 'cannot declare "pattern" before "recipe"' - if @_recipeData.pattern? then throw new Error 'duplicate declaration of "pattern"' - if not ModVersionParserV1.PATTERN.test pattern - throw new Error 'a pattern must have 9 digits using 0-9 for items and "." for an empty spot; - spaces are optional' - - @_recipeData.pattern = pattern - - _command_quantity: (quantity)-> - if not @_recipeData? then throw new Error 'cannot declare "quantity" before "recipe"' - if @_recipeData.quantity? then throw new Error 'duplicate declaration of "quantity"' - if not ModVersionParserV1.INTEGER.test(quantity) then throw new Error 'quantity must be an integer' - - @_recipeData.quantity = quantity - @_recipeData.output[0].quantity = parseInt quantity - - _command_recipe: -> - if not @_itemData? then throw new Error 'cannot delcare "recipe" before "item"' - - @_multiblockData = null - @_recipeData = line:@_lineNumber, input:[], output:[{quantity:1, name:@_itemData.name}], tools:[] - @_itemData.recipes ?= [] - @_itemData.recipes.push @_recipeData - - _command_tools: (toolNames...)-> - if not @_recipeData? then throw new Error 'cannot declare "tools" before "recipe"' - if @_recipeData.tools.length isnt 0 then throw new Error 'duplicate declaration of "tools"' - - for name in toolNames - if name.length is 0 then throw new Error 'tool names cannot be empty' - @_recipeData.tools.push name:name, quantity:1 - - _command_update: (name='')-> - if not name.length > 0 then throw new 'the item name cannot be empty' - - @_itemData = name:name, line:@_lineNumber, type:'update' - @_recipeData = null - - @_rawData.items ?= {} - @_rawData.items[name] = @_itemData - - # Parsing Helpers ############################################################################## - - _parseStack: (stackText)-> - match = ModVersionParserV1.STACK.exec stackText - if match? - return quantity:parseInt(match[1]), name:match[2] - else - return quantity:1, name:stackText - - # Object Creation Methods ###################################################################### - - _buildModVersion: (modVersionData, modVersion)-> - modVersionData.items ?= [] - - for itemName, itemData of modVersionData.items - @_handleErrors @_buildItem, modVersion, itemData - - modVersion.sort() - return modVersion - - _buildItem: (modVersion, itemData)-> - @_lineNumber = itemData.line - itemData.gatherable ?= false - itemData.ignoreDuringCrafting ?= false - itemData.recipes ?= [] - - if itemData.type is 'new' - item = new Item - name: itemData.name, - ignoreDuringCrafting: itemData.ignoreDuringCrafting, - isGatherable: itemData.gatherable, - group: itemData.group - modVersion.addItem item - itemData.slug = item.slug - else - itemData.slug = ItemSlug.slugify itemData.name - modVersion.registerName itemData.slug, itemData.name - - if itemData.multiblockData - item.multiblock = @_handleErrors @_buildMultiblock, modVersion, item, itemData.multiblockData - - for recipeData in itemData.recipes - @_handleErrors @_buildRecipe, modVersion, itemData, recipeData - - return item - - _buildMultiblock: (modVersion, item, multiblockData)-> - @_lineNumber = multiblockData.line - if multiblockData.layers.length is 0 then throw new Error '"multiblock" requires at least one "layer"' - if multiblockData.input.length is 0 then throw new Error '"multiblock" requires at least one "input"' - - input = @_buildStackList modVersion, multiblockData.input - multiblock = new Multiblock input:input, layers:multiblockData.layers - return multiblock - - _buildRecipe: (modVersion, itemData, recipeData)-> - @_lineNumber = recipeData.line - if recipeData.input.length is 0 then throw new Error 'the "input" declaration is required' - if not recipeData.pattern? then throw new Error 'the "pattern" declaration is required' - - recipe = new Recipe - condition: recipeData.condition - ignoreDuringCrafting: recipeData.ignoreDuringCrafting - input: @_buildStackList modVersion, recipeData.input, recipeData.pattern - output: @_buildStackList modVersion, recipeData.output - pattern: recipeData.pattern - tools: @_buildStackList modVersion, recipeData.tools - - modVersion.addRecipe recipe - return recipe - - _buildStackList: (modVersion, data, pattern=null)-> - createSlug = (name)=> - item = @_rawData.items[name] - if item? and item.type isnt 'update' - slug = new ItemSlug modVersion.modSlug, _.slugify name - else - slug = new ItemSlug name - modVersion.registerName slug, name - return slug - - stacks = [] - for stackData in data - itemSlug = createSlug stackData.name - stacks.push new Stack itemSlug:itemSlug, quantity:stackData.quantity - - if pattern? - expectedIndexes = _.reduce [0...stacks.length], ((obj, i)-> obj[i] = true; return obj), {} - for c in pattern - continue if c is '.' - continue if c is ' ' - delete expectedIndexes[c] - if not stacks[parseInt(c)]? then throw new Error "there is no item #{c} in this recipe" - - unusedNames = _.map(_.keys(expectedIndexes), ((i)-> data[parseInt(i)].name)) - if unusedNames.length > 1 - throw new Error "#{unusedNames.join(', ')} are listed for this recipe, but do not appear in the pattern" - else if unusedNames.length is 1 - throw new Error "#{unusedNames[0]} is listed for this recipe, but does not appear in the pattern" - - return stacks - - # Un-parsing Methods ########################################################################### - - _unparseModVersion: (builder, modVersion)-> - builder - .line 'schema: ', 1 - .line() - - modVersion.eachGroup (group)=> - @_unparseGroup builder, modVersion, group - - externalRecipes = modVersion.findExternalRecipes() - keys = _.keys(externalRecipes).sort() - for itemSlugText in keys - recipeList = externalRecipes[itemSlugText] - - builder - .line 'update: ', modVersion.findName ItemSlug.slugify(itemSlugText) - .indent() - .loop(recipeList, delimiter:'', onEach:(b, r)=> @_unparseRecipe(b, r)) - .outdent() - .line() - - _unparseGroup: (builder, modVersion, group)-> - if group isnt Item.Group.Other - builder - .line 'group: ', group - .line() - .indent() - - modVersion.eachItemInGroup group, (item)=> - @_unparseItem builder, modVersion, item - builder.line() - - if group isnt Item.Group.Other - builder.outdent() - - _unparseItem: (builder, modVersion, item)-> - recipes = modVersion.findRecipes item.slug, [], onlyPrimary:true - - builder - .line 'item: ', item.name - .indent() - .onlyIf item.isGatherable, => builder.line 'gatherable: yes' - .onlyIf item.multiblock?, => - builder - .indent() - .call => @_unparseMultiblock builder, item.multiblock - .outdent() - .onlyIf recipes.length > 0, => - builder.loop recipes, delimiter:'', onEach:(b, r)=> @_unparseRecipe(b, r) - .outdent() - - _unparseMultiblock: (builder, multiblock)-> - builder - .line 'multiblock:' - .indent() - .push "input: " - .call => @_unparseStackList builder, multiblock.input - .push ";\n" - .loop(multiblock.layers, delimiter:'', onEach:(builder, layer)=> builder.line "layer: #{layer}") - .outdent() - - _unparseRecipe: (builder, recipe)-> - inputStacks = recipe.input[..] - inputStacks.sort (a, b)-> Stack.compare a, b - inputNames = [] - for stack in inputStacks - name = builder.context.findName stack.itemSlug - if stack.quantity > 1 - inputNames.push "#{stack.quantity} #{name}" - else - inputNames.push name - - patternMap = {'.', '.'} - for i in [0...recipe.input.length] - stack = recipe.input[i] - name = builder.context.findName(stack.itemSlug) - name = "#{stack.quantity} #{name}" if stack.quantity > 1 - patternMap["#{i}"] = "#{inputNames.indexOf name}" - - pattern = recipe.pattern or recipe.defaultPattern - newPattern = [] - for c in pattern.split('') - newPattern.push patternMap[c] - newPattern = newPattern.join '' - newPattern = newPattern.replace /(...)(...)(...)/, '$1 $2 $3' - - quantity = recipe.output[0].quantity - - extraOutputs = recipe.output[0...recipe.output.length] - extraOutputs.shift() - - builder - .line 'recipe:' - .indent() - .onlyIf recipe.condition?, => - builder.push 'onlyIf: ' - .onlyIf recipe.condition.inverted, -> builder.push 'not ' - .line recipe.condition.verb, ' ', recipe.condition.noun - .onlyIf extraOutputs.length > 0, => - builder - .push 'extras: ' - .call => @_unparseStackList builder, extraOutputs - .line() - .onlyIf recipe.ignoreDuringCrafting, => builder.line 'ignoreDuringCrafting: yes' - .push 'input: ' - .loop inputNames - .line() - .line 'pattern: ', newPattern - .onlyIf quantity > 1, => builder.line 'quantity: ', quantity - .onlyIf recipe.tools.length > 0, => - builder - .push 'tools: ' - .call => @_unparseStackList builder, recipe.tools - .line() - .outdent() - - _unparseStackList: (builder, stackList)-> - if stackList.length is 1 and stackList[0].quantity is 1 - builder.push builder.context.findName(stackList[0].itemSlug) - else - builder.loop stackList, onEach:(b, stack)=> - builder - .onlyIf stack.quantity > 1, => builder.push stack.quantity, ' ' - .push builder.context.findName stack.itemSlug diff --git a/src/client/models-old/parsing/mod_version_parser_v1.test.coffee b/src/client/models-old/parsing/mod_version_parser_v1.test.coffee deleted file mode 100644 index 657f9bf60..000000000 --- a/src/client/models-old/parsing/mod_version_parser_v1.test.coffee +++ /dev/null @@ -1,344 +0,0 @@ -# -# Crafting Guide - mod_version_parser_v1.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CommandParserVersionBase = require './command_parser_version_base' -ItemSlug = require '../game/item_slug' -ModVersion = require '../game/mod_version' -ModVersionParserV1 = require './mod_version_parser_v1' - -######################################################################################################################## - -baseText = modVersion = parser = null - -######################################################################################################################## - -describe 'mod_version_parser_v1.coffee', -> - - beforeEach -> - modVersion = new ModVersion modSlug:'test', version:'0.0' - parser = new ModVersionParserV1 model:modVersion - - describe 'Item', -> - - it 'allows multiple recipes', -> - recipes = "item: Charlie; - recipe:; input:Alpha; pattern:... .0. ...; - recipe:; input:Bravo; pattern:... 0.0 ...;" - modVersion = parser.parse recipes - recipes = modVersion.findRecipes ItemSlug.slugify 'test__charlie' - recipes[0].input[0].itemSlug.qualified.should.equal 'alpha' - recipes[1].input[0].itemSlug.qualified.should.equal 'bravo' - - describe 'name', -> - - it 'adds the name when present', -> - modVersion = parser.parse 'item: Charlie' - modVersion._items.charlie.name.should.equal 'Charlie' - - it 'requires a non-empty name', -> - func = -> parser.parse 'item: \n' - expect(func).to.throw Error, 'cannot be empty' - - describe 'gatherable', -> - - it 'adds "gatherable" when present', -> - modVersion = parser.parse 'item: Alpha Bravo; gatherable: yes' - modVersion._items.alpha_bravo.isGatherable.should.be.true - - it 'does not allow a duplicate "gatherable" declaration', -> - func = -> parser.parse 'item: Alpha Bravo; gatherable: yes; gatherable: yes' - expect(func).to.throw Error, 'duplicate' - - it 'requires "gatherable" to be "yes" or "no"', -> - func = -> parser.parse 'item: Alpha Bravo; gatherable: true' - expect(func).to.throw Error, 'gatherable must be' - - it 'does not allow "gatherable" before "item"', -> - func = -> parser.parse 'gatherable: yes; item: Alpha Bravo; gatherable: yes' - expect(func).to.throw Error, '"gatherable" before "item"' - - describe 'Multiblock', -> - - it 'adds a "multiblock" when present', -> - modVersion = parser.parse 'item: Alpha; multiblock:; input:Bravo; layer: 0' - item = modVersion.findItemByName 'Alpha' - item.multiblock.height.should.equal 1 - - it 'requires "item" be declared before "multiblock"', -> - func = -> parser.parse "multiblock:" - expect(func).to.throw Error, '"multiblock" before "item"' - - it 'prohibits multiple "multiblock" commands per item"', -> - func = -> parser.parse "item: Alpha; multiblock:; multiblock:" - expect(func).to.throw Error, 'duplicate' - - it 'prohibits multiblocks with no inputs', -> - func = -> parser.parse "item: Alpha; multiblock:; layer: 000" - expect(func).to.throw Error, 'at least one "input"' - - describe 'layer', -> - - it 'allows multiple "layer" commands', -> - modVersion = parser.parse 'item: Alpha; multiblock:; input:Bravo, Charlie; layer: 01 10; layer: 10 01' - item = modVersion.findItemByName 'Alpha' - item.multiblock.depth.should.equal 2 - item.multiblock.height.should.equal 2 - item.multiblock.width.should.equal 2 - - it 'prohibits empty layers', -> - func = -> parser.parse 'item: Alpha; multiblock:; input: Bravo; layer:; layer: 00 00' - expect(func).to.throw Error, 'empty layer' - - it 'prohibits multiblocks with no layers', -> - func = -> parser.parse "item: Alpha; multiblock:; input: Bravo" - expect(func).to.throw Error, 'at least one "layer"' - - describe 'Recipe', -> - - beforeEach -> baseText = 'item: Charlie; ' - - describe 'input', -> - - it 'adds "input" when present', -> - modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern: ... 010 ...' - charlieSlug = ItemSlug.slugify('test__charlie') - slugs = (s.itemSlug.item for s in modVersion.findRecipes(charlieSlug)[0].input) - slugs.should.eql ['alpha', 'bravo'] - - it 'requires an "input" declaration', -> - func = -> parser.parse baseText + 'recipe:; pattern: ... .0. ...' - expect(func).to.throw Error, 'the "input" declaration is required' - - it 'does not allow a duplicate "input" declaration', -> - func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:....0....; input:Bravo' - expect(func).to.throw Error, 'duplicate declaration of "input"' - - it 'does not allow "input" before "recipe"', -> - func = -> parser.parse baseText + 'input:Alpha, Bravo; recipe:; pattern:....0....' - expect(func).to.throw Error, 'cannot declare "input" before "recipe"' - - it 'registers slugs for each input name', -> - modVersion = parser.parse baseText + 'recipe:; input:Delta, Echo, Foxtrot; pattern:...012...' - (s.item for s in modVersion._slugs).should.eql ['charlie', 'delta', 'echo', 'foxtrot'] - - it 'correctly handles recipes which use the same input multiple times', -> - modVersion = parser.parse baseText + 'recipe:; input:Alpha; pattern:.0..0....' - recipe = _.values(modVersion._recipes)[0] - recipe.getQuantityRequired(ItemSlug.slugify('alpha')).should.equal 2 - - it 'allows a quantity for each input', -> - modVersion = parser.parse baseText + 'recipe:; input: 12 Delta, 3 Echo; pattern:... 0.1 ...' - recipe = _.values(modVersion._recipes)[0] - recipe.input[0].quantity.should.equal 12 - recipe.input[0].itemSlug.qualified.should.equal 'delta' - recipe.input[1].quantity.should.equal 3 - recipe.input[1].itemSlug.qualified.should.equal 'echo' - - describe 'pattern', -> - - it 'adds "pattern" when present', -> - modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern:... .0. .1.' - modVersion.findRecipes(ItemSlug.slugify('test__charlie'))[0].pattern.should.equal '... .0. .1.' - - it 'requires a "pattern" declaration', -> - func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo' - expect(func).to.throw Error, 'the "pattern" declaration is required' - - it 'does not allow a duplicate "pattern" declaration', -> - func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern:....0..1.; pattern:01.......' - expect(func).to.throw Error, 'duplicate declaration of "pattern"' - - it 'requires pattern to be the right length', -> - func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:000' - expect(func).to.throw Error, 'a pattern must have' - - it 'requires pattern to only use proper characters', -> - func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:abc def ghi' - expect(func).to.throw Error, 'a pattern must have' - - it 'requires pattern to only refer to existing items', -> - func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:... 010 ...' - expect(func).to.throw Error, 'there is no item 1 in this recipe' - - it 'requires all items to appear in the pattern', -> - func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern: 000 0.0 000' - expect(func).to.throw Error, 'Bravo is listed' - - it 'does not allow "pattern" before "recipe"', -> - func = -> parser.parse baseText + 'pattern:... .0. ...; recipe:; inputs:Alpha' - expect(func).to.throw Error, 'cannot declare "pattern" before "recipe"' - - describe 'quantity', -> - - beforeEach -> - baseText = 'item: Charlie; recipe:; input:Alpha; pattern:...0.0...; ' - - it 'adds "quantity" when present', -> - modVersion = parser.parse baseText + 'quantity: 2' - modVersion.findRecipes(ItemSlug.slugify('test__charlie'))[0].output[0].quantity.should.equal 2 - - it 'does not allow a duplicate "quantity" declaration', -> - func = -> parser.parse baseText + 'quantity:1; quantity:2' - expect(func).to.throw Error, 'duplicate declaration of "quantity"' - - it 'requires quantity to be an integer', -> - func = -> parser.parse baseText + 'quantity:ten' - expect(func).to.throw Error, 'quantity must be an integer' - - it 'assumes a quantity of 1 by default', -> - modVersion = parser.parse baseText - modVersion.findRecipes(ItemSlug.slugify('test__charlie'))[0].output[0].quantity.should.equal 1 - - it 'does not allow "quantity" before recipe', -> - func = -> parser.parse 'item:Bravo; quantity:12; recipe:;' - expect(func).to.throw Error, 'cannot declare "quantity" before "recipe"' - - describe 'onlyIf', -> - - beforeEach -> - baseText = 'item: Charlie; recipe:; input:Alpha; pattern:...0.0...; ' - - it 'understands the "item" verb', -> - modVersion = parser.parse baseText + 'onlyIf: item Iron Ingot' - recipes = [] - modVersion.eachRecipe (recipe)-> recipes.push recipe - recipe = recipes[0] - recipe.condition.should.eql verb:'item', noun:'Iron Ingot', inverted:false - - it 'understands the "mod" verb', -> - modVersion = parser.parse baseText + 'onlyIf: mod BuildCraft' - recipes = [] - modVersion.eachRecipe (recipe)-> recipes.push recipe - recipe = recipes[0] - recipe.condition.should.eql verb:'mod', noun:'BuildCraft', inverted:false - - it 'understands inverting verbs', -> - modVersion = parser.parse baseText + 'onlyIf: not item Iron Ingot' - recipes = [] - modVersion.eachRecipe (recipe)-> recipes.push recipe - recipe = recipes[0] - recipe.condition.should.eql verb:'item', noun:'Iron Ingot', inverted:true - - it 'requires at least two words', -> - func = -> parser.parse baseText + 'onlyIf: item' - expect(func).to.throw Error, 'verb followed by a noun' - - it 'only allows known verbs', -> - func = -> parser.parse baseText + 'onlyIf: foo Iron Ingot' - expect(func).to.throw Error, 'unknown verb' - - describe 'output', -> - - beforeEach -> - baseText = 'item: Delta; item:Bravo; recipe:; input:Charlie; pattern:... .0. ...; ' - - it 'adds a single item as the default output', -> - modVersion = parser.parse baseText - stack = modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].output[0] - stack.itemSlug.qualified.should.equal 'test__bravo' - stack.quantity.should.equal 1 - - it 'can add multiple extras with quantities', -> - modVersion = parser.parse baseText + 'extras:2 Delta, 4 Echo' - output = modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].output - output[0].itemSlug.qualified.should.equal 'test__bravo' - output[0].quantity.should.equal 1 - output[1].itemSlug.qualified.should.equal 'test__delta' - output[1].quantity.should.equal 2 - output[2].itemSlug.qualified.should.equal 'echo' - output[2].quantity.should.equal 4 - - it 'does not allow "extras" before "recipe"', -> - func = -> parser.parse 'item:Bravo; extras:Charlie' - expect(func).to.throw Error, 'cannot declare "extras" before "recipe"' - - it 'registers slugs for each output name', -> - modVersion = parser.parse baseText + 'extras:Delta, Echo' - (s.qualified for s in modVersion._slugs).should.eql [ - 'test__bravo', 'charlie', 'test__delta', 'echo' - ] - - it 'does not allow a duplicate "extras" declaration', -> - func = -> parser.parse baseText + 'extras:Echo; extras:Delta' - expect(func).to.throw Error, 'duplicate declaration of "extras"' - - describe 'tools', -> - - beforeEach -> - baseText = 'item:Bravo; recipe:; input:Charlie; pattern:... .0. ...; ' - - it 'can add a single tool', -> - modVersion = parser.parse baseText + 'tools: Furnace' - modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].tools[0].itemSlug.item.should.equal 'furnace' - - it 'can add multiple tools', -> - modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace' - tools = modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].tools - tools[0].itemSlug.item.should.equal 'crafting_table' - tools[1].itemSlug.item.should.equal 'furnace' - - it 'registers slugs for each tool name', -> - modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace' - (s.item for s in modVersion._slugs).should.eql ['bravo', 'charlie', 'crafting_table', 'furnace'] - - it 'does not allow a duplicate "tools" declaration', -> - func = -> parser.parse baseText + 'tools:Crafting Table; tools:Furnace' - expect(func).to.throw Error, 'duplicate declaration of "tools"' - - describe "unparsing", -> - - beforeEach -> - baseText = """ - schema: 1 - - group: Agriculture - - item: Apple - - item: Baked Potato - recipe: - input: furnace fuel, Potato - pattern: .1. ... .0. - tools: Furnace - - item: (filled) Canned Food - recipe: - input: 4 (Empty) Tin Can, Apple - pattern: .1. .0. ... - - item: Pyramid - multiblock: - input: Cobblestone - layer: 000 000 000 - layer: ... .0. ... - - group: Functional Blocks - - item: Furnace - recipe: - input: Cobblestone - pattern: 000 0.0 000 - tools: Crafting Table - - update: Iron Ingot - recipe: - input: furnace fuel, Iron Dust - pattern: .1. ... .0. - tools: Furnace - recipe: - onlyIf: item Redstone Furnace - input: Iron Ore - pattern: ... .0. ... - tools: Redstone Furnace - """ - - it 'can round-trip a data file', -> - text = parser.unparse parser.parse baseText - actual = CommandParserVersionBase.simplify text - expected = CommandParserVersionBase.simplify baseText - - actual.should.equal expected diff --git a/src/client/models-old/parsing/tutorial_parser.coffee b/src/client/models-old/parsing/tutorial_parser.coffee deleted file mode 100644 index 40579a1aa..000000000 --- a/src/client/models-old/parsing/tutorial_parser.coffee +++ /dev/null @@ -1,19 +0,0 @@ -# -# Crafting Guide - tutorial_parser.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -VersionedParserBase = require './versioned_parser_base' -TutorialParserV1 = require './tutorial_parser_v1' - -######################################################################################################################## - -module.exports = class TutorialParser extends VersionedParserBase - - # VersionedParserBase Overrides ################################################################ - - _createParsers: (options)-> - return result = - '1': new TutorialParserV1 options diff --git a/src/client/models-old/parsing/tutorial_parser_v1.coffee b/src/client/models-old/parsing/tutorial_parser_v1.coffee deleted file mode 100644 index a3577ee54..000000000 --- a/src/client/models-old/parsing/tutorial_parser_v1.coffee +++ /dev/null @@ -1,65 +0,0 @@ -# -# Crafting Guide - tutorial_parser_v1.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CommandParserVersionBase = require './command_parser_version_base' -Tutorial = require '../site/tutorial' - -######################################################################################################################## - -module.exports = class TutorialParserV1 extends CommandParserVersionBase - - # CommandParserVersionBase Overrides ########################################################### - - _buildModel: (rawData, model)-> - @_buildTutorial rawData, model - - _unparseModel: (builder, model)-> - @_unparseTutorial builder, model - - # Command Methods ############################################################################## - - _command_content: (contentParts...)-> - if not @_rawData.currentSection? then throw new Error 'cannot declare "title" before "section"' - if @_rawData.currentSection.content? then throw new Error 'duplicate declaration of content' - content = contentParts.join(', ').trim() - if not content.length > 0 then throw new Error 'content cannot be empty' - - @_rawData.currentSection.content = content - - _command_officialUrl: (officialUrl)-> - if @_rawData.officialUrl? then throw new Error 'duplicate declaration of "officialUrl"' - if officialUrl.length is 0 then throw new Error 'officialUrl cannot be empty' - @_rawData.officialUrl = officialUrl - - _command_section: (textParts...)-> - @_rawData.sections ?= [] - @_rawData.sections.push @_rawData.currentSection = {} - - _command_title: (titleParts...)-> - if not @_rawData.currentSection? then throw new Error 'cannot declare "title" before "section"' - if @_rawData.currentSection.title? then throw new Error 'duplicate declaration of title' - title = titleParts.join(', ').trim() - if not title.length > 0 then throw new Error 'title cannot be empty' - - @_rawData.currentSection.title = title - - _command_video: (youTubeId, nameParts...)-> - if not youTubeId?.length then throw new Error 'video declaration requires a YouTubeID' - name = nameParts.join ', ' - if not name?.length then throw new Error 'video declaration requires a name' - - @_rawData.videos ?= [] - @_rawData.videos.push youTubeId:youTubeId, name:name - - # Object Building Methods ###################################################################### - - _buildTutorial: (rawData, model)-> - if not rawData.sections? then throw new Error 'the "section" declaration is required' - - model.officialUrl = rawData.officialUrl - model.videos = rawData.videos - model.sections = rawData.sections diff --git a/src/client/models-old/parsing/versioned_parser_base.coffee b/src/client/models-old/parsing/versioned_parser_base.coffee deleted file mode 100644 index 6642420ad..000000000 --- a/src/client/models-old/parsing/versioned_parser_base.coffee +++ /dev/null @@ -1,51 +0,0 @@ -# -# Crafting Guide - versioned_parser_base.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class VersionedParserBase - - constructor: (options={})-> - @_parsers = @_createParsers options - @_currentSchema = _.chain(@_parsers).keys().last().value() - @errors = [] - - # Class Members ################################################################################ - - @SCHEMA = /schema: *([0-9]+)/ - - # Public Methods ############################################################################### - - parse: (text)-> - return unless text? - - schema = @_identifySchema text - parser = @_parsers[schema] - if not parser? then throw new Error "schema version #{schema} is not supported" - - parser.parse text - @errors = parser.errors - - return @_model - - unparse: (schema=null)-> - schema ?= @_currentSchema - - parser = @_parsers["#{schema}"] - if not parser? then throw new Error "version #{schema} is not supported" - - return parser.unparse() - - # Overridable Methods ########################################################################## - - _createParsers: (options)-> - throw new Error 'subclasses must override this method' - - _identifySchema: (text)-> - match = VersionedParserBase.SCHEMA.exec text - if not match? then throw new Error 'missing "schema" declaration' - return match[1] diff --git a/src/client/models-old/site/tutorial.coffee b/src/client/models-old/site/tutorial.coffee deleted file mode 100644 index 702f0d718..000000000 --- a/src/client/models-old/site/tutorial.coffee +++ /dev/null @@ -1,33 +0,0 @@ -# -# Crafting Guide - tutorial.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -BaseModel = require '../base_model' - -######################################################################################################################## - -module.exports = class Tutorial extends BaseModel - - constructor: (attributes={}, options={})-> - if not attributes.name?.length > 0 then throw new Error "attributes.name cannot be empty" - attributes.modSlug ?= null - attributes.officialUrl ?= null - attributes.sections ?= [] - attributes.slug ?= _.slugify attributes.name - attributes.videos ?= [] - super attributes, options - - # Backbone.Model Overrides ##################################################################### - - parse: (text)-> - TutorialParser = require '../parsing/tutorial_parser' # to avoid require cycles - @_parser ?= new TutorialParser model:this - @_parser.parse text - - return null # prevent calling `set` - - url: -> - return c.url.tutorialData modSlug:@modSlug, tutorialSlug:@slug diff --git a/src/client/models/base_model.coffee b/src/client/models/base_model.coffee deleted file mode 100644 index 8da42e281..000000000 --- a/src/client/models/base_model.coffee +++ /dev/null @@ -1,129 +0,0 @@ -# -# Crafting Guide - base_model.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class BaseModel extends Backbone.Model - - @_loadingQueue = [] - @_isDraining = false - - constructor: (attributes={}, options={})-> - options.logEvents ?= true - super attributes, options - - makeGetter = (name)-> return -> @get name - makeSetter = (name)-> return (value)-> @set name, value - for name, value of attributes - continue if name is 'id' - Object.defineProperty this, name, get:makeGetter(name), set:makeSetter(name) - - @fileCache = options.fileCache or null - @loading = null - @logEvents = options.logEvents or false - @state = c.modelState.unloaded - - Object.defineProperties this, - isUnloaded: { get:-> @state is c.modelState.unloaded } - isLoading: { get:-> @state is c.modelState.loading } - isLoaded: { get:-> @state is c.modelState.loaded } - isError: { get:-> @state is c.modelState.error } - - # Event Methods ################################################################################ - - onLoadSucceeded: (text, status, xhr)-> - try - @set @parse text - - @state = c.modelState.loaded - @trigger c.event.change, this - @trigger c.event.sync, this - logger.info => "#{@constructor.name}.#{@cid} loaded successfully" - catch e - logger.error -> "A parsing error occured: #{e.stack}" - @onLoadFailed e.message, 'parsing failed', xhr - - onLoadFailed: (error, status, xhr)-> - @state = c.modelState.error - logger.error => "#{@constructor.name}.#{@cid} failed to load: status:#{status}, message:#{error}" - @trigger c.event.error, this, error - - # Backbone.Model Overrides ##################################################################### - - fetch: (options={})-> - options.force ?= false - return if (@isLoading or @isLoaded) and not options.force - - url = @url() - logger.info => "#{@constructor.name}.#{@cid} reading from url: #{url}" - - @state = c.modelState.loading - @trigger c.event.request, this - - loadFromServer = => - w.promise (resolve, reject)=> - $.ajax - url: url - dataType: 'text' - success: (text, status, xhr)=> resolve @onLoadSucceeded text, status, xhr - error: (xhr, status, error)=> reject @onLoadFailed error, status, xhr - - if @fileCache? - @loading = @fileCache.loading.then => - if @fileCache.hasFile url - return @_addToLoadingQueue @fileCache.getFile(url), 'success', {url:url} - else - @loading = loadFromServer() - else - @loading = loadFromServer() - - @loading.catch (e)-> # do nothing - return @loading - - parse: (text)-> - return JSON.parse text - - sync: (method, model)-> - throw new Error "#{@constructor.name}.#{@cid} is not permitted to #{method}" - - trigger: (name, model, args...)-> - if @logEvents - argText = ("#{arg}"[0..50] for arg in args).join ", " - logger.trace => "#{@constructor.name}.#{@cid} triggered event #{name} with args: #{argText}" - super - - # Object Overrides ############################################################################# - - toString: -> - return "#{@constructor.name}.#{@cid}" - - # Private Methods ############################################################################## - - _addToLoadingQueue: (text, status, xhr)-> - deferred = w.defer() - - BaseModel._loadingQueue.push resolve:deferred.resolve, func:(=> @onLoadSucceeded text, status, xhr) - @_drainLoadingQueue() - return deferred.promise - - _drainLoadingQueue: -> - return if @_isDraining - @_isDraining = true - - drainDelay = 50 - drain = => - toLoad = BaseModel._loadingQueue.shift() - if not toLoad? - @_isDraining = false - else - toLoad.func() - toLoad.resolve(true) - - _.delay drain, drainDelay - - _.delay drain, drainDelay - diff --git a/src/client/models/crafting/crafting_plan.coffee b/src/client/models/crafting/crafting_plan.coffee deleted file mode 100644 index 283bb073a..000000000 --- a/src/client/models/crafting/crafting_plan.coffee +++ /dev/null @@ -1,144 +0,0 @@ -# -# Crafting Guide - crafting_plan.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Inventory = require "../game/inventory" -{StringBuilder} = require "crafting-guide-common" - -######################################################################################################################## - -module.exports = class CraftingPlan - - constructor: (attributes={})-> - @_id = _.uniqueId "crafting-plan-" - @_make = null - @_need = null - @have = attributes.have - @steps = attributes.steps - @want = attributes.want - - @_computeResources() - @_consolidateSteps() - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - have: # an Inventory specifying what the player already has - get: -> return @_have - set: (have)-> - have ?= new Inventory - if @_have is have then return - if @_have? then throw new Error "have cannot be reassigned" - @_have = new Inventory have - - id: # a string uniquely specifying this crafting plan - get: -> return @_id - set: -> throw new Error "id is not assignabled" - - make: # an Inventory specifying what the results of executing the plan will be - get: -> return @_make - set: -> throw new Error "make cannot be assigned" - - need: # an Inventory specifying what the player will need to gather before executing the plan - get: -> return @_need - set: -> throw new Error "need cannot be assigned" - - want: # an Inventory specifying what the player wants to make - get: -> return @_want - set: (want)-> - if not want? then throw new Error "want is required" - if want.isEmpty then throw new Error "want cannot be empty" - if @_want is want then return - if @_want? then throw new Error "want cannot be reassigned" - @_want = new Inventory want - - steps: # an array of CraftingPlanSteps detailing the steps of the plan - get: -> return @_steps - set: (steps)-> - steps ?= [] - if steps is @_steps then return - if @_steps? then throw new Error "steps cannot be reassigned" - @_steps = steps - - # Object Overrides ############################################################################# - - toString: (options={})-> - options.full ?= false - - if options.full - b = new StringBuilder - b.line "CraftingPlan<", @_id, ">" - b.indent() - b.line "have: ", @_have.toString full:true - b.line "want: ", @_want.toString full:true - b.line "need: ", @_need.toString full:true - b.line "steps:" - b.indent() - b.loop @_steps, delimiter:"\n", onEach:(b, step)-> - b.push step.count, " × ", step.recipe.toString(full:true) - b.line() - b.outdent() - b.line "make: ", @_make.toString full:true - b.outdent() - - return b.toString() - else - return "CraftingPlan:#{@_make.toString(full:true)}<#{@_id}>" - - # Private Methods ############################################################################## - - _computeResources: -> - need = new Inventory @_want - make = new Inventory @_have - steps = @_steps[..].reverse() - - for step in steps - step.count = 0 - - for productStack in step.recipe.allProducts - continue unless need.contains productStack.item - productCount = Math.ceil need.getQuantity(productStack.item) / productStack.quantity - step.count = Math.max step.count, productCount - - continue unless step.count > 0 - - for itemId, item of step.recipe.inputs - amountNeeded = step.count * step.recipe.computeQuantityRequired item - amountAvailable = make.getQuantity item - amountUsed = Math.min amountAvailable, amountNeeded - amountMissing = amountNeeded - amountUsed - - make.remove item, amountUsed - need.add item, amountMissing - - for productStack in step.recipe.allProducts - amountCreated = step.count * productStack.quantity - amountNeeded = need.getQuantity productStack.item - amountFulfilled = Math.min amountNeeded, amountCreated - amountSurplus = amountCreated - amountFulfilled - - need.remove productStack.item, amountFulfilled - make.add productStack.item, amountSurplus - - make.merge @_want - - @_make = make - @_need = need - - _consolidateSteps: -> - steps = [] - stepsByRecipeId = {} - - for step in @_steps - priorStep = stepsByRecipeId[step.recipe.id] - if priorStep? - priorStep.count += step.count - else if step.count > 0 - steps.push step - stepsByRecipeId[step.recipe.id] = step - - @_steps = steps diff --git a/src/client/models/crafting/crafting_plan.test.coffee b/src/client/models/crafting/crafting_plan.test.coffee deleted file mode 100644 index 4011acd40..000000000 --- a/src/client/models/crafting/crafting_plan.test.coffee +++ /dev/null @@ -1,99 +0,0 @@ -# -# Crafting Guide - crafting_plan.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CraftingPlan = require './crafting_plan' -CraftingPlanStep = require './crafting_plan_step' -Inventory = require '../game/inventory' -fixtures = require '../fixtures' - -######################################################################################################################## - -describe "CraftingPlan", -> - - beforeEach -> - @mod = fixtures.createMod() - @want = new Inventory - @have = new Inventory - - describe "with a single step plan", -> - - beforeEach -> - @oakPlank = fixtures.configureOakPlank @mod - @want.add @oakPlank, 11 - - @plan = new CraftingPlan want:@want, steps:[ - new CraftingPlanStep @oakPlank.firstRecipe - ] - - it "correctly computes the step counts", -> - @plan.steps[0].count.should.equal 3 - - it "correctly determines the inputs needed", -> - @plan.need.toString(full:true).should.equal "3 Oak Wood" - - it "correctly computes the products created", -> - @plan.make.toString(full:true).should.equal "12 Oak Planks" - - describe "with a multi-step plan", -> - - beforeEach -> - @ironIngot = fixtures.configureIronIngot @mod - @ironSword = fixtures.configureIronSword @mod - @oakPlank = fixtures.configureOakPlank @mod - @stick = fixtures.configureStick @mod - - @want.add @ironSword, 20 - - @plan = new CraftingPlan want:@want, steps:[ - new CraftingPlanStep @oakPlank.firstRecipe - new CraftingPlanStep @stick.firstRecipe - new CraftingPlanStep @ironIngot.firstRecipe - new CraftingPlanStep @ironSword.firstRecipe - ] - - it "correctly computes the step counts", -> - @plan.steps[0].count.should.equal 3 - @plan.steps[1].count.should.equal 5 - @plan.steps[2].count.should.equal 5 - @plan.steps[3].count.should.equal 20 - - it "correctly determines the inputs needed", -> - @plan.need.toString(full:true).should.equal "5 Coal, 40 Iron Ore, 3 Oak Wood" - - it "correctly computes the products created", -> - @plan.make.toString(full:true).should.equal "20 Iron Sword, 2 Oak Planks" - - describe "with a plan which recycles some items", -> - - beforeEach -> - @bucket = fixtures.configureBucket @mod - @cake = fixtures.configureCake @mod - @ironIngot = fixtures.configureIronIngot @mod - @milkBucket = fixtures.configureMilkBucket @mod - @sugar = fixtures.configureSugar @mod - - @want.add @cake, 2 - - @plan = new CraftingPlan want:@want, steps:[ - new CraftingPlanStep @ironIngot.firstRecipe - new CraftingPlanStep @bucket.firstRecipe - new CraftingPlanStep @milkBucket.firstRecipe - new CraftingPlanStep @sugar.firstRecipe - new CraftingPlanStep @cake.firstRecipe - ] - - it "correctly computes the step counts", -> - @plan.steps.length.should.equal 3 - @plan.steps[0].count.should.equal 6 - @plan.steps[1].count.should.equal 4 - @plan.steps[2].count.should.equal 2 - - it "correctly determines the inputs needed", -> - @plan.need.toString(full:true).should.equal "2 Egg, 6 Milk, 4 Sugar Cane, 6 Wheat" - - it "correctly computes the products created", -> - @plan.make.toString(full:true).should.equal "2 Cake" diff --git a/src/client/models/crafting/crafting_plan_step.coffee b/src/client/models/crafting/crafting_plan_step.coffee deleted file mode 100644 index 2986e2b4e..000000000 --- a/src/client/models/crafting/crafting_plan_step.coffee +++ /dev/null @@ -1,39 +0,0 @@ -# -# Crafting Guide - crafting_plan_step.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class CraftingPlanStep - - constructor: (recipe, count=1)-> - @_id = _.uniqueId "crafting-plan-step-" - @recipe = recipe - @count = count - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - recipe: - get: -> return @_recipe - set: (recipe)-> - if not recipe? then throw new Error 'recipe is required' - if @_recipe is recipe then return - if @_recipe? then throw new Error 'recipe cannot be reassigned' - @_recipe = recipe - - count: - get: -> return @_count - set: (count)-> - count = parseInt "#{count}" - count = if Number.isNaN(count) then 0 else Math.max 0, count - @_count = count - - # Object Overrides ############################################################################# - - toString: -> - return "CraftingPlanStep:#{@_recipe}×#{@_count}<#{@_id}>" diff --git a/src/client/models/crafting/evaluation.coffee b/src/client/models/crafting/evaluation.coffee deleted file mode 100644 index c5ff49a52..000000000 --- a/src/client/models/crafting/evaluation.coffee +++ /dev/null @@ -1,105 +0,0 @@ -# -# Crafting Guide - evaluation.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class Evaluation - - constructor: (attributes={})-> - @evaluator = attributes.evaluator - @item = attributes.item if attributes.item? - @recipe = attributes.recipe if attributes.recipe? - @baseScore = attributes.baseScore - - @_id = _.uniqueId "evaluation-" - @_includedTools = {} - @_toolScore = null - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - baseScore: - get: -> return @_baseScore - set: (baseScore)-> - baseScore = parseFloat "#{baseScore}" - baseScore = if Number.isNaN(baseScore) then null else baseScore - @_baseScore = baseScore - - evaluator: - get: -> return @_evaluator - set: (evaluator)-> - if not evaluator? then throw new Error "evaluator is required" - if @_evaluator is evaluator then return - if @_evaluator? then throw new Error "evaluator cannot be reassigned" - @_evaluator = evaluator - - includedTools: - get: -> return @_includedTools - set: -> throw new Error "includedTools cannot be assigned" - - item: - get: -> return @_item - set: (item)-> - if @_recipe? then throw new Error "this evaluation is for a recipe: cannot set an item" - if @_item is item then return - if not item? then throw new Error "item cannot be assigned null" - if @_item? then throw new Error "item cannot be reassigned" - @_item = item - - recipe: - get: -> return @_recipe - set: (recipe)-> - if @_item? then throw new Error "this evaluation is for an item: cannot set an recipe" - if @_recipe is recipe then return - if not recipe? then throw new Error "recipe cannot be assigned null" - if @_recipe? then throw new Error "recipe cannot be reassigned" - @_recipe = recipe - - toolScore: - get: -> @_computeToolScore() - set: -> throw new Error "toolScore cannot be assigned" - - # Public Methods ############################################################################### - - addIncludedTool: (item)-> - return if @_includedTools[item.id]? - @_includedTools[item.id] = item - @_toolScore = null - - addIncludedToolsFrom: (evaluation)-> - for id, toolItem of evaluation.includedTools - @addIncludedTool toolItem - - computeTotalScore: (quantity=1)-> - if @item? - multiplier = quantity - else if @recipe? - multiplier = Math.ceil 1.0 * quantity / @recipe.output.quantity - - return @baseScore * multiplier + @toolScore - - isToolIncluded: (item)-> - return @_includedTools[item.id]? - - # Object Overrides ############################################################################# - - toString: -> - obj = if @item? then @item else @recipe - return "#{@evaluator.constructor.name}:#{obj}@#{@baseScore}<#{@_id}>" - - # Private Methods ############################################################################## - - _computeToolScore: -> - if not @_toolScore? - @_toolScore = 0 - for id, toolItem of @_includedTools - evaluation = @evaluator.evaluateItem toolItem - continue unless evaluation?.baseScore? - @_toolScore += evaluation.baseScore - - return @_toolScore diff --git a/src/client/models/crafting/evaluator.coffee b/src/client/models/crafting/evaluator.coffee deleted file mode 100644 index 386550090..000000000 --- a/src/client/models/crafting/evaluator.coffee +++ /dev/null @@ -1,102 +0,0 @@ -# -# Crafting Guide - evaluator.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Evaluation = require './evaluation' - -######################################################################################################################## - -module.exports = class Evaluator - - constructor: -> - @_id = _.uniqueId "evaluator-" - @_evaluations = {} - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - id: - get: -> return @_id - set: -> throw new Error "id cannot be assigned" - - # Public Methods ############################################################################### - - evaluateItem: (item)-> - return null unless item? - - evaluation = @_evaluations[item.id] - if not evaluation? - evaluation = @_evaluations[item.id] = new Evaluation evaluator:this, item:item - - recipeEvaluation = @_findBestRecipeEvaluationFor item - if recipeEvaluation? - evaluation.baseScore = recipeEvaluation.baseScore - evaluation.addIncludedToolsFrom recipeEvaluation - else - @_computeGatherableItemScore item, evaluation - - return evaluation - - evaluateRecipe: (recipe)-> - return null unless recipe? - - evaluation = @_evaluations[recipe.id] - if not evaluation? - evaluation = @_evaluations[recipe.id] = new Evaluation evaluator:this, recipe:recipe - @_computeRecipeScore recipe, evaluation - - for id, item of recipe.inputs - inputEvaluation = @evaluateItem item - evaluation.addIncludedToolsFrom inputEvaluation - - for id, toolItem of recipe.tools - evaluation.addIncludedTool toolItem - evaluation.addIncludedToolsFrom @evaluateItem toolItem - - return evaluation - - getOrderedRecipes: (item, quantity=1)-> - recipes = (recipe for recipeId, recipe of item.recipes) - - recipes.sort (a, b)=> - scoreA = @evaluateRecipe(a).computeTotalScore quantity - scoreB = @evaluateRecipe(b).computeTotalScore quantity - - if scoreA isnt scoreB - return if scoreA < scoreB then -1 else +1 - - return 0 - - return recipes - - # Object Overrides ############################################################################# - - toString: -> - return "#{@constructor.name}<#{@id}>" - - # Overrideable Methods ######################################################################### - - _computeGatherableItemScore: (item, evaluation)-> - throw new Error "#{@constructor.name} must override _computeGatherableItemScore" - - _computeRecipeScore: (recipe, evaluation)-> - throw new Error "#{@constructor.name} must override _computeRecipeScore" - - # Private Methods ############################################################################## - - _findBestRecipeEvaluationFor: (item)-> - return null if item.isGatherable - result = null - - for id, recipe of item.recipes - evaluation = @evaluateRecipe recipe - continue unless evaluation?.baseScore? - - if not result? then result = evaluation - if evaluation.baseScore < result.baseScore then result = evaluation - - return result diff --git a/src/client/models/crafting/plan_builder.coffee b/src/client/models/crafting/plan_builder.coffee deleted file mode 100644 index 47ad57257..000000000 --- a/src/client/models/crafting/plan_builder.coffee +++ /dev/null @@ -1,92 +0,0 @@ -# -# Crafting Guide - plan_builder.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -CraftingPlan = require "./crafting_plan" -CraftingPlanStep = require "./crafting_plan_step" - -######################################################################################################################## - -module.exports = class PlanBuilder - - constructor: (evaluator)-> - @evaluator = evaluator - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - evaluator: - get: -> return @_evaluator - set: (evaluator)-> - if not evaluator? then throw new Error "evaluator is required" - if @_evaluator is evaluator then return - if @_evaluator? then throw new Error "evaluator cannot be reassigned" - @_evaluator = evaluator - - # Public Methods ############################################################################### - - createPlan: (want, have)-> - @_alreadyMaking = {} - @_recipesInUse = {} - - stepList = [] - for itemId, stack of want.stacks - steps = @_findStepsForItem stack.item, stack.quantity - return null unless steps? - - stepList.push steps - - plan = new CraftingPlan want:want, have:have, steps:_.flatten(stepList) - return plan - - # Private Methods ############################################################################## - - _findStepsForItem: (item, quantity=1)-> - steps = null - - if not @_alreadyMaking[item.id] - @_alreadyMaking[item.id] = true - - recipes = @_evaluator.getOrderedRecipes item, quantity - if recipes.length is 0 - steps = [] - else - for recipe in recipes - continue if @_recipesInUse[recipe.id]? - - steps = @_findStepsForRecipe recipe, quantity - break if steps? - - delete @_alreadyMaking[item.id] - - return steps - - _findStepsForRecipe: (recipe, quantity=1)-> - steps = null - - if not @_recipesInUse[recipe.id] - @_recipesInUse[recipe.id] = true - - invalidRecipe = false - for itemId, item of recipe.inputs - inputSteps = @_findStepsForItem item, quantity * recipe.computeQuantityRequired(item) - if not inputSteps? - invalidRecipe = true - break - - steps ?= [] - for step in inputSteps - steps.push step - - if invalidRecipe - steps = null - else - steps.push new CraftingPlanStep recipe - - delete @_recipesInUse[recipe.id] - - return steps diff --git a/src/client/models/crafting/plan_builder.test.coffee b/src/client/models/crafting/plan_builder.test.coffee deleted file mode 100644 index e374fc102..000000000 --- a/src/client/models/crafting/plan_builder.test.coffee +++ /dev/null @@ -1,158 +0,0 @@ -# -# Crafting Guide - plan_builder.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -fixtures = require '../fixtures' -Inventory = require '../game/inventory' -PlanBuilder = require './plan_builder' -ResourcesEvaluator = require './resources_evaluator' - -######################################################################################################################## - -describe "PlanBuilder", -> - - beforeEach -> - @planner = new PlanBuilder new ResourcesEvaluator - @mod = fixtures.createMod() - @want = new Inventory - @have = new Inventory - - describe "for a gatherable item, creates a plan that", -> - - beforeEach -> - @oakWood = fixtures.configureOakWood @mod - @want.add @oakWood, 2 - @plan = @planner.createPlan @want - - it "has no steps at all", -> - @plan.steps.length.should.equal 0 - - it "demands the item itself as the input", -> - @plan.need.toString(full:true).should.equal "2 Oak Wood" - - it "produces the item as the result", -> - @plan.make.toString(full:true).should.equal "2 Oak Wood" - - describe "for a single item with a one-step recipe, it creates a plan that", -> - - beforeEach -> - @oakPlank = fixtures.configureOakPlank @mod - @want.add @oakPlank, 63 - @plan = @planner.createPlan @want - - it "has the recipe as the only step", -> - @plan.steps.length.should.equal 1 - @plan.steps[0].recipe.output.item.displayName.should.equal "Oak Planks" - - it "demands the right inputs", -> - @plan.need.toString(full:true).should.equal "16 Oak Wood" - - it "produces the right results", -> - @plan.make.toString(full:true).should.equal "64 Oak Planks" - - describe "for a single item with a complex recipe, it creates a plan that", -> - - beforeEach -> - @ironSword = fixtures.configureIronSword @mod - @want.add @ironSword, 2 - @plan = @planner.createPlan @want - - it "has the right steps", -> - @plan.steps.length.should.equal 4 - @plan.steps[0].recipe.output.item.displayName.should.equal "Iron Ingot" - @plan.steps[1].recipe.output.item.displayName.should.equal "Oak Planks" - @plan.steps[2].recipe.output.item.displayName.should.equal "Stick" - @plan.steps[3].recipe.output.item.displayName.should.equal "Iron Sword" - - it "demands the right inputs", -> - @plan.need.toString(full:true).should.equal "1 Coal, 8 Iron Ore, 1 Oak Wood" - - it "produces the right results", -> - @plan.make.toString(full:true).should.equal "4 Iron Ingot, 2 Iron Sword, 2 Oak Planks, 2 Stick" - - describe "for multiple items with complex recipes, it creates a plan that", -> - - beforeEach -> - @ironSword = fixtures.configureIronSword @mod - @ironShovel = fixtures.configureIronShovel @mod - @want.add @ironSword, 3 - @want.add @ironShovel, 3 - @plan = @planner.createPlan @want - - it "has consolidated the common steps", -> - (step.recipe.output.item.displayName for step in @plan.steps).should.eql [ - "Iron Ingot", "Oak Planks", "Stick", "Iron Sword", "Iron Shovel" - ] - - it "requires the correct repetitions for each step", -> - (step.count for step in @plan.steps).should.eql [ 2, 2, 3, 3, 3 ] - - it "demands the right inputs", -> - @plan.need.toString(full:true).should.equal "2 Coal, 16 Iron Ore, 2 Oak Wood" - - it "produces the right results", -> - @plan.make.toString(full:true).should.equal( - "7 Iron Ingot, 3 Iron Shovel, 3 Iron Sword, 2 Oak Planks, 3 Stick" - ) - - describe "for an item which has a recursive recipe, it creates a plan that", -> - - beforeEach -> - @ironBlock = fixtures.configureIronBlock @mod - @want.add @ironBlock, 4 - @plan = @planner.createPlan @want - - it "avoids the recursive options", -> - (step.recipe.output.item.displayName for step in @plan.steps).should.eql [ - "Iron Ingot", "Iron Block" - ] - - it "requires the correct repetitions for each step", -> - (step.count for step in @plan.steps).should.eql [ 5, 4 ] - - it "demands the right inputs", -> - @plan.need.toString(full:true).should.equal "5 Coal, 40 Iron Ore" - - it "produces the right results", -> - @plan.make.toString(full:true).should.equal( - "4 Iron Block, 4 Iron Ingot" - ) - - describe "when making enormous quantities of something simple", -> - - beforeEach -> - @oakPlank = fixtures.configureOakPlank @mod - @want.add @oakPlank, Number.MAX_VALUE / 2 - @start = Date.now() - @plan = @planner.createPlan @want - @duration = Date.now() - @start - - it "doesn't take forever to create the plan", -> - @duration.should.be.lessThan 10 - - describe "when making an item which can be made with and without a tool, for a", -> - - beforeEach -> - @oakPlank = fixtures.configureOakPlank @mod - @saw = fixtures.configureSaw @mod - - describe "small batch, the plan", -> - - beforeEach -> - @want.add @oakPlank, 2 - @plan = @planner.createPlan @want - - it "avoids the expensive tool", -> - (step.recipe.needsTools for step in @plan.steps).should.eql [ false ] - - describe "large batch, the plan", -> - - beforeEach -> - @want.add @oakPlank, 2048 - @plan = @planner.createPlan @want - - it "uses the tool for efficiency", -> - (step.recipe.needsTools for step in @plan.steps).should.eql [ true ] diff --git a/src/client/models/crafting/resources_evaluator.coffee b/src/client/models/crafting/resources_evaluator.coffee deleted file mode 100644 index 55756cc0d..000000000 --- a/src/client/models/crafting/resources_evaluator.coffee +++ /dev/null @@ -1,40 +0,0 @@ -# -# Crafting Guide - resources_evaluator.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Evaluator = require './evaluator' - -######################################################################################################################## - -module.exports = class ResourcesEvaluator extends Evaluator - - # Evaluator Overrides ########################################################################## - - _computeRecipeScore: (recipe, evaluation)-> - evaluation.baseScore = 0 - - for x in [0...recipe.width] - for y in [0...recipe.height] - for z in [0...recipe.depth] - stack = recipe.getInputAt x, y, z - continue unless stack? - - inputEvaluation = @evaluateItem stack.item - if inputEvaluation?.baseScore? - evaluation.baseScore += inputEvaluation.baseScore * stack.quantity - else - evaluation.baseScore = null - return - - for id, extraStack of recipe.extras - extraEvaluation = @evaluateItem extraStack.item - continue unless extraEvaluation?.baseScore? - evaluation.baseScore -= extraStack.quantity * extraEvaluation.baseScore - - evaluation.baseScore = evaluation.baseScore / recipe.output.quantity - - _computeGatherableItemScore: (item, evaluation)-> - evaluation.baseScore = 1 diff --git a/src/client/models/crafting/resources_evaluator.test.coffee b/src/client/models/crafting/resources_evaluator.test.coffee deleted file mode 100644 index 324a3087c..000000000 --- a/src/client/models/crafting/resources_evaluator.test.coffee +++ /dev/null @@ -1,129 +0,0 @@ -# -# Crafting Guide - resources_evaluator.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -ResourcesEvaluator = require './resources_evaluator' -fixtures = require '../fixtures' - -######################################################################################################################## - -describe "ResourcesEvaluator", -> - - beforeEach -> - @evaluator = new ResourcesEvaluator - @mod = fixtures.createMod() - - describe 'evaluating a gatherable item', -> - - beforeEach -> - @cobblestone = fixtures.configureCobblestone @mod - @evaluation = @evaluator.evaluateItem @cobblestone - - it 'should return a score of 1 for each item', -> - # 1 cobblestone - @evaluation.computeTotalScore().should.equal 1 - - describe 'evaluating a first-level recipe', -> - - beforeEach -> - @craftingTable = fixtures.configureCraftingTable @mod - @evaluation = @evaluator.evaluateItem @craftingTable - - it 'should return a score which is the sum of all resources', -> - # 1 oak wood => 4 planks (1/4) - # 4 oak planks => 1 crafting table (4 * 1/4 = 1) - @evaluation.computeTotalScore().should.equal 1 - - describe 'evaluating a recipe which requires a tool', -> - - beforeEach -> - @furnace = fixtures.configureFurnace @mod - @evaluation = @evaluator.evaluateItem @furnace - - it 'includes the cost of the tool', -> - # 1 oak wood => 4 planks (1/4) - # 4 oak planks => 1 crafting table (4 * 1/4 = 1) - # 8 cobblestone =(crafting table)=> 1 furnace (8 + 1 = 9) - @evaluation.computeTotalScore().should.equal 9 - - describe 'evaluating a recipe where a tool requires a tool', -> - - beforeEach -> - @ironIngot = fixtures.configureIronIngot @mod - @evaluation = @evaluator.evaluateItem @ironIngot - - it 'should include both tools in the score', -> - # raw ingredients: - # 8 iron ore + 1 coal =(furnace)=> 8 iron ingots ((8 + 1) / 8 = 1.125) - # tools: - # 8 cobblestone =(crafting table)=> 1 furnace (8) - # 4 oak planks => 1 crafting table (4 * 1/4 = 1) - # 1 oak wood => 4 planks (1/4) - @evaluation.computeTotalScore().should.equal 10.125 - - describe 'evaluating a recipe which requires a tool multiple times', -> - - beforeEach -> - @ironSword = fixtures.configureIronSword @mod - @evaluation = @evaluator.evaluateItem @ironSword - - it 'should include the tool only once', -> - # raw ingredients: - # 1 oak wood ==> 4 planks (0.25) - # 2 oak planks ==> 4 sticks (0.125) - # 8 iron ore + 1 coal =(furnace)=> 8 iron ingots (1.125) - # 2 iron ingot + 1 stick =(crafting table)=> 1 iron sword (2.375) - # - # tools: - # 8 cobblestone =(crafting table)=> 1 furnace (8) - # - # 1 oak wood => 4 planks (0.25) - # 4 oak planks => 1 crafting table (1) - # - @evaluation.computeTotalScore().should.equal 11.375 - - describe 'evaluating a recipe which has a recursive recipe', -> - - beforeEach -> - @ironBlock = fixtures.configureIronBlock @mod - @evaluation = @evaluator.evaluateItem @ironBlock - - it 'should avoid the recursive recipes', -> - # raw ingredients: - # 8 iron ore + 1 coal =(furnace)=> 8 iron ingots ((8 + 1) / 8 = 1.125) - # 9 iron ingot =(crafting table)=> 1 iron block (10.125) - # - # tools: - # 8 cobblestone =(crafting table)=> 1 furnace (8) - # 4 oak planks => 1 crafting table (4 * 1/4 = 1) - # 1 oak wood => 4 planks (1/4) - @evaluation.computeTotalScore().should.equal 19.125 - - describe 'evaluating a recipe with multiple ouputs', -> - - beforeEach -> - @cake = fixtures.configureCake @mod - @evaluation = @evaluator.evaluateItem @cake - - it 'should discount the extra outputs', -> - # raw ingredients: - # 8 iron ore + 1 coal =(furnace)=> 8 iron ingot (1.125) - # 3 iron ingot =(crafting table)=> 1 bucket (3.375) - # 1 milk + 1 bucket ==> 1 milk bucket (4.375) - # 1 sugar cane ==> 1 sugar (1) - # 3 wheat + 1 egg + 2 sugar + 3 milk bucket =(crafting table)=> 1 cake, 3 buckets (9) - # - # furnace: - # 8 cobblestone =(crafting table)=> 1 furnace (8) - # - # crafting table - # 1 oak wood ==> 4 oak planks (0.25) - # 4 planks ==> 1 crafting table (1) - @evaluation.computeTotalScore().should.equal 18 - - describe 'evaluating a recipe which is only ever made as an extra', -> - - it 'should still be able to come up with an evaluation' diff --git a/src/client/models/crafting/steps_evaluator.coffee b/src/client/models/crafting/steps_evaluator.coffee deleted file mode 100644 index 41472ce73..000000000 --- a/src/client/models/crafting/steps_evaluator.coffee +++ /dev/null @@ -1,41 +0,0 @@ -# -# Crafting Guide - steps_evaluator.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Evaluator = require './evaluator' - -######################################################################################################################## - -module.exports = class StepsEvaluator extends Evaluator - - # Evaluator Overrides ########################################################################## - - _computeRecipeScore: (recipe, evaluation)-> - evaluation.score = 0 - - for id, stack of recipe.inputs - inputEvaluation = @evaluateItem stack.item - if not inputEvaluation?.score? - evaluation.score = null - return - - evaluation.score = Math.min evaluation.score, inputEvaluation.score + 1 - - for id, item of recipe.tools - continue if evaluation.isToolIncluded item - - toolEvaluation = @evaluateItem stack.item - if not toolEvaluation?.score? - evaluation.score = null - return - - evaluation.addIncludedTool item - evaluation.score += toolEvaluation.score - - return result - - _computeGatherableItemScore: (item, evaluation)-> - evaluation.score = 0 diff --git a/src/client/models/event_recorder.coffee b/src/client/models/event_recorder.coffee deleted file mode 100644 index 507ed32cd..000000000 --- a/src/client/models/event_recorder.coffee +++ /dev/null @@ -1,36 +0,0 @@ -# -# Crafting Guide - event_recorder.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -util = require 'util' - -######################################################################################################################## - -module.exports = class EventRecorder - - constructor: (model)-> - if not model? then throw new Error 'model is required' - - @model = model - @events = [] - - @model.on 'all', (event, model, args...)=> - logger.verbose -> "#{model?.constructor?.name}(#{model?.cid}) emitted #{event} - with args: #{util.inspect(args)}" - @events.push id:model?.cid, event:event, args:args - - # Public Methods ############################################################################### - - reset: -> - @events = [] - - # Property Methods ############################################################################# - - getNames: -> - return (e.event for e in @events) - - Object.defineProperties @prototype, - names: {get:@prototype.getNames} diff --git a/src/client/models/fixtures.coffee b/src/client/models/fixtures.coffee deleted file mode 100644 index ebc346a09..000000000 --- a/src/client/models/fixtures.coffee +++ /dev/null @@ -1,319 +0,0 @@ -# -# Crafting Guide - fixtures.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Item = require "./game/item" -Mod = require "./game/mod" -ModPack = require "./game/mod_pack" -Recipe = require "./game/recipe" -Stack = require "./game/stack" - -# Instance Creation Fixtures ########################################################################################### - -exports.createModPack = createModPack = (attributes={})-> - attributes.id ?= _.uniqueId "mod-pack-" - attributes.displayName ?= "Test ModPack" - return new ModPack attributes - -exports.createMod = createMod = (attributes={})-> - attributes.modPack ?= createModPack() - attributes.id ?= _.uniqueId "mod-" - attributes.displayName ?= "Test Mod" - return new Mod attributes - -exports.createItem = createItem = (attributes={})-> - attributes.mod ?= createMod() - attributes.id ?= _.uniqueId "item-" - attributes.displayName ?= _.uniqueId "Test Item " - return new Item attributes - -exports.createRecipe = createRecipe = (attributes={})-> - attributes.id ?= _.uniqueId "recipe-" - attributes.output ?= createItem() - return new Recipe attributes - -exports.createStack = createStack = (attributes={})-> - attributes.item ?= createItem() - attributes.quantity ?= 1 - return new Stack attributes - -# Item Configuration Fixtures ########################################################################################## - -exports.configureBucket = configureBucket = (mod)-> - bucket = mod.modPack.findItem "bucket" - if not bucket? - ironIngot = configureIronIngot mod - bucket = createItem mod:mod, id:"bucket", displayName:"Bucket" - craftingTable = configureCraftingTable mod - - recipe = createRecipe output:createStack item:bucket - recipe.setInputAt 0, 0, createStack item:ironIngot - recipe.setInputAt 1, 1, createStack item:ironIngot - recipe.setInputAt 2, 0, createStack item:ironIngot - recipe.addTool craftingTable - - return bucket - -exports.configureCake = configureCake = (mod)-> - cake = mod.modPack.findItem "cake" - if not cake? - bucket = configureBucket mod - cake = createItem mod:mod, id:"cake", displayName:"Cake" - craftingTable = configureCraftingTable mod - egg = configureEgg mod - milkBucket = configureMilkBucket mod - sugar = configureSugar mod - wheat = configureWheat mod - - recipe = createRecipe output:createStack item:cake - recipe.setInputAt 0, 0, createStack item:milkBucket - recipe.setInputAt 1, 0, createStack item:milkBucket - recipe.setInputAt 2, 0, createStack item:milkBucket - recipe.setInputAt 0, 1, createStack item:sugar - recipe.setInputAt 1, 1, createStack item:egg - recipe.setInputAt 2, 1, createStack item:sugar - recipe.setInputAt 0, 2, createStack item:wheat - recipe.setInputAt 1, 2, createStack item:wheat - recipe.setInputAt 2, 2, createStack item:wheat - recipe.addTool craftingTable - recipe.addExtra createStack item:bucket, quantity:3 - - return cake - -exports.configureCoal = configureCoal = (mod)-> - coal = mod.modPack.findItem "coal" - if not coal? - coal = createItem mod:mod, id:"coal", displayName:"Coal" - return coal - -exports.configureCobblestone = configureCobblestone = (mod)-> - cobblestone = mod.modPack.findItem "cobblestone" - if not cobblestone? - cobblestone = createItem mod:mod, displayName:"Cobblestone" - return cobblestone - -exports.configureCraftingTable = configureCraftingTable = (mod)-> - craftingTable = mod.modPack.findItem "crafting_table" - if not craftingTable? - craftingTable = createItem mod:mod, id:"crafting_table", displayName:"Crafting Table" - oakPlanks = configureOakPlank mod - - recipe = createRecipe output:createStack item:craftingTable - recipe.setInputAt 0, 0, createStack item:oakPlanks - recipe.setInputAt 1, 0, createStack item:oakPlanks - recipe.setInputAt 0, 1, createStack item:oakPlanks - recipe.setInputAt 1, 1, createStack item:oakPlanks - - return craftingTable - -exports.configureEgg = configureEgg = (mod)-> - egg = mod.modPack.findItem "egg" - if not egg? - egg = createItem mod:mod, id:"egg", displayName:"Egg" - return egg - -exports.configureFurnace = configureFurnace = (mod)-> - furnace = mod.modPack.findItem "furnace" - if not furnace? - cobblestone = configureCobblestone mod - craftingTable = configureCraftingTable mod - furnace = createItem mod:mod, displayName:"Furnace" - - recipe = createRecipe output:createStack item:furnace - recipe.setInputAt 0, 0, createStack item:cobblestone - recipe.setInputAt 1, 0, createStack item:cobblestone - recipe.setInputAt 2, 0, createStack item:cobblestone - recipe.setInputAt 0, 1, createStack item:cobblestone - recipe.setInputAt 2, 1, createStack item:cobblestone - recipe.setInputAt 0, 2, createStack item:cobblestone - recipe.setInputAt 1, 2, createStack item:cobblestone - recipe.setInputAt 2, 2, createStack item:cobblestone - recipe.addTool craftingTable - - return furnace - -exports.configureIronIngot = configureIronIngot = (mod)-> - ironIngot = mod.modPack.findItem "iron_ingot" - if not ironIngot? - coal = configureCoal mod - furnace = configureFurnace mod - ironIngot = createItem mod:mod, id:"iron_ingot", displayName:"Iron Ingot" - ironOre = configureIronOre mod - - recipe = createRecipe output:createStack item:ironIngot, quantity:8 - recipe.setInputAt 1, 0, createStack item:ironOre, quantity:8 - recipe.setInputAt 1, 2, createStack item:coal - recipe.addTool furnace - - return ironIngot - -exports.configureIronBlock = configureIronBlock = (mod)-> - ironBlock = mod.modPack.findItem "iron_block" - if not ironBlock? - craftingTable = configureCraftingTable mod - ironBlock = createItem mod:mod, id:"iron_block", displayName:"Iron Block" - ironIngot = configureIronIngot mod - - recipe = createRecipe output:createStack item:ironBlock - for x in [0..2] - for y in [0..2] - recipe.setInputAt x, y, createStack item:ironIngot - recipe.addTool craftingTable - - recipe = createRecipe output:createStack item:ironIngot, quantity:9 - recipe.setInputAt 1, 1, createStack item:ironBlock - - return ironBlock - -exports.configureIronSword = configureIronSword = (mod)-> - ironSword = mod.modPack.findItem "iron_sword" - if not ironSword? - craftingTable = configureCraftingTable mod - ironIngot = configureIronIngot mod - ironSword = createItem mod:mod, id:"iron_sword", displayName:"Iron Sword" - stick = configureStick mod - - recipe = createRecipe output:createStack item:ironSword - recipe.setInputAt 1, 0, createStack item:ironIngot - recipe.setInputAt 1, 1, createStack item:ironIngot - recipe.setInputAt 1, 2, createStack item:stick - recipe.addTool craftingTable - - return ironSword - -exports.configureIronShovel = configureIronShovel = (mod)-> - ironShovel = mod.modPack.findItem "iron_shovel" - if not ironShovel? - craftingTable = configureCraftingTable mod - ironIngot = configureIronIngot mod - ironShovel = createItem mod:mod, id:"iron_shovel", displayName:"Iron Shovel" - stick = configureStick mod - - recipe = createRecipe output:createStack item:ironShovel - recipe.setInputAt 1, 0, createStack item:ironIngot - recipe.setInputAt 1, 1, createStack item:stick - recipe.setInputAt 1, 2, createStack item:stick - recipe.addTool craftingTable - - return ironShovel - -exports.configureIronOre = configureIronOre = (mod)-> - ironOre = mod.modPack.findItem "iron_ore" - if not ironOre? - ironOre = createItem mod:mod, id:"iron_ore", displayName:"Iron Ore" - return ironOre - -exports.configureMilk = configureMilk = (mod)-> - milk = mod.modPack.findItem "milk" - if not milk? - milk = createItem mod:mod, id:"milk", displayName:"Milk" - return milk - -exports.configureMilkBucket = configureMilkBucket = (mod)-> - milkBucket = mod.modPack.findItem "milk_bucket" - if not milkBucket? - bucket = configureBucket mod - milk = configureMilk mod - milkBucket = createItem mod:mod, id:"milkBucket", displayName:"Milk Bucket" - - recipe = createRecipe output:createStack item:milkBucket - recipe.setInputAt 1, 0, createStack item:milk - recipe.setInputAt 1, 1, createStack item:bucket - - return milkBucket - -exports.configureOakPlank = configureOakPlank = (mod)-> - oakPlanks = mod.modPack.findItem "oak_planks" - if not oakPlanks? - oakPlanks = createItem mod:mod, id:"oak_planks", displayName:"Oak Planks" - oakWood = configureOakWood mod - - recipe = createRecipe output:createStack item:oakPlanks, quantity:4 - recipe.setInputAt 1, 1, createStack item:oakWood - - return oakPlanks - -exports.configureOakWood = configureOakWood = (mod)-> - oakWood = mod.modPack.findItem "oak_wood" - if not oakWood? - oakWood = createItem mod:mod, id:"oak_wood", displayName:"Oak Wood" - return oakWood - -exports.configureObsidian = configureObsidian = (mod)-> - obsidian = mod.modPack.findItem "obsidian" - if not obsidian? - obsidian = createItem mod:mod, id:"obsidian", displayName:"Obsidian" - return obsidian - -exports.configureRedstoneDust = configureRedstoneDust = (mod)-> - redstoneDust = mod.modPack.findItem "redstone_dust" - if not redstoneDust? - redstoneDust = createItem mod:mod, id:"redstone_dust", displayName:"Redstone Dust" - return redstoneDust - -exports.configureStick = configureStick = (mod)-> - stick = mod.modPack.findItem "stick" - if not stick? - oakPlanks = configureOakPlank mod - stick = createItem mod:mod, id:"stick", displayName:"Stick" - - recipe = createRecipe output:createStack item:stick, quantity:4 - recipe.setInputAt 0, 0, createStack item:oakPlanks - recipe.setInputAt 0, 1, createStack item:oakPlanks - - return stick - -exports.configureSugar = configureSugar = (mod)-> - sugar = mod.modPack.findItem "sugar" - if not sugar? - sugar = createItem mod:mod, id:"sugar", displayName:"Sugar" - sugarCane = configureSugarCane mod - - recipe = createRecipe output:createStack item:sugar - recipe.setInputAt 1, 1, createStack item:sugarCane - - return sugar - -exports.configureSaw = configureSaw = (mod)-> - saw = mod.modPack.findItem "saw" - if not saw? - craftingTable = configureCraftingTable mod - ironBlock = configureIronBlock mod - ironIngot = configureIronIngot mod - oakPlank = configureOakPlank mod - oakWood = configureOakWood mod - redstoneDust = configureRedstoneDust mod - saw = createItem mod:mod, id:"saw", displayName:"Saw" - - recipe = createRecipe output:createStack item:saw - recipe.setInputAt 0, 0, createStack item:oakPlank - recipe.setInputAt 1, 0, createStack item:ironIngot - recipe.setInputAt 2, 0, createStack item:oakPlank - recipe.setInputAt 0, 1, createStack item:oakPlank - recipe.setInputAt 1, 1, createStack item:ironBlock - recipe.setInputAt 2, 1, createStack item:oakPlank - recipe.setInputAt 0, 2, createStack item:oakPlank - recipe.setInputAt 1, 2, createStack item:redstoneDust - recipe.setInputAt 2, 2, createStack item:oakPlank - recipe.addTool craftingTable - - recipe = createRecipe output:createStack item:oakPlank, quantity:8 - recipe.setInputAt 1, 1, createStack item:oakWood - recipe.addTool saw - - return saw - -exports.configureSugarCane = configureSugarCane = (mod)-> - sugarCane = mod.modPack.findItem "sugar_cane" - if not sugarCane? - sugarCane = createItem mod:mod, id:"sugar_cane", displayName:"Sugar Cane" - return sugarCane - -exports.configureWheat = configureWheat = (mod)-> - wheat = mod.modPack.findItem "wheat" - if not wheat? - wheat = createItem mod:mod, id:"wheat", displayName:"Wheat" - return wheat diff --git a/src/client/models/game/inventory.coffee b/src/client/models/game/inventory.coffee deleted file mode 100644 index 4eadaa7ba..000000000 --- a/src/client/models/game/inventory.coffee +++ /dev/null @@ -1,90 +0,0 @@ -# -# Crafting Guide - inventory.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Stack = require './stack' - -######################################################################################################################## - -module.exports = class Inventory - - constructor: (inventory=null)-> - @_id = _.uniqueId "inventory-" - @_stacks = {} - - if inventory? then @merge inventory - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - isEmpty: - get: -> (id for id, stack of @_stacks).length is 0 - - stacks: - get: -> return @_stacks - set: -> throw new Error "stacks cannot be replaced" - - # Public Methods ############################################################################### - - add: (item, quantity)-> - return unless item? - return if quantity is 0 - - existingStack = @_stacks[item.id] - if existingStack? - if existingStack.quantity + quantity < 0 then throw new Error "cannot have a negative quantity" - existingStack.quantity += quantity - else - if quantity < 0 then throw new Error "cannot have a negative quantity" - @_stacks[item.id] = new Stack item:item, quantity:quantity - - if @_stacks[item.id].quantity is 0 - delete @_stacks[item.id] - - clear: -> - @_stacks = {} - - contains: (item)-> - return @_stacks[item.id]? - - getQuantity: (item)-> - existingStack = @_stacks[item.id] - return 0 unless existingStack? - return existingStack.quantity - - merge: (inventory)-> - for id, stack of inventory.stacks - @add stack.item, stack.quantity - - remove: (item, quantity)-> - @add item, -1 * quantity - - # Object Overrides ############################################################################# - - toString: (options={})-> - options.full ?= false - - if options.full - result = [] - needsDelimiter = false - - stackList = (stack for itemId, stack of @_stacks) - stackList.sort (a, b)-> - if a.item.displayName isnt b.item.displayName - return if a.item.displayName < b.item.displayName then -1 else +1 - return 0 - - for stack in stackList - if needsDelimiter then result.push ", " - needsDelimiter = true - - result.push stack.quantity - result.push " " - result.push stack.item.displayName - return result.join "" - else - return "Inventory<#{@_id}>@#{(id for id, item of @_stacks).length}" diff --git a/src/client/models/game/item.coffee b/src/client/models/game/item.coffee deleted file mode 100644 index 2e0a4796e..000000000 --- a/src/client/models/game/item.coffee +++ /dev/null @@ -1,96 +0,0 @@ -# -# Crafting Guide - item.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class Item - - constructor: (attributes={})-> - @id = attributes.id - @displayName = attributes.displayName - @isGatherable = attributes.isGatherable - @mod = attributes.mod - - @_hasPrimaryRecipe = false - @_recipesAsPrimary = {} - @_recipesAsExtra = {} - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - displayName: # a string containing the user-facing name of this item - get: -> return @_displayName - set: (displayName)-> - if not displayName? then throw new Error "displayName is required" - @_displayName = displayName - - firstRecipe: # the first Recipe returned by iterating the `recipes` property - get: -> - recipeList = (recipe for id, recipe of @recipes) - return null unless recipeList.length > 0 - return recipeList[0] - set: -> throw new Error "firstRecipe cannot be assigned" - - id: # a string containing a unique identifier for this item - get: -> return @_id - set: (id)-> - if not id? then throw new Error "id is required" - return if @_id is id - if @_id? then throw new Error "id cannot be reassigned" - @_id = id - - isGatherable: # whether this item can be gathered directly without needing to be crafted - get: -> - return true if @_isGatherable is true - return false if (id for id, recipe of @_recipesAsPrimary).length > 0 - return false if (id for id, recipe of @_recipesAsExtra).length > 0 - return true - set: (isGatherable)-> - @_isGatherable = null unless isGatherable? - @_isGatherable = !!isGatherable - - mod: # the Mod which adds this item to the game - get: -> return @_mod - set: (mod)-> - if not mod? then throw new Error "mod is required" - if @_mod is mod then return - if @_mod? then throw new Error "mod cannot be reassigned" - @_mod = mod - @_mod.addItem this - - modPack: # the ModPack containing this item - get: -> return @_mod.modPack - set: -> throw new Error "modPack cannot be assigned" - - recipes: # a hash of recipeId to Recipe containing `recipesAsPrimary` if not empty or else `recipesAsExtra` - get: -> return if @_hasPrimaryRecipe then @_recipesAsPrimary else @_recipesAsExtra - set: -> throw new Error "recipes cannot be assigned" - - recipesAsPrimary: # a hash of recipeId to Recipe where this item is the primary output - get: -> return @_recipesAsPrimary - set: -> throw new Error "recipes cannot be assigned" - - recipesAsExtra: # a hash of recipeId to Recipe where this item is an extra output - get: -> return @_recipesAsExtra - set: -> throw new Error "recipesAsExtra cannot be assigned" - - # Public Recipes ############################################################################### - - addRecipe: (recipe)-> - if recipe.output.item is this - @_recipesAsPrimary[recipe.id] = recipe - @_hasPrimaryRecipe = true - else if recipe.extras[this.id]?.item is this - @_recipesAsExtra[recipe.id] = recipe - else - throw new Error "recipe<#{recipe.id}> does not produce this item<#{@id}>" - - # Object Overrides ############################################################################# - - toString: -> - return "Item:#{@displayName}<#{@id}>" diff --git a/src/client/models/game/mod.coffee b/src/client/models/game/mod.coffee deleted file mode 100644 index b4e013b86..000000000 --- a/src/client/models/game/mod.coffee +++ /dev/null @@ -1,62 +0,0 @@ -# -# Crafting Guide - mod.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class Mod - - constructor: (attributes={})-> - @id = attributes.id - @displayName = attributes.displayName - @modPack = attributes.modPack - - @_items = {} - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - displayName: - get: -> return @_displayName - set: (displayName)-> - if not displayName? then throw new Error "displayName is required" - return if @_displayName is displayName - @_displayName = displayName - - id: - get: -> return @_id - set: (id)-> - if not id? then throw new Error "id is required" - return if @_id is id - if @_id? then throw new Error "id cannot be reassigned" - @_id = id - - items: - get: -> return @_items - set: -> throw new Error "items cannot be replaced" - - modPack: - get: -> return @_modPack - set: (modPack)-> - if not modPack? then throw new Error "modPack is required" - if @_modPack is modPack then return - if @_modPack? then throw new Error "modPack cannot be reassigned" - @_modPack = modPack - @_modPack.addMod this - - # Public Methods ############################################################################### - - addItem: (item)-> - if not item? then return - if @_items[item.id] is item then return - @_items[item.id] = item - item.mod = this - - # Object Overrides ############################################################################# - - toString: -> - return "Mod:#{@displayName}<#{@id}>" diff --git a/src/client/models/game/mod_pack.coffee b/src/client/models/game/mod_pack.coffee deleted file mode 100644 index f7e3842a9..000000000 --- a/src/client/models/game/mod_pack.coffee +++ /dev/null @@ -1,59 +0,0 @@ -# -# Crafting Guide - mod_pack.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class ModPack - - constructor: (attributes={})-> - @id = attributes.id - @displayName = attributes.displayName - - @_mods = {} - - # Property Methods ############################################################################# - - Object.defineProperties @prototype, - - displayName: # a string containing the user-displayable name of this ModPack - get: -> return @_displayName - set: (displayName)-> - if not displayName? then throw new Error "displayName is required" - if @_displayName is displayName then return - @_displayName = displayName - - id: # a string which uniquely identifies this ModPack - get: -> return @_id - set: (id)-> - if not id? then throw new Error "id is required" - if @_id is id then return - if @_id? then throw new Error "id cannot be reassigned" - @_id = id - - mods: # a hash of mod id to Mod containing all the mods which are part of this ModPack - get: -> return @_mods - set: -> throw new Error "mods cannot be replaced" - - # Public Methods ############################################################################### - - addMod: (mod)-> - if not mod? then return - if @_mods[mod.id] is mod then return - @_mods[mod.id] = mod - mod.modPack = this - - findItem: (itemId)-> - for modId, mod of @mods - item = mod.items[itemId] - return item if item? - - return null - - # Object Overrides ############################################################################# - - toString: -> - return "ModPack:#{@displayName}<#{@id}>" diff --git a/src/client/models/game/recipe.coffee b/src/client/models/game/recipe.coffee deleted file mode 100644 index 8cb220542..000000000 --- a/src/client/models/game/recipe.coffee +++ /dev/null @@ -1,174 +0,0 @@ -# -# Crafting Guide - recipe.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -{StringBuilder} = require 'crafting-guide-common' - -######################################################################################################################## - -module.exports = class Recipe - - constructor: (attributes={})-> - @depth = attributes.depth - @height = attributes.height - @id = attributes.id - @output = attributes.output - @width = attributes.width - - @_extras = {} - @_inputs = {} - @_inputGrid = [] - @_tools = {} - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - allProducts: # an array of Stacks starting the the primary output of this recipe - get: -> return [].concat @output, (stack for id, stack of @extras) - set: -> throw new Error "allProducts cannot be assigned" - - depth: # an integer specifying the number of layers to this recipe - get: -> return @_depth - set: (depth)-> - depth = parseInt "#{depth}" - depth = if Number.isNaN(depth) then 0 else Math.max(0, depth) - @_depth = depth - - extras: # a hash of item id to Stack of all the non-primary outputs of this recipe - get: -> return @_extras - set: -> throw new Error "extras cannot be replaced" - - id: # a string which uniquely identifies this recipe - get: -> return @_id - set: (id)-> - if not id? then throw new Error "id is required" - if @_id is id then return - if @_id? then throw new Error "id cannot be reassigned" - @_id = id - - height: # an integer specifying the number of rows needed by this recipe - get: -> return @_height - set: (height)-> - height = parseInt "#{height}" - height = if Number.isNaN(height) then 0 else Math.max(0, height) - @_height = height - - inputs: # a hash of item id to Item containing all the inputs to this recipe - get: -> return @_inputs - set: -> throw new Error "inputs cannot be replaced" - - needsTools: # a boolean indicating whether this recipe requires a tool - get: -> return (id for id, toolItem of @_tools).length > 0 - - output: # a Stack specifying the primary output of this recipe - get: -> return @_output - set: (output)-> - if not output? then throw new Error "output is required" - if @_output is output then return - if @_output? then throw new Error "output cannot be reassigned" - @_output = output - @_output.item.addRecipe this - - modPack: # the ModPack to which this recipe belongs - get: -> return @_output.modPack - set: -> throw new Error "modPack cannot be replaced" - - tools: # a hash of item id to Item of all the tools required for this recipe - get: -> return @_tools - set: -> throw new Error "tools cannot be assigned" - - width: # an integer specifying the number of columns needed by this recipe - get: -> return @_width - set: (width)-> - width = parseInt "#{width}" - width = if Number.isNaN(width) then 0 else Math.max(0, width) - @_width = width - - # Public Methods ############################################################################### - - addExtra: (stack)-> - return unless stack - return if @_extras[stack.item.id] is stack - @_extras[stack.item.id] = stack - stack.item.addRecipe this - - addTool: (item)-> - return unless item - @_tools[item.id] = item - - computeQuantityRequired: (item)-> - result = 0 - - for x in [0...@width] - for y in [0...@height] - for z in [0...@depth] - stack = @getInputAt x, y, z - continue unless stack? - continue unless stack.item.id is item.id - result += stack.quantity - - return result - - computeQuantityProduced: (item)-> - result = 0 - - if @_output.item.id is item.id - result += @_output.quantity - - for itemId, stack of @_extras - continue unless itemId is item.id - result += stack.quantity - - return result - - getInputAt: -> - [x, y, z] = [0, 0, 0] - if arguments.length is 3 - [x, y, z] = arguments - else - [x, y] = arguments - - return @_inputGrid[x]?[y]?[z] or null - - setInputAt: -> - [x, y, z, stack] = [0, 0, 0, null] - if arguments.length is 4 - [x, y, z, stack] = arguments - else - [x, y, stack] = arguments - - @_depth = Math.max @_depth, z + 1 - @_height = Math.max @_height, y + 1 - @_width = Math.max @_width, x + 1 - - @_inputGrid[x] ?= [] - @_inputGrid[x][y] ?= [] - @_inputGrid[x][y][z] = stack - - if stack?.item? then @_inputs[stack.item.id] = stack.item - - # Object Overrides ############################################################################# - - toString: (options={})-> - options.full ?= false - - if options.full - b = new StringBuilder - b.loop (item for id, item of @inputs), delimiter:" + ", onEach:(b, item)=> - b.push @computeQuantityRequired(item), " ", item.displayName - b.push " =" - b.onlyIf @needsTools, (b)=> - b.push "(" - b.loop (toolItem for id, toolItem of @tools), onEach:(b, toolItem)-> b.push toolItem.displayName - b.push ")" - b.push "=> " - b.loop @allProducts, delimiter:" + ", onEach:(b, stack)=> - b.push stack.quantity, " ", stack.item.displayName - - return b.toString() - else - return "Recipe:#{@output}<#{@id}>" diff --git a/src/client/models/game/recipe.test.coffee b/src/client/models/game/recipe.test.coffee deleted file mode 100644 index ef9bffda0..000000000 --- a/src/client/models/game/recipe.test.coffee +++ /dev/null @@ -1,72 +0,0 @@ -# -# Crafting Guide - recipe.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -fixtures = require "../fixtures" -Item = require "./item" -Recipe = require "./recipe" -Stack = require "./stack" - -######################################################################################################################## - -describe "Recipe", -> - - beforeEach -> - @mod = fixtures.createMod() - @stick = fixtures.configureStick @mod - @ironIngot = fixtures.configureIronIngot @mod - @obsidian = fixtures.configureObsidian @mod - - @ironSword = new Item id:"iron_sword", displayName:"Iron Sword", mod:@mod - @obsidianBox = new Item id:"obsidian_box", displayName:"Obsidian Box", mod:@mod - - describe "getting & setting inputs", -> - - describe "for 2D recipes", -> - - beforeEach -> - @recipe = new Recipe id:"test1", output:new Stack item:@ironSword - @recipe.setInputAt 1, 0, new Stack item:@ironIngot - @recipe.setInputAt 1, 1, new Stack item:@ironIngot - @recipe.setInputAt 1, 2, new Stack item:@stick - - it "returns assigned values as expected", -> - expect(@recipe.getInputAt(0, 0)).to.equal null - @recipe.getInputAt(1, 0).item.displayName.should.equal "Iron Ingot" - @recipe.getInputAt(1, 1).item.displayName.should.equal "Iron Ingot" - @recipe.getInputAt(1, 2).item.displayName.should.equal "Stick" - expect(@recipe.getInputAt(2, 2)).to.equal null - - it "determines the correct dimentions", -> - @recipe.depth.should.equal 1 - @recipe.height.should.equal 3 - @recipe.width.should.equal 2 - - describe "for 3D recipes", -> - - beforeEach -> - @recipe = new Recipe id:"test2", output:new Stack item:@obsidianBox - for x in [0..2] - for y in [0..2] - for z in [0..2] - continue if x is 1 and y is 1 - continue if x is 1 and z is 1 - continue if y is 1 and z is 1 - - @recipe.setInputAt x, y, z, new Stack item:@obsidian - - it "returns assigned values as expected", -> - @recipe.getInputAt(0, 0, 0).item.displayName.should.equal "Obsidian" - @recipe.getInputAt(2, 0, 0).item.displayName.should.equal "Obsidian" - @recipe.getInputAt(0, 2, 0).item.displayName.should.equal "Obsidian" - @recipe.getInputAt(2, 2, 2).item.displayName.should.equal "Obsidian" - expect(@recipe.getInputAt(1, 1, 1)).to.equal null - expect(@recipe.getInputAt(1, 1, 0)).to.equal null - - it "determines the correct dimentions", -> - @recipe.depth.should.equal 3 - @recipe.height.should.equal 3 - @recipe.width.should.equal 3 \ No newline at end of file diff --git a/src/client/models/game/stack.coffee b/src/client/models/game/stack.coffee deleted file mode 100644 index aa8541cfc..000000000 --- a/src/client/models/game/stack.coffee +++ /dev/null @@ -1,42 +0,0 @@ -# -# Crafting Guide - stack.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class Stack - - constructor: (attributes={})-> - @item = attributes.item - @quantity = attributes.quantity - - # Properties ################################################################################### - - Object.defineProperties @prototype, - - item: - get: -> return @_item - set: (item)-> - if not item? then throw new Error "item is required" - if @_item is item then return - if @_item? then throw new Error "item cannot be reassigned" - @_item = item - - modPack: - get: -> return @_item.modPack - set: -> throw new Error "modPack cannot be replaced" - - quantity: - get: -> return @_quantity - set: (quantity)-> - quantity = parseInt "#{quantity}" - quantity = if Number.isNaN(quantity) then 0 else Math.max(0, quantity) - @_quantity = quantity - - # Object Overrides ############################################################################# - - toString: -> - return "Stack:#{@item}×#{@quantity}" \ No newline at end of file diff --git a/src/client/models/parsing/mod_pack_json.test.coffee b/src/client/models/parsing/mod_pack_json.test.coffee deleted file mode 100644 index 6053ed7ad..000000000 --- a/src/client/models/parsing/mod_pack_json.test.coffee +++ /dev/null @@ -1,124 +0,0 @@ -# -# Crafting Guide - mod_pack_json.test.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -fixtures = require "../fixtures" -ModPackJsonFormatter = require "./mod_pack_json_formatter" -ModPackJsonParser = require "./mod_pack_json_parser" - -######################################################################################################################## - -describe "ModPackJsonParser & ModPackJsonFormatter", -> - - beforeEach -> - @formatter = new ModPackJsonFormatter - @parser = new ModPackJsonParser - - @modPack = fixtures.createModPack id:"alpha", displayName:"ALPHA" - - @runTest = => - @string1 = @formatter.format @modPack - @string2 = @formatter.format @parser.parse @string1 - @result = @parser.parse @string2 - - describe "an empty modpack", -> - - beforeEach -> @runTest() - - it "can survive a round trip", -> - @string1.should.equal @string2 - - it "contains the modpack's own properties", -> - @result.id.should.equal @modPack.id - @result.displayName.should.equal @modPack.displayName - - it "doesn't contain a mods list", -> - expect(@result.mods).to.beUndefined - - describe "a modpack with a single mod", -> - - beforeEach -> - @mod = fixtures.createMod modPack:@modPack, id:"bravo", displayName:"BRAVO" - - describe "containing only a gatherable item", -> - - beforeEach -> - @oakWood = fixtures.configureOakWood @mod - @runTest() - - it "can survive a round trip", -> - @string1.should.equal @string2 - - it "contains the correct mod", -> - mod = @result.mods[@mod.id] - mod.displayName.should.equal @mod.displayName - - it "contains the item", -> - item = @result.mods[@mod.id].items[@oakWood.id] - item.displayName.should.equal @oakWood.displayName - - describe "containing a multi-step item & it's requirements", -> - - beforeEach -> - @craftingTable = fixtures.configureCraftingTable @mod - @oakPlank = fixtures.configureOakPlank @mod - @runTest() - - it "can survive a round trip", -> - @string1.should.equal @string2 - - it "contains oak planks", -> - item = @result.mods[@mod.id].items[@oakPlank.id] - item.displayName.should.equal @oakPlank.displayName - - it "has the recipe for a crafting table", -> - recipe = @result.mods[@mod.id].items[@craftingTable.id].firstRecipe - recipe.getInputAt(0, 0).item.id.should.equal @oakPlank.id - recipe.getInputAt(0, 1).item.id.should.equal @oakPlank.id - recipe.getInputAt(1, 0).item.id.should.equal @oakPlank.id - recipe.getInputAt(1, 1).item.id.should.equal @oakPlank.id - recipe.output.quantity.should.equal 1 - - describe "containing a complex item which needs tools & it's requirements", -> - - beforeEach -> - @cake = fixtures.configureCake @mod - @runTest() - - it "can survive a round trip", -> - @string1.should.equal @string2 - - it "has the recipe for a cake", -> - recipe = @result.mods[@mod.id].items[@cake.id].firstRecipe - - describe "a modpack with multiple mods", -> - - beforeEach -> - @modA = fixtures.createMod modPack:@modPack, id:"bravo", displayName:"BRAVO" - @modB = fixtures.createMod modPack:@modPack, id:"charlie", displayName:"CHARLIE" - - describe "where items are used in recipes crossing mods", -> - - beforeEach -> - @stick = fixtures.configureStick @modA - @ironIngot = fixtures.configureIronIngot @modA - @ironSword = fixtures.configureIronSword @modB - @runTest() - - it "can survive a round trip", -> - @string1.should.equal @string2 - - it "has each item in the correct mod", -> - @result.mods[@modA.id].items[@ironIngot.id].displayName.should.equal @ironIngot.displayName - @result.mods[@modB.id].items[@ironSword.id].displayName.should.equal @ironSword.displayName - - it "has the correct recipe for an iron sword", -> - recipe = @result.mods[@modB.id].items[@ironSword.id].firstRecipe - recipe.output.item.id.should.equal @ironSword.id - recipe.output.quantity.should.equal 1 - recipe.getInputAt(1, 0).item.id.should.equal @ironIngot.id - recipe.getInputAt(1, 1).item.id.should.equal @ironIngot.id - recipe.getInputAt(1, 2).item.id.should.equal @stick.id diff --git a/src/client/models/parsing/mod_pack_json_formatter.coffee b/src/client/models/parsing/mod_pack_json_formatter.coffee deleted file mode 100644 index f0032a1de..000000000 --- a/src/client/models/parsing/mod_pack_json_formatter.coffee +++ /dev/null @@ -1,101 +0,0 @@ -# -# Crafting Guide - mod_pack_json_formatter.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -######################################################################################################################## - -module.exports = class ModPackJsonParser - - constructor: -> - @_reset() - - # Public Methods ############################################################################### - - format: (modPack)-> - @_reset() - return JSON.stringify @_formatModPack modPack - - # Private Methods ############################################################################## - - _formatItem: (item)-> - result = {} - result.id = item.id - result.displayName = item.displayName - - if item.isGatherable and item.firstRecipe? - result.gatherable = true - - return result - - _formatMod: (mod)-> - result = {} - result.id = mod.id - result.displayName = mod.displayName - - for itemId, item of mod.items - result.items ?= [] - @_itemIndexById[item.id] = @_itemIndex++ - result.items.push @_formatItem item - - return result - - _formatModPack: (modPack)-> - result = {} - result.id = modPack.id - result.displayName = modPack.displayName - - for modId, mod of modPack.mods - result.mods ?= [] - result.mods.push @_formatMod mod - - if result.mods? - for modResult in result.mods - for itemResult in modResult.items - item = modPack.mods[modResult.id].items[itemResult.id] - for recipeId, recipe of item.recipesAsPrimary - itemResult.recipes ?= [] - itemResult.recipes.push @_formatRecipe recipe - - return result - - _formatRecipe: (recipe)-> - result = {} - result.id = recipe.id - - if recipe.output.quantity > 1 - result.quantity = recipe.output.quantity - - result.width = recipe.width - result.height = recipe.height - result.depth = recipe.depth if recipe.depth > 1 - result.inputs = [] - - for x in [0...recipe.width] - for y in [0...recipe.height] - for z in [0...recipe.depth] - inputStack = @_formatStack recipe.getInputAt x, y, z - result.inputs.push inputStack - - for itemId, stack of recipe.extras - result.extras ?= [] - result.extras.push @_formatStack stack - - for itemId, item of recipe.tools - result.tools ?= [] - result.tools.push @_itemIndexById[itemId] - - return result - - _formatStack: (stack)-> - return null unless stack? - - itemIndex = @_itemIndexById[stack.item.id] - if stack.quantity is 1 then return itemIndex - return [itemIndex, stack.quantity] - - _reset: -> - @_itemIndex = 0 - @_itemIndexById = {} diff --git a/src/client/models/parsing/mod_pack_json_parser.coffee b/src/client/models/parsing/mod_pack_json_parser.coffee deleted file mode 100644 index e0aa25f74..000000000 --- a/src/client/models/parsing/mod_pack_json_parser.coffee +++ /dev/null @@ -1,164 +0,0 @@ -# -# Crafting Guide - mod_pack_json_parser.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -Item = require "../game/item" -Mod = require "../game/mod" -ModPack = require "../game/mod_pack" -Recipe = require "../game/recipe" -Stack = require "../game/stack" - -######################################################################################################################## - -module.exports = class ModPackJsonParser - - constructor: -> - @_reset() - - # Public Methods ############################################################################### - - parse: (arg, fileName=null)-> - @_reset() - @_fileName = fileName - - if _.isString arg - @_parseText arg - else - @_parseObject arg - - return @_modPack - - # Private Methods ############################################################################## - - _parseInteger: (text, defaultValue)-> - result = parseInt "#{text}" - result = if Number.isNaN result then defaultValue else result - return result - - _parseText: (text)-> - try - obj = JSON.parse text - catch error - @_throwError "could not parse JSON: #{error}" - - @_parseObject obj - - _parseObject: (obj)-> - @_data = obj - @_parseModPack() - @_parseMods() - @_parseItems() - @_parseRecipes() - - _parseModPack: -> - if not @_data? then @_throwError "there is no valid data" - if not @_data.id? then @_throwError "modPack requires an id" - if not @_data.displayName? then @_throwError "modPack requires a displayName" - - @_modPack = new ModPack id:@_data.id, displayName:@_data.displayName - - _parseMods: -> - return unless @_data.mods? - - for modData, index in @_data.mods - @_location = "mods[#{index}]" - if not modData.id? then @_throwError "mod requires an id" - if not modData.displayName? then @_throwError "mod requires a displayName" - - new Mod modPack:@_modPack, id:modData.id, displayName:modData.displayName - - _parseItems: -> - return unless @_data.mods? - - for modData in @_data.mods - continue unless modData.items? - - mod = @_modPack.mods[modData.id] - for itemData, index in modData.items - @_location = "<#{mod.id}>.items[#{index}]" - if not itemData.id? then @_throwError "item requires an id" - if not itemData.displayName? then @_throwError "item requires a displayName" - - item = new Item mod:mod, id:itemData.id, displayName:itemData.displayName - item.gatherable = itemData.gatherable if itemData.gatherable? - @_items.push item - - _parseRecipes: -> - return unless @_data.mods? - - for modData in @_data.mods - continue unless modData.items? - - mod = @_modPack.mods[modData.id] - for itemData, index in modData.items - continue unless itemData.recipes? - - item = mod.items[itemData.id] - for recipeData, index in itemData.recipes - @_location = "<#{itemData.id}>.recipes[#{index}]" - if not recipeData.id? then @_throwError "recipe requires id" - if not recipeData.inputs? then @_throwError "recipe requires inputs" - - quantity = @_parseInteger recipeData.quantity, 1 - outputStack = new Stack item:item, quantity:quantity - recipe = new Recipe id:recipeData.id, output:outputStack - - depth = @_parseInteger recipeData.depth, 1 - height = @_parseInteger recipeData.height, 3 - width = @_parseInteger recipeData.width, 3 - - index = 0 - for x in [0...width] - for y in [0...height] - for z in [0...depth] - stack = @_parseStack recipeData.inputs[index] - if stack? then recipe.setInputAt x, y, z, stack - index++ - - if recipeData.extras - for stackData in recipeData.extras - recipe.addExtra @_parseStack stackData - - if recipeData.tools - for index in recipeData.tools - toolItem = @_items[index] - if not toolItem? then @_throwError "there is no item #{index}" - recipe.addTool toolItem - - _parseStack: (stackData)-> - return null unless stackData? - if _.isArray(stackData) - if stackData.length isnt 2 then @_throwError "input stacks must have an item index and a quantity" - index = stackData[0] - quantity = stackData[1] - else - index = stackData - quantity = 1 - - item = @_items[index] - if not item? then @_throwError "there is no item #{index}" - - return new Stack item:item, quantity:quantity - - _reset: -> - @_data = null - @_fileName = null - @_items = [] - @_location = null - @_modPack = null - - _throwError: (message, cause=null)-> - if @_location? then message = "#{@_location}: #{message}" - if @_fileName? and @_location? then message = "@#{message}" - if @_fileName? then message = "#{@_fileName}#{message}" - if cause? then message = "#{message}: #{cause}" - - error = new Error message - error.cause = cause if cause? - error.fileName = @_fileName if @_fileName? - error.location = @_location if @_location? - - throw error diff --git a/src/client/models/site/craft_page.coffee b/src/client/models/site/craft_page.coffee index 3085cc24f..712a53ff8 100644 --- a/src/client/models/site/craft_page.coffee +++ b/src/client/models/site/craft_page.coffee @@ -5,10 +5,12 @@ # All rights reserved. # -BaseModel = require '../base_model' -Craftsman = require '../crafting/craftsman' -Inventory = require '../game/inventory' -ModPack = require '../game/mod_pack' +CraftingGuideCommon = require "crafting-guide-common" + +{BaseModel} = CraftingGuideCommon.deprecated +{Craftsman} = CraftingGuideCommon.deprecated.crafting +{Inventory} = CraftingGuideCommon.deprecated.game +{ModPack} = CraftingGuideCommon.deprecated.game ######################################################################################################################## diff --git a/src/client/models/site/editable_file.coffee b/src/client/models/site/editable_file.coffee index b37bda8b5..7c05503ed 100644 --- a/src/client/models/site/editable_file.coffee +++ b/src/client/models/site/editable_file.coffee @@ -5,7 +5,8 @@ # All rights reserved. # -BaseModel = require '../base_model' +{BaseModel} = require("crafting-guide-common").deprecated +w = require "when" ######################################################################################################################## diff --git a/src/client/models/site/file_cache.coffee b/src/client/models/site/file_cache.coffee index 2ddd6580c..521d71559 100644 --- a/src/client/models/site/file_cache.coffee +++ b/src/client/models/site/file_cache.coffee @@ -5,6 +5,8 @@ # All rights reserved. # +w = require "when" + ######################################################################################################################## module.exports = class FileCache extends Backbone.Events diff --git a/src/client/models/site/file_cache.test.coffee b/src/client/models/site/file_cache.test.coffee index 737778c47..2fef672e7 100644 --- a/src/client/models/site/file_cache.test.coffee +++ b/src/client/models/site/file_cache.test.coffee @@ -1,3 +1,9 @@ +# +# Crafting Guide - file_cache.test.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# FileCache = require './file_cache' diff --git a/src/client/models/site/github_user.coffee b/src/client/models/site/github_user.coffee index fb2afe354..35f718fad 100644 --- a/src/client/models/site/github_user.coffee +++ b/src/client/models/site/github_user.coffee @@ -5,7 +5,7 @@ # All rights reserved. # -BaseModel = require '../base_model' +{BaseModel} = require("crafting-guide-common").deprecated ######################################################################################################################## diff --git a/src/client/models/site/item_page.coffee b/src/client/models/site/item_page.coffee index e673f004a..052e4768a 100644 --- a/src/client/models/site/item_page.coffee +++ b/src/client/models/site/item_page.coffee @@ -5,7 +5,7 @@ # All rights reserved. # -BaseModel = require '../base_model' +{BaseModel} = require("crafting-guide-common").deprecated ######################################################################################################################## diff --git a/src/client/models/site/item_selector.coffee b/src/client/models/site/item_selector.coffee index ae6ec3071..3c543d7bf 100644 --- a/src/client/models/site/item_selector.coffee +++ b/src/client/models/site/item_selector.coffee @@ -5,8 +5,8 @@ # All rights reserved. # -BaseModel = require '../base_model' -ItemSlug = require '../game/item_slug' +{BaseModel} = require('crafting-guide-common').deprecated +{ItemSlug} = require('crafting-guide-common').deprecated.game ######################################################################################################################## diff --git a/src/client/models/site/markdown_image_list.coffee b/src/client/models/site/markdown_image_list.coffee index 9ad3331e3..acac909fb 100644 --- a/src/client/models/site/markdown_image_list.coffee +++ b/src/client/models/site/markdown_image_list.coffee @@ -5,7 +5,7 @@ # All rights reserved. # -BaseModel = require '../base_model' +{BaseModel} = require('crafting-guide-common').deprecated MarkdownImage = require './markdown_image' ######################################################################################################################## diff --git a/src/client/models/site/tutorial.coffee b/src/client/models/site/tutorial.coffee deleted file mode 100644 index 702f0d718..000000000 --- a/src/client/models/site/tutorial.coffee +++ /dev/null @@ -1,33 +0,0 @@ -# -# Crafting Guide - tutorial.coffee -# -# Copyright © 2014-2016 by Redwood Labs -# All rights reserved. -# - -BaseModel = require '../base_model' - -######################################################################################################################## - -module.exports = class Tutorial extends BaseModel - - constructor: (attributes={}, options={})-> - if not attributes.name?.length > 0 then throw new Error "attributes.name cannot be empty" - attributes.modSlug ?= null - attributes.officialUrl ?= null - attributes.sections ?= [] - attributes.slug ?= _.slugify attributes.name - attributes.videos ?= [] - super attributes, options - - # Backbone.Model Overrides ##################################################################### - - parse: (text)-> - TutorialParser = require '../parsing/tutorial_parser' # to avoid require cycles - @_parser ?= new TutorialParser model:this - @_parser.parse text - - return null # prevent calling `set` - - url: -> - return c.url.tutorialData modSlug:@modSlug, tutorialSlug:@slug diff --git a/src/client/models/stores/mod_pack_store.coffee b/src/client/models/stores/mod_pack_store.coffee index 4ddcfe86e..5c8bcf947 100644 --- a/src/client/models/stores/mod_pack_store.coffee +++ b/src/client/models/stores/mod_pack_store.coffee @@ -6,6 +6,7 @@ # ModPackJsonParser = require "../parsing/mod_pack_json_parser" +w = require "when" ######################################################################################################################## diff --git a/src/client/site/browse_page/browse_page_controller.coffee b/src/client/site/browse_page/browse_page_controller.coffee index 2f1422129..b1972a414 100644 --- a/src/client/site/browse_page/browse_page_controller.coffee +++ b/src/client/site/browse_page/browse_page_controller.coffee @@ -5,10 +5,10 @@ # All rights reserved. # +{CraftingGuideClient} = require('crafting-guide-common').api ModBallotController = require './mod_ballot/mod_ballot_controller' ModTileController = require './mod_tile/mod_tile_controller' PageController = require '../page_controller' -{CraftingGuideClient} = require 'crafting-guide-common' ######################################################################################################################## diff --git a/src/client/site/common/adsense/adsense_controller.coffee b/src/client/site/common/adsense/adsense_controller.coffee index f0143d6d2..90d1fb6c8 100644 --- a/src/client/site/common/adsense/adsense_controller.coffee +++ b/src/client/site/common/adsense/adsense_controller.coffee @@ -140,7 +140,7 @@ module.exports = class AdsenseController adCount = @_computeAdCount @_computeAdType() if adCount is 0 - logger.verbose "Adsense is waiting for room to insert ads" + logger.trace "Adsense is waiting for room to insert ads" @_waiting = true _.delay (=> @_waiting = false; @_waitForPageReadiness()), c.adsense.readinessCheckInterval else diff --git a/src/client/site/common/item_selector/item_selector_controller.coffee b/src/client/site/common/item_selector/item_selector_controller.coffee index ed108ba08..f5a5feeee 100644 --- a/src/client/site/common/item_selector/item_selector_controller.coffee +++ b/src/client/site/common/item_selector/item_selector_controller.coffee @@ -8,6 +8,7 @@ BaseController = require '../../base_controller' ItemSelector = require '../../../models/site/item_selector' ElementController = require './element/element_controller' +w = require "when" ######################################################################################################################## diff --git a/src/client/site/common/recipe/recipe_controller.coffee b/src/client/site/common/recipe/recipe_controller.coffee index 274d40566..e1b82e99b 100644 --- a/src/client/site/common/recipe/recipe_controller.coffee +++ b/src/client/site/common/recipe/recipe_controller.coffee @@ -8,7 +8,7 @@ BaseController = require '../../base_controller' CraftingGridController = require '../crafting_grid/crafting_grid_controller' SlotController = require '../slot/slot_controller' -{StringBuilder} = require 'crafting-guide-common' +{StringBuilder} = require('crafting-guide-common').util ######################################################################################################################## diff --git a/src/client/site/craft_page/craft_page_controller.coffee b/src/client/site/craft_page/craft_page_controller.coffee index 47a9dc634..05b79758a 100644 --- a/src/client/site/craft_page/craft_page_controller.coffee +++ b/src/client/site/craft_page/craft_page_controller.coffee @@ -7,11 +7,11 @@ BaseController = require '../base_controller' CraftPage = require '../../models/site/craft_page' -Craftsman = require '../../models/crafting/craftsman' +{Craftsman} = require('crafting-guide-common').deprecated.crafting CraftsmanWorkingController = require './craftsman_working/craftsman_working_controller' InventoryController = require '../common/inventory/inventory_controller' PageController = require '../page_controller' -SimpleInventory = require '../../models/crafting/simple_inventory' +{SimpleInventory} = require('crafting-guide-common').deprecated.crafting StepController = require './step/step_controller' ######################################################################################################################## @@ -64,6 +64,7 @@ module.exports = class CraftPageController extends PageController sampleInventory.parse inventoryText @model.craftsman.want.addInventory sampleInventory @_scrollTo @$workingSection + @onWantInventoryChanged() return false diff --git a/src/client/site/craft_page/craftsman_working/craftsman_working_controller.coffee b/src/client/site/craft_page/craftsman_working/craftsman_working_controller.coffee index 36498fb1c..934854582 100644 --- a/src/client/site/craft_page/craftsman_working/craftsman_working_controller.coffee +++ b/src/client/site/craft_page/craftsman_working/craftsman_working_controller.coffee @@ -6,7 +6,7 @@ # BaseController = require '../../base_controller' -Craftsman = require '../../../models/crafting/craftsman' +{Craftsman} = require('crafting-guide-common').deprecated.crafting ######################################################################################################################## diff --git a/src/client/site/craft_page/step/step_controller.coffee b/src/client/site/craft_page/step/step_controller.coffee index 4015c6e06..cf6df7d74 100644 --- a/src/client/site/craft_page/step/step_controller.coffee +++ b/src/client/site/craft_page/step/step_controller.coffee @@ -8,7 +8,7 @@ BaseController = require '../../base_controller' InventoryController = require '../../common/inventory/inventory_controller' RecipeController = require '../../common/recipe/recipe_controller' -SimpleInventory = require '../../../models/crafting/simple_inventory' +{SimpleInventory} = require('crafting-guide-common').deprecated.crafting ######################################################################################################################## diff --git a/src/client/site/feedback/feedback_controller.coffee b/src/client/site/feedback/feedback_controller.coffee index 0157099e4..70f8bcc6f 100644 --- a/src/client/site/feedback/feedback_controller.coffee +++ b/src/client/site/feedback/feedback_controller.coffee @@ -7,6 +7,7 @@ BaseController = require '../base_controller' EmailClient = require '../../models/site/email_client' +w = require "when" ######################################################################################################################## diff --git a/src/client/site/item_page/item_page_controller.coffee b/src/client/site/item_page/item_page_controller.coffee index f78e95ed0..ea811d61e 100644 --- a/src/client/site/item_page/item_page_controller.coffee +++ b/src/client/site/item_page/item_page_controller.coffee @@ -6,15 +6,16 @@ # EditableFile = require '../../models/site/editable_file' -Item = require '../../models/game/item' +{Item} = require('crafting-guide-common').deprecated.game ItemGroupController = require '../common/item_group/item_group_controller' ItemPage = require '../../models/site/item_page' -ItemSlug = require '../../models/game/item_slug' +{ItemSlug} = require('crafting-guide-common').deprecated.game MarkdownSectionController = require '../common/markdown_section/markdown_section_controller' MultiblockViewerController = require './multiblock_viewer/multiblock_viewer_controller' PageController = require '../page_controller' RecipeDetailController = require './recipe_detail/recipe_detail_controller' VideoController = require '../common/video/video_controller' +w = require "when" ######################################################################################################################## diff --git a/src/client/site/item_page/recipe_detail/recipe_detail_controller.coffee b/src/client/site/item_page/recipe_detail/recipe_detail_controller.coffee index e386a27b2..16a3bb99f 100644 --- a/src/client/site/item_page/recipe_detail/recipe_detail_controller.coffee +++ b/src/client/site/item_page/recipe_detail/recipe_detail_controller.coffee @@ -7,9 +7,9 @@ BaseController = require '../../base_controller' CraftingGridController = require '../../common/crafting_grid/crafting_grid_controller' -Inventory = require '../../../models/game/inventory' +{Inventory} = require('crafting-guide-common').deprecated.game InventoryController = require '../../common/inventory/inventory_controller' -{StringBuilder} = require 'crafting-guide-common' +{StringBuilder} = require('crafting-guide-common').util ######################################################################################################################## diff --git a/src/client/site/login_page/login_page_controller.coffee b/src/client/site/login_page/login_page_controller.coffee index c63a185cc..8256258f2 100644 --- a/src/client/site/login_page/login_page_controller.coffee +++ b/src/client/site/login_page/login_page_controller.coffee @@ -7,7 +7,7 @@ PageController = require '../page_controller' GitHubUser = require '../../models/site/github_user' -{CraftingGuideClient} = require 'crafting-guide-common' +{CraftingGuideClient} = require('crafting-guide-common').api ######################################################################################################################## diff --git a/src/client/site/mod_page/mod_page_controller.coffee b/src/client/site/mod_page/mod_page_controller.coffee index 3bc5ce730..1daf88343 100644 --- a/src/client/site/mod_page/mod_page_controller.coffee +++ b/src/client/site/mod_page/mod_page_controller.coffee @@ -5,10 +5,10 @@ # All rights reserved. # -PageController = require '../page_controller' -Item = require '../../models/game/item' +{Item} = require('crafting-guide-common').deprecated.game ItemGroupController = require '../common/item_group/item_group_controller' -Mod = require '../../models/game/mod' +{Mod} = require('crafting-guide-common').deprecated.game +PageController = require '../page_controller' TutorialController = require './tutorial/tutorial_controller' ######################################################################################################################## diff --git a/src/client/site/router.coffee b/src/client/site/router.coffee index c2b4eca18..e4b047710 100644 --- a/src/client/site/router.coffee +++ b/src/client/site/router.coffee @@ -5,15 +5,15 @@ # All rights reserved. # -BrowsePageController = require './browse_page/browse_page_controller' -CraftPageController = require './craft_page/craft_page_controller' -ItemPageController = require './item_page/item_page_controller' -ItemSlug = require '../models/game/item_slug' -LoginPageController = require './login_page/login_page_controller' -ModPageController = require './mod_page/mod_page_controller' -NewsPageController = require './news_page/news_page_controller' -TutorialPageController = require './tutorial_page/tutorial_page_controller' -UrlParams = require './url_params' +BrowsePageController = require './browse_page/browse_page_controller' +CraftPageController = require './craft_page/craft_page_controller' +ItemPageController = require './item_page/item_page_controller' +{ItemSlug} = require('crafting-guide-common').deprecated.game +LoginPageController = require './login_page/login_page_controller' +ModPageController = require './mod_page/mod_page_controller' +NewsPageController = require './news_page/news_page_controller' +TutorialPageController = require './tutorial_page/tutorial_page_controller' +UrlParams = require './url_params' ######################################################################################################################## diff --git a/src/client/site/site_controller.coffee b/src/client/site/site_controller.coffee index 5c51d3a7a..13f050d04 100644 --- a/src/client/site/site_controller.coffee +++ b/src/client/site/site_controller.coffee @@ -13,9 +13,8 @@ FooterController = require './footer/footer_controller' GitHubUser = require '../models/site/github_user' HeaderController = require './header/header_controller' ImageLoader = require './image_loader' -Mod = require '../models/game/mod' -ModPack = require '../models/game/mod_pack' -ModPackStore = require '../models/store/mod_pack_store' +{Mod} = require('crafting-guide-common').deprecated.game +{ModPack} = require('crafting-guide-common').deprecated.game Router = require './router' ######################################################################################################################## @@ -31,6 +30,7 @@ module.exports = class SiteController extends BaseController @client = options.client @fileCache = new FileCache c.url.modpackArchive() @imageLoader = new ImageLoader defaultUrl:'/images/unknown.png' + @modPack = new ModPack {}, fileCache:@fileCache @router = new Router this @storage = options.storage @@ -41,6 +41,23 @@ module.exports = class SiteController extends BaseController # Public Methods ############################################################################### + loadDefaultModPack: -> + makeResponder = (m)-> return -> + m.activeModVersion.fetch() if m.activeModVersion? + + for modSlug, modData of c.defaultMods + mod = new Mod {slug:modSlug}, {fileCache:@fileCache} + mod.on c.event.change + ':activeModVersion', makeResponder mod + @storage.register "mod:#{mod.slug}", mod, 'activeVersion', modData.defaultVersion + mod.fetch() + + @modPack.addMod mod + + if global.env isnt 'prerender' + @modPack.once c.event.sync, => + @$pageContent.removeClass 'hidden' + @$pageContentLoading.addClass 'hidden' + loadCurrentUser: -> @client.getCurrentUser() .then (response)=> diff --git a/src/client/tracker.coffee b/src/client/tracker.coffee index f67681d62..2ad3edbe7 100644 --- a/src/client/tracker.coffee +++ b/src/client/tracker.coffee @@ -5,6 +5,8 @@ # All rights reserved. # +w = require "when" + ######################################################################################################################## module.exports = class Tracker diff --git a/src/common/constants.coffee b/src/common/constants.coffee index 78d35d37a..b059663fe 100644 --- a/src/common/constants.coffee +++ b/src/common/constants.coffee @@ -5,6 +5,9 @@ # All rights reserved. # +_ = require "./underscore" +_.extend exports, require("crafting-guide-common").constants + ######################################################################################################################## exports.adsense = adsense = {} @@ -29,31 +32,52 @@ adsense.skyscraper.margin = 24 # px adsense.skyscraper.slotIds = ['7613920409', '9574673605', '3388539204'] adsense.skyscraper.width = 160 # px +exports.defaultMods = defaultMods = {} +defaultMods.minecraft = { defaultVersion: '1.7.10' } # Minecraft must be first + +defaultMods.advanced_solar_panels = { defaultVersion: '3.5.1' } +defaultMods.agricraft = { defaultVersion: '1.7.10_1.5.0' } +defaultMods.applied_energistics_2 = { defaultVersion: 'rv1-stable-1' } +defaultMods.big_reactors = { defaultVersion: '0.4.2A2' } +defaultMods.buildcraft = { defaultVersion: '1.7.18' } +defaultMods.computercraft = { defaultVersion: '1.74' } +defaultMods.draconic_evolution = { defaultVersion: '1.0.2h' } +defaultMods.ender_storage = { defaultVersion: '1.4.5.29' } +defaultMods.enderio = { defaultVersion: '2.2.7.325' } +defaultMods.extra_cells = { defaultVersion: '2.2.73b129' } +defaultMods.extra_utilities = { defaultVersion: '1.2.2' } +defaultMods.forestry = { defaultVersion: '3.4.0.7' } +defaultMods.forge_multipart = { defaultVersion: '1.2.0.345' } +defaultMods.galacticraft = { defaultVersion: '3.0.12.404' } +defaultMods.hydraulicraft = { defaultVersion: '2.1.242' } +defaultMods.ic2_classic = { defaultVersion: 'none' } +defaultMods.industrial_craft_2 = { defaultVersion: '2.2.663' } +defaultMods.iron_chests = { defaultVersion: '6.0.62.742' } +defaultMods.jabba = { defaultVersion: '1.2.1a' } +defaultMods.logistics_pipes = { defaultVersion: '0.9.3.100' } +defaultMods.mekanism = { defaultVersion: '7.1.1.127' } +defaultMods.minefactory_reloaded = { defaultVersion: '2.8.0RC8-86' } +defaultMods.modular_powersuits = { defaultVersion: '0.11.0-300-thermal-expansion' } +defaultMods.opencomputers = { defaultVersion: '1.5.22' } +defaultMods.quantum_flux = { defaultVersion: '1.3.4' } +defaultMods.project_red = { defaultVersion: '4.5.16.77' } +defaultMods.redstone_arsenal = { defaultVersion: '9.5.0' } +defaultMods.railcraft = { defaultVersion: '9.5.0' } +defaultMods.simply_jetpacks = { defaultVersion: '1.4.1' } +defaultMods.solar_expansion = { defaultVersion: '1.6a' } +defaultMods.solar_flux = { defaultVersion: '0.5b' } +defaultMods.storage_drawers = { defaultVersion: '1.7.10-1.6.2' } +defaultMods.thermal_dynamics = { defaultVersion: '1.7.10r1.2.0' } +defaultMods.thermal_expansion = { defaultVersion: '1.7.10r4.1.4' } +defaultMods.thermal_foundation = { defaultVersion: '1.7.10r1.2.5' } +defaultMods.tinkers_construct = { defaultVersion: '1.7.10-1.8.8' } + exports.duration = duration = {} duration.snap = 100 duration.fast = 200 duration.normal = 400 duration.slow = 1200 -exports.event = event = {} -event.add = 'add' # collection, item... -event.button = {} -event.button.complete = 'button:complete' # controller -event.button.first = 'button:first' # controller, buttonType -event.button.second = 'button:second' # controller, buttonType -event.change = 'change' # model -event.click = 'click' # event -event.load = {} -event.load.started = 'load:started' # controller, url -event.load.succeeded = 'load:succeeded' # controller, book -event.load.failed = 'load:failed' # controller, error message -event.load.finished = 'load:finished' # controller -event.remove = 'remove' # collection, item... -event.request = 'request' # model -event.route = 'route' -event.sort = 'sort' -event.sync = 'sync' # model, response - exports.gitHub = gitHub = {} gitHub.file = {} gitHub.file.itemDescription = {} @@ -67,10 +91,6 @@ key.escape = 27 key.upArrow = 38 key.downArrow = 40 -exports.limits = limits = {} -limits.maximumGraphSize = 5000 -limits.maximumPlanCount = 5000 - exports.login = login = {} login.authorizeUrl = _.template "https://github.com/login/oauth/authorize" + "?client_id=<%= clientId %>&scope=public_repo&state=<%= state %>" @@ -82,12 +102,6 @@ login.clientIds = exports.modpack = modpack = {} modpack.default = "crafting-guide-default" -exports.modelState = modelState = {} -modelState.unloaded = 'unloaded' -modelState.loading = 'loading' -modelState.loaded = 'loaded' -modelState.failed = 'failed' - exports.opacity = opacity = {} opacity.hidden = 1e-6 opacity.shown = 1 @@ -138,22 +152,3 @@ tracking.category.modVote = 'mod-vote' tracking.category.multiblock = 'multiblock' tracking.category.navigate = 'navigate' tracking.category.search = 'search' - -exports.url = url = {} -url.crafting = _.template "/craft/<%= inventoryText %>" -url.item = _.template "/browse/<%= modSlug %>/<%= itemSlug %>/" -url.itemData = _.template "/data/<%= modSlug %>/items/<%= itemSlug %>/item.cg" -url.itemIcon = _.template "/data/<%= modSlug %>/items/<%= itemSlug %>/icon.png" -url.itemImageDir = _.template "/data/<%= modSlug %>/items/<%= itemSlug %>" -url.login = _.template "/login" -url.mod = _.template "/browse/<%= modSlug %>/" -url.modData = _.template "/data/<%= modSlug %>/mod.cg" -url.modIcon = _.template "/data/<%= modSlug %>/icon.png" -url.modPackData = _.template "/data/<%= modPackId %>/modpack.json" -url.modVersionData = _.template "/data/<%= modSlug %>/versions/<%= modVersion %>/mod-version.cg" -url.root = _.template "/" -url.tutorial = _.template "/browse/<%= modSlug %>/tutorials/<%= tutorialSlug %>/" -url.tutorialData = _.template "/data/<%= modSlug %>/tutorials/<%= tutorialSlug %>/tutorial.cg" -url.tutorialIcon = _.template "/data/<%= modSlug %>/tutorials/<%= tutorialSlug %>/icon.png" -url.tutorialIcon = _.template "/data/<%= modSlug %>/tutorials/<%= tutorialSlug %>/icon.png" -url.tutorialImageDir = _.template "/data/<%= modSlug %>/tutorials/<%= tutorialSlug %>" diff --git a/src/common/underscore.coffee b/src/common/underscore.coffee index af599c914..776b89d24 100644 --- a/src/common/underscore.coffee +++ b/src/common/underscore.coffee @@ -5,33 +5,4 @@ # All rights reserved. # -module.exports = _ = require 'underscore' - -_.mixin require 'underscore.inflections' -_.mixin require('crafting-guide-common').stringMixins - -_.mixin - parseMarkdown: (text)-> - return markdown.parse text, 'Maruku' - - slugify: (text)-> - return null unless text? - - result = text.toLowerCase() - result = result.replace /[^a-zA-Z0-9_]/g, '_' - result = result.replace /__+/g, '_' - result = result.replace /^_/, '' - result = result.replace /_$/, '' - return result - - composeSlugs: (part1, part2)-> - return "#{part1}__#{part2}" - - decomposeSlug: (slug)-> - return [null, null] unless slug? - - parts = slug.split '__' - if parts.length is 1 - parts = [ null, parts[0] ] - - return parts +module.exports = _ = require('crafting-guide-common')._ diff --git a/src/index.coffee b/src/index.coffee index 77e6e047c..8e32e472b 100644 --- a/src/index.coffee +++ b/src/index.coffee @@ -7,13 +7,4 @@ ######################################################################################################################## -global.Backbone = require 'backbone' -global._ = require './common/underscore' -global.c = require './common/constants' - -module.exports = - constants: require './common/constants' - Mod: require './client/models/game/mod' - ModParser: require './client/models/parsing/mod_parser' - ModVersion: require './client/models/game/mod_version' - ModVersionParser: require './client/models/parsing/mod_version_parser' +# Just to keep NPM happy... nothing is actually exported. diff --git a/src/server/crafting_guide_server.coffee b/src/server/crafting_guide_server.coffee index 2e2faa82d..3459e7fac 100644 --- a/src/server/crafting_guide_server.coffee +++ b/src/server/crafting_guide_server.coffee @@ -9,6 +9,7 @@ express = require 'express' http = require 'http' middleware = require './middleware' routes = require './routes' +w = require "when" ############################################################################################################ diff --git a/src/server/server.coffee b/src/server/server.coffee index 01e7e8feb..0cf899533 100644 --- a/src/server/server.coffee +++ b/src/server/server.coffee @@ -9,12 +9,6 @@ require('dotenv').config() ######################################################################################################################## -global._ = require './underscore' -global.c = require './constants' -global.w = require 'when' - -######################################################################################################################## - CraftingGuideServer = require './crafting_guide_server' server = new CraftingGuideServer process.env.WEBSITE_SERVER_PORT server.start() diff --git a/src/test_helper.coffee b/src/test_helper.coffee index e5a1a7bfc..6aad498d1 100644 --- a/src/test_helper.coffee +++ b/src/test_helper.coffee @@ -1,9 +1,9 @@ -### +# # Crafting Guide - test.coffee # # Copyright (c) 2014-2015 by Redwood Labs # All rights reserved. -### +# # Test Set-up ########################################################################################################## @@ -27,5 +27,5 @@ global.π = Math.PI global.Backbone = Backbone = require 'backbone' Backbone.$ = $ -{Logger} = require 'crafting-guide-common' +{Logger} = require('crafting-guide-common').util global.logger = new Logger level:Logger.FATAL diff --git a/src/underscore.coffee b/src/underscore.coffee new file mode 100644 index 000000000..d8860dcfb --- /dev/null +++ b/src/underscore.coffee @@ -0,0 +1,10 @@ +# +# Crafting Guide - underscore.coffee +# +# Copyright (c) 2014-2017 by Redwood Labs +# All rights reserved. +# + +######################################################################################################################## + +module.exports = _ = require("crafting-guide-common")._ From 69e4d968b5c453ecb9b4d7a0502018dd25fc12d4 Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Tue, 2 May 2017 19:18:02 -0700 Subject: [PATCH 4/5] Upgrade packages to most recent versions & lock --- package.json | 66 ++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 9617daca9..6bd28e8b6 100644 --- a/package.json +++ b/package.json @@ -10,41 +10,41 @@ "test": "grunt test" }, "author": "Andrew Miner", - "license": "none", + "license": "UNLICENSED", "devDependencies": { - "chai": "^1.10.0", - "coffee-script": "^1.9.3", - "coffeeify": "^1.1.0", - "grunt": "^0.4.5", - "grunt-contrib-clean": "^0.6.0", - "grunt-contrib-coffee": "^1.0.0", - "grunt-contrib-compress": "^0.14.0", - "grunt-contrib-copy": "^0.8.2", - "grunt-contrib-jade": "^0.15.0", - "grunt-contrib-sass": "^0.9.2", - "grunt-contrib-uglify": "^0.11.0", - "grunt-contrib-watch": "^0.6.1", - "grunt-mocha-test": "^0.12.7", - "grunt-sass-globbing": "^1.4.0", - "mocha": "^2.4.5", - "sass": "^0.5.0", - "sinon": "^1.14.1", - "sinon-chai": "^2.7.0" + "chai": "3.5.0", + "coffee-script": "1.12.5", + "coffeeify": "2.1.0", + "grunt": "1.0.1", + "grunt-contrib-clean": "1.1.0", + "grunt-contrib-coffee": "1.0.0", + "grunt-contrib-compress": "1.4.1", + "grunt-contrib-copy": "1.0.0", + "grunt-contrib-jade": "1.0.0", + "grunt-contrib-sass": "1.0.0", + "grunt-contrib-uglify": "2.3.0", + "grunt-contrib-watch": "1.0.0", + "grunt-mocha-test": "0.13.2", + "grunt-sass-globbing": "1.5.1", + "mocha": "3.3.0", + "sass": "0.5.0", + "sinon": "2.2.0", + "sinon-chai": "2.10.0" }, "dependencies": { - "backbone": "^1.2.3", - "body-parser": "^1.13.3", - "client-sessions": "^0.7.0", - "cookie-parser": "^1.3.5", - "crafting-guide-common": "^3.2.0", - "dotenv": "^2.0.0", - "express": "^4.13.3", - "express-session": "^1.11.3", - "jade": "^1.11.0", - "jquery": "^2.1.4", - "marked": "^0.3.5", - "underscore": "^1.8.3", - "underscore.inflections": "^0.2.1", - "when": "^3.7.3" + "backbone": "1.3.3", + "body-parser": "1.17.1", + "client-sessions": "0.8.0", + "cookie-parser": "1.4.3", + "crafting-guide-common": ">=3.0.0", + "dotenv": "4.0.0", + "express": "4.15.2", + "express-session": "1.15.2", + "jade": "1.11.0", + "jquery": "3.2.1", + "marked": "0.3.6", + "underscore": "1.8.3", + "underscore.inflections": "0.2.1", + "when": "3.7.8" } } From 7865ba98c3f8aa509fcd48725baa156739e468b4 Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Tue, 2 May 2017 19:19:35 -0700 Subject: [PATCH 5/5] Update copyright notice --- Gruntfile.coffee | 2 +- src/client/client.coffee | 2 +- src/client/includes/_google_analytics.jade | 2 +- src/client/includes/_google_sitelinks.jade | 2 +- src/client/models/site/craft_page.coffee | 2 +- src/client/models/site/editable_file.coffee | 2 +- src/client/models/site/email_client.coffee | 2 +- src/client/models/site/file_cache.coffee | 2 +- src/client/models/site/file_cache.test.coffee | 2 +- src/client/models/site/github_user.coffee | 2 +- src/client/models/site/item_page.coffee | 2 +- src/client/models/site/item_selector.coffee | 2 +- src/client/models/site/markdown_image.coffee | 2 +- src/client/models/site/markdown_image_list.coffee | 2 +- src/client/models/site/markdown_image_list.test.coffee | 2 +- src/client/models/stores/mod_pack_store.coffee | 2 +- src/client/site/base_controller.coffee | 2 +- src/client/site/browse_page/browse_page.jade | 2 +- src/client/site/browse_page/browse_page.scss | 2 +- src/client/site/browse_page/browse_page_controller.coffee | 2 +- src/client/site/browse_page/mod_ballot/mod_ballot.jade | 2 +- src/client/site/browse_page/mod_ballot/mod_ballot.scss | 2 +- .../site/browse_page/mod_ballot/mod_ballot_controller.coffee | 2 +- .../browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.jade | 2 +- .../browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.scss | 2 +- .../mod_ballot_line/mod_ballot_line_controller.coffee | 2 +- .../mod_ballot/suggest_mod_panel/suggest_mod_panel.jade | 2 +- .../mod_ballot/suggest_mod_panel/suggest_mod_panel.scss | 2 +- .../suggest_mod_panel/suggest_mod_panel_controller.coffee | 2 +- src/client/site/browse_page/mod_tile/mod_tile.jade | 2 +- src/client/site/browse_page/mod_tile/mod_tile.scss | 2 +- src/client/site/browse_page/mod_tile/mod_tile_controller.coffee | 2 +- src/client/site/common/adsense/adsense.jade | 2 +- src/client/site/common/adsense/adsense.scss | 2 +- src/client/site/common/adsense/adsense_controller.coffee | 2 +- src/client/site/common/crafting_grid/crafting_grid.scss | 2 +- .../site/common/crafting_grid/crafting_grid_controller.coffee | 2 +- src/client/site/common/inventory/inventory.jade | 2 +- src/client/site/common/inventory/inventory.scss | 2 +- src/client/site/common/inventory/inventory_controller.coffee | 2 +- src/client/site/common/item_group/item_group.scss | 2 +- src/client/site/common/item_group/item_group_controller.coffee | 2 +- src/client/site/common/item_group/item_tile/item_tile.scss | 2 +- .../common/item_group/item_tile/item_tile_controller.coffee | 2 +- src/client/site/common/item_selector/element/element.jade | 2 +- src/client/site/common/item_selector/element/element.scss | 2 +- .../site/common/item_selector/element/element_controller.coffee | 2 +- src/client/site/common/item_selector/item_selector.jade | 2 +- src/client/site/common/item_selector/item_selector.scss | 2 +- .../site/common/item_selector/item_selector_controller.coffee | 2 +- .../markdown_image_list/markdown_image/markdown_image.jade | 2 +- .../markdown_image_list/markdown_image/markdown_image.scss | 2 +- .../markdown_image_list/markdown_image_list.jade | 2 +- .../markdown_image_list/markdown_image_list.scss | 2 +- .../markdown_image_list/markdown_image_list_controller.coffee | 2 +- src/client/site/common/markdown_section/markdown_section.jade | 2 +- src/client/site/common/markdown_section/markdown_section.scss | 2 +- .../common/markdown_section/markdown_section_controller.coffee | 2 +- src/client/site/common/recipe/recipe.scss | 2 +- src/client/site/common/recipe/recipe_controller.coffee | 2 +- src/client/site/common/slot/slot.scss | 2 +- src/client/site/common/slot/slot_controller.coffee | 2 +- src/client/site/common/stack/stack.jade | 2 +- src/client/site/common/stack/stack.scss | 2 +- src/client/site/common/stack/stack_controller.coffee | 2 +- src/client/site/common/twitter/twitter.jade | 2 +- src/client/site/common/twitter/twitter.scss | 2 +- src/client/site/common/video/video.jade | 2 +- src/client/site/common/video/video.scss | 2 +- src/client/site/common/video/video_controller.coffee | 2 +- src/client/site/craft_page/craft_page.jade | 2 +- src/client/site/craft_page/craft_page.scss | 2 +- src/client/site/craft_page/craft_page_controller.coffee | 2 +- .../site/craft_page/craftsman_working/craftsman_working.jade | 2 +- .../craftsman_working/craftsman_working_controller.coffee | 2 +- src/client/site/craft_page/step/step.jade | 2 +- src/client/site/craft_page/step/step.scss | 2 +- src/client/site/craft_page/step/step_controller.coffee | 2 +- src/client/site/feedback/feedback.jade | 2 +- src/client/site/feedback/feedback.scss | 2 +- src/client/site/feedback/feedback_controller.coffee | 2 +- src/client/site/footer/footer.jade | 2 +- src/client/site/footer/footer.scss | 2 +- src/client/site/footer/footer_controller.coffee | 2 +- src/client/site/header/header.jade | 2 +- src/client/site/header/header.scss | 2 +- src/client/site/header/header_controller.coffee | 2 +- src/client/site/image_loader.coffee | 2 +- src/client/site/index.jade | 2 +- src/client/site/index.scss | 2 +- src/client/site/item_page/item_page.jade | 2 +- src/client/site/item_page/item_page.scss | 2 +- src/client/site/item_page/item_page_controller.coffee | 2 +- .../site/item_page/multiblock_viewer/multiblock/multiblock.scss | 2 +- .../site/item_page/multiblock_viewer/multiblock_viewer.scss | 2 +- .../multiblock_viewer/multiblock_viewer_controller.coffee | 2 +- src/client/site/item_page/recipe_detail/recipe_detail.jade | 2 +- src/client/site/item_page/recipe_detail/recipe_detail.scss | 2 +- .../item_page/recipe_detail/recipe_detail_controller.coffee | 2 +- src/client/site/login_page/login_page.jade | 2 +- src/client/site/login_page/login_page.scss | 2 +- src/client/site/login_page/login_page_controller.coffee | 2 +- src/client/site/mod_page/mod_page.jade | 2 +- src/client/site/mod_page/mod_page.scss | 2 +- src/client/site/mod_page/mod_page_controller.coffee | 2 +- src/client/site/mod_page/tutorial/tutorial.jade | 2 +- src/client/site/mod_page/tutorial/tutorial.scss | 2 +- src/client/site/mod_page/tutorial/tutorial_controller.coffee | 2 +- src/client/site/news_page/news_page.jade | 2 +- src/client/site/news_page/news_page.scss | 2 +- src/client/site/news_page/news_page_controller.coffee | 2 +- src/client/site/page_controller.coffee | 2 +- src/client/site/router.coffee | 2 +- src/client/site/site_controller.coffee | 2 +- src/client/site/tutorial_page/tutorial_page.jade | 2 +- src/client/site/tutorial_page/tutorial_page.scss | 2 +- src/client/site/tutorial_page/tutorial_page_controller.coffee | 2 +- src/client/site/url_params.coffee | 2 +- src/client/storage.coffee | 2 +- src/client/styles/classes.scss | 2 +- src/client/styles/colors.scss | 2 +- src/client/styles/fonts.scss | 2 +- src/client/styles/layers.scss | 2 +- src/client/styles/main.scss | 2 +- src/client/styles/markdown.scss | 2 +- src/client/styles/media.scss | 2 +- src/client/styles/mixins.scss | 2 +- src/client/styles/sizes.scss | 2 +- src/client/styles/tags.scss | 2 +- src/client/tracker.coffee | 2 +- src/common/constants.coffee | 2 +- src/common/fs.extensions.coffee | 2 +- src/common/fs.extensions.test.coffee | 2 +- src/common/underscore.coffee | 2 +- src/index.coffee | 2 +- src/server/crafting_guide_server.coffee | 2 +- src/server/middleware.coffee | 2 +- src/server/routes.coffee | 2 +- src/server/server.coffee | 2 +- 139 files changed, 139 insertions(+), 139 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 1f09793e0..28e6376ac 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - Gruntfile.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/client.coffee b/src/client/client.coffee index 2b417c1a9..9c7bbc064 100644 --- a/src/client/client.coffee +++ b/src/client/client.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - client.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/includes/_google_analytics.jade b/src/client/includes/_google_analytics.jade index 4860d2583..b76732e2a 100644 --- a/src/client/includes/_google_analytics.jade +++ b/src/client/includes/_google_analytics.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - _google_analytics.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/includes/_google_sitelinks.jade b/src/client/includes/_google_sitelinks.jade index dfd41d517..3b79118e2 100644 --- a/src/client/includes/_google_sitelinks.jade +++ b/src/client/includes/_google_sitelinks.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - _google_sitelinks.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/models/site/craft_page.coffee b/src/client/models/site/craft_page.coffee index 712a53ff8..4f2325d41 100644 --- a/src/client/models/site/craft_page.coffee +++ b/src/client/models/site/craft_page.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - craft_page.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/models/site/editable_file.coffee b/src/client/models/site/editable_file.coffee index 7c05503ed..de6963563 100644 --- a/src/client/models/site/editable_file.coffee +++ b/src/client/models/site/editable_file.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - editable_file.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/models/site/email_client.coffee b/src/client/models/site/email_client.coffee index f8a17c889..599ca6a7d 100644 --- a/src/client/models/site/email_client.coffee +++ b/src/client/models/site/email_client.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - email_client.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/models/site/file_cache.coffee b/src/client/models/site/file_cache.coffee index 521d71559..97d5eeae6 100644 --- a/src/client/models/site/file_cache.coffee +++ b/src/client/models/site/file_cache.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - file_cache.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/models/site/file_cache.test.coffee b/src/client/models/site/file_cache.test.coffee index 2fef672e7..46cf086a8 100644 --- a/src/client/models/site/file_cache.test.coffee +++ b/src/client/models/site/file_cache.test.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - file_cache.test.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/models/site/github_user.coffee b/src/client/models/site/github_user.coffee index 35f718fad..ee4c8aab1 100644 --- a/src/client/models/site/github_user.coffee +++ b/src/client/models/site/github_user.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - github_user.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/models/site/item_page.coffee b/src/client/models/site/item_page.coffee index 052e4768a..c0a812bd8 100644 --- a/src/client/models/site/item_page.coffee +++ b/src/client/models/site/item_page.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - item_page.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/models/site/item_selector.coffee b/src/client/models/site/item_selector.coffee index 3c543d7bf..b63b2cc97 100644 --- a/src/client/models/site/item_selector.coffee +++ b/src/client/models/site/item_selector.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - item_selector.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/models/site/markdown_image.coffee b/src/client/models/site/markdown_image.coffee index 0860d7a21..d72ccd082 100644 --- a/src/client/models/site/markdown_image.coffee +++ b/src/client/models/site/markdown_image.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - markdown_image.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/models/site/markdown_image_list.coffee b/src/client/models/site/markdown_image_list.coffee index acac909fb..42821fad3 100644 --- a/src/client/models/site/markdown_image_list.coffee +++ b/src/client/models/site/markdown_image_list.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - markdown_image_list.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/models/site/markdown_image_list.test.coffee b/src/client/models/site/markdown_image_list.test.coffee index ed31a6dad..58e458766 100644 --- a/src/client/models/site/markdown_image_list.test.coffee +++ b/src/client/models/site/markdown_image_list.test.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - markdown_image_list.test.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/models/stores/mod_pack_store.coffee b/src/client/models/stores/mod_pack_store.coffee index 5c8bcf947..8184c3e3d 100644 --- a/src/client/models/stores/mod_pack_store.coffee +++ b/src/client/models/stores/mod_pack_store.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - mod_pack_store.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/base_controller.coffee b/src/client/site/base_controller.coffee index aa23f96c7..21564d860 100644 --- a/src/client/site/base_controller.coffee +++ b/src/client/site/base_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - base_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/browse_page/browse_page.jade b/src/client/site/browse_page/browse_page.jade index 6e13350ef..f93f60083 100644 --- a/src/client/site/browse_page/browse_page.jade +++ b/src/client/site/browse_page/browse_page.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - browse_page.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/browse_page/browse_page.scss b/src/client/site/browse_page/browse_page.scss index a9554ea0f..d85841c64 100644 --- a/src/client/site/browse_page/browse_page.scss +++ b/src/client/site/browse_page/browse_page.scss @@ -1,7 +1,7 @@ // // Crafting Guide - browse_page.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/browse_page/browse_page_controller.coffee b/src/client/site/browse_page/browse_page_controller.coffee index b1972a414..7baa67088 100644 --- a/src/client/site/browse_page/browse_page_controller.coffee +++ b/src/client/site/browse_page/browse_page_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - browse_page_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot.jade b/src/client/site/browse_page/mod_ballot/mod_ballot.jade index 1266986e2..1315ee2db 100644 --- a/src/client/site/browse_page/mod_ballot/mod_ballot.jade +++ b/src/client/site/browse_page/mod_ballot/mod_ballot.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - mod_ballot.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot.scss b/src/client/site/browse_page/mod_ballot/mod_ballot.scss index c6bb5793a..bf0c57303 100644 --- a/src/client/site/browse_page/mod_ballot/mod_ballot.scss +++ b/src/client/site/browse_page/mod_ballot/mod_ballot.scss @@ -1,7 +1,7 @@ // // Crafting Guide - mod_ballot.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot_controller.coffee b/src/client/site/browse_page/mod_ballot/mod_ballot_controller.coffee index b3007b69d..cf3d42980 100644 --- a/src/client/site/browse_page/mod_ballot/mod_ballot_controller.coffee +++ b/src/client/site/browse_page/mod_ballot/mod_ballot_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - mod_ballot_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.jade b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.jade index b3b06f7aa..5b8bd657c 100644 --- a/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.jade +++ b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - mod_ballot_line.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.scss b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.scss index e686260ea..effe63aab 100644 --- a/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.scss +++ b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.scss @@ -1,7 +1,7 @@ // // Crafting Guide - mod_ballot_line.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line_controller.coffee b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line_controller.coffee index 6acf670fd..9383e690e 100644 --- a/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line_controller.coffee +++ b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - mod_ballot_line_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.jade b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.jade index 1ad427d3f..44d618d8e 100644 --- a/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.jade +++ b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - suggest_mod_panel.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.scss b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.scss index 9a33b386a..a4b31d435 100644 --- a/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.scss +++ b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.scss @@ -1,7 +1,7 @@ // // Crafting Guide - suggest_mod_panel.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel_controller.coffee b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel_controller.coffee index bc096947d..d5705a5d6 100644 --- a/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel_controller.coffee +++ b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - suggest_mod_panel_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/browse_page/mod_tile/mod_tile.jade b/src/client/site/browse_page/mod_tile/mod_tile.jade index aa0138d6a..fa5b60c66 100644 --- a/src/client/site/browse_page/mod_tile/mod_tile.jade +++ b/src/client/site/browse_page/mod_tile/mod_tile.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - mod_tile.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/browse_page/mod_tile/mod_tile.scss b/src/client/site/browse_page/mod_tile/mod_tile.scss index c25808bee..04d4bb4f4 100644 --- a/src/client/site/browse_page/mod_tile/mod_tile.scss +++ b/src/client/site/browse_page/mod_tile/mod_tile.scss @@ -1,7 +1,7 @@ // // Crafting Guide - mod_tile.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/browse_page/mod_tile/mod_tile_controller.coffee b/src/client/site/browse_page/mod_tile/mod_tile_controller.coffee index 25c7e6e04..15fa0802d 100644 --- a/src/client/site/browse_page/mod_tile/mod_tile_controller.coffee +++ b/src/client/site/browse_page/mod_tile/mod_tile_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - mod_tile_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/adsense/adsense.jade b/src/client/site/common/adsense/adsense.jade index e9de92cb9..75a942a79 100644 --- a/src/client/site/common/adsense/adsense.jade +++ b/src/client/site/common/adsense/adsense.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - adsense.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/common/adsense/adsense.scss b/src/client/site/common/adsense/adsense.scss index 49dc8a7d6..b8b38d4e3 100644 --- a/src/client/site/common/adsense/adsense.scss +++ b/src/client/site/common/adsense/adsense.scss @@ -1,7 +1,7 @@ // // Crafting Guide - adsense.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/adsense/adsense_controller.coffee b/src/client/site/common/adsense/adsense_controller.coffee index 90d1fb6c8..b2bf38f0b 100644 --- a/src/client/site/common/adsense/adsense_controller.coffee +++ b/src/client/site/common/adsense/adsense_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - adsense_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/crafting_grid/crafting_grid.scss b/src/client/site/common/crafting_grid/crafting_grid.scss index 67d36fb5c..a7aeeb334 100644 --- a/src/client/site/common/crafting_grid/crafting_grid.scss +++ b/src/client/site/common/crafting_grid/crafting_grid.scss @@ -1,7 +1,7 @@ // // Crafting Guide - crafting_grid.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/crafting_grid/crafting_grid_controller.coffee b/src/client/site/common/crafting_grid/crafting_grid_controller.coffee index 48c2a2edc..ab87389b9 100644 --- a/src/client/site/common/crafting_grid/crafting_grid_controller.coffee +++ b/src/client/site/common/crafting_grid/crafting_grid_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - crafting_grid_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/inventory/inventory.jade b/src/client/site/common/inventory/inventory.jade index 33d2792e7..6d3219184 100644 --- a/src/client/site/common/inventory/inventory.jade +++ b/src/client/site/common/inventory/inventory.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - inventory.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/common/inventory/inventory.scss b/src/client/site/common/inventory/inventory.scss index dd3b52737..8d20e8e81 100644 --- a/src/client/site/common/inventory/inventory.scss +++ b/src/client/site/common/inventory/inventory.scss @@ -1,7 +1,7 @@ // // Crafting Guide - inventory.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/inventory/inventory_controller.coffee b/src/client/site/common/inventory/inventory_controller.coffee index d8852f809..9e2e4fe11 100644 --- a/src/client/site/common/inventory/inventory_controller.coffee +++ b/src/client/site/common/inventory/inventory_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - inventory_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/item_group/item_group.scss b/src/client/site/common/item_group/item_group.scss index 4f001a8fe..705933256 100644 --- a/src/client/site/common/item_group/item_group.scss +++ b/src/client/site/common/item_group/item_group.scss @@ -1,7 +1,7 @@ // // Crafting Guide - item_group.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/item_group/item_group_controller.coffee b/src/client/site/common/item_group/item_group_controller.coffee index 358cc6e89..d7482f241 100644 --- a/src/client/site/common/item_group/item_group_controller.coffee +++ b/src/client/site/common/item_group/item_group_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - item_group_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/item_group/item_tile/item_tile.scss b/src/client/site/common/item_group/item_tile/item_tile.scss index 492511137..24f30f3f2 100644 --- a/src/client/site/common/item_group/item_tile/item_tile.scss +++ b/src/client/site/common/item_group/item_tile/item_tile.scss @@ -1,7 +1,7 @@ // // Crafting Guide - item_tile.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/item_group/item_tile/item_tile_controller.coffee b/src/client/site/common/item_group/item_tile/item_tile_controller.coffee index 226455f1f..ad867f32e 100644 --- a/src/client/site/common/item_group/item_tile/item_tile_controller.coffee +++ b/src/client/site/common/item_group/item_tile/item_tile_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - item_group_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/item_selector/element/element.jade b/src/client/site/common/item_selector/element/element.jade index a51521f10..1081b781c 100644 --- a/src/client/site/common/item_selector/element/element.jade +++ b/src/client/site/common/item_selector/element/element.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - element.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/common/item_selector/element/element.scss b/src/client/site/common/item_selector/element/element.scss index f768a6394..5fc0fd031 100644 --- a/src/client/site/common/item_selector/element/element.scss +++ b/src/client/site/common/item_selector/element/element.scss @@ -1,7 +1,7 @@ // // Crafting Guide - element.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/item_selector/element/element_controller.coffee b/src/client/site/common/item_selector/element/element_controller.coffee index 04560efc8..c1ccbe108 100644 --- a/src/client/site/common/item_selector/element/element_controller.coffee +++ b/src/client/site/common/item_selector/element/element_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - element.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/item_selector/item_selector.jade b/src/client/site/common/item_selector/item_selector.jade index 828756733..4446f0c64 100644 --- a/src/client/site/common/item_selector/item_selector.jade +++ b/src/client/site/common/item_selector/item_selector.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - item_selector.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/common/item_selector/item_selector.scss b/src/client/site/common/item_selector/item_selector.scss index b4e391520..30193e5c6 100644 --- a/src/client/site/common/item_selector/item_selector.scss +++ b/src/client/site/common/item_selector/item_selector.scss @@ -1,7 +1,7 @@ // // Crafting Guide - item_selector.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/item_selector/item_selector_controller.coffee b/src/client/site/common/item_selector/item_selector_controller.coffee index f5a5feeee..33c634a7a 100644 --- a/src/client/site/common/item_selector/item_selector_controller.coffee +++ b/src/client/site/common/item_selector/item_selector_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - item_selector_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/markdown_section/markdown_image_list/markdown_image/markdown_image.jade b/src/client/site/common/markdown_section/markdown_image_list/markdown_image/markdown_image.jade index c97cbd8bd..1cec65dba 100644 --- a/src/client/site/common/markdown_section/markdown_image_list/markdown_image/markdown_image.jade +++ b/src/client/site/common/markdown_section/markdown_image_list/markdown_image/markdown_image.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - markdown_image.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/common/markdown_section/markdown_image_list/markdown_image/markdown_image.scss b/src/client/site/common/markdown_section/markdown_image_list/markdown_image/markdown_image.scss index e423b103c..c98afabe8 100644 --- a/src/client/site/common/markdown_section/markdown_image_list/markdown_image/markdown_image.scss +++ b/src/client/site/common/markdown_section/markdown_image_list/markdown_image/markdown_image.scss @@ -1,7 +1,7 @@ // // Crafting Guide - markdown_image.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list.jade b/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list.jade index b660423db..84dba9371 100644 --- a/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list.jade +++ b/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - markdown_image_list.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list.scss b/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list.scss index 2396e5745..df0cb0da5 100644 --- a/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list.scss +++ b/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list.scss @@ -1,7 +1,7 @@ // // Crafting Guide - markdown_image_list.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list_controller.coffee b/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list_controller.coffee index 235931f4e..81db1a92d 100644 --- a/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list_controller.coffee +++ b/src/client/site/common/markdown_section/markdown_image_list/markdown_image_list_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - markdown_image_list_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/markdown_section/markdown_section.jade b/src/client/site/common/markdown_section/markdown_section.jade index 036221930..6ae6fdedf 100644 --- a/src/client/site/common/markdown_section/markdown_section.jade +++ b/src/client/site/common/markdown_section/markdown_section.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - markdown_section.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/common/markdown_section/markdown_section.scss b/src/client/site/common/markdown_section/markdown_section.scss index 45417f014..872238f92 100644 --- a/src/client/site/common/markdown_section/markdown_section.scss +++ b/src/client/site/common/markdown_section/markdown_section.scss @@ -1,7 +1,7 @@ // // Crafting Guide - markdown_section.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/markdown_section/markdown_section_controller.coffee b/src/client/site/common/markdown_section/markdown_section_controller.coffee index c9c9c159a..fe758d45b 100644 --- a/src/client/site/common/markdown_section/markdown_section_controller.coffee +++ b/src/client/site/common/markdown_section/markdown_section_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - markdown_section_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/recipe/recipe.scss b/src/client/site/common/recipe/recipe.scss index fd89eb8b5..5d9bf6d57 100644 --- a/src/client/site/common/recipe/recipe.scss +++ b/src/client/site/common/recipe/recipe.scss @@ -1,7 +1,7 @@ // // Crafting Guide - recipe.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/recipe/recipe_controller.coffee b/src/client/site/common/recipe/recipe_controller.coffee index e1b82e99b..9089af172 100644 --- a/src/client/site/common/recipe/recipe_controller.coffee +++ b/src/client/site/common/recipe/recipe_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - recipe_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/slot/slot.scss b/src/client/site/common/slot/slot.scss index 61f853777..eb7c2905e 100644 --- a/src/client/site/common/slot/slot.scss +++ b/src/client/site/common/slot/slot.scss @@ -1,7 +1,7 @@ // // Crafting Guide - slot.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/slot/slot_controller.coffee b/src/client/site/common/slot/slot_controller.coffee index d6d02f90f..ecde0b23d 100644 --- a/src/client/site/common/slot/slot_controller.coffee +++ b/src/client/site/common/slot/slot_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - slot_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/stack/stack.jade b/src/client/site/common/stack/stack.jade index 235680654..5de156581 100644 --- a/src/client/site/common/stack/stack.jade +++ b/src/client/site/common/stack/stack.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - stack.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/common/stack/stack.scss b/src/client/site/common/stack/stack.scss index 4bf82ed69..454a14a78 100644 --- a/src/client/site/common/stack/stack.scss +++ b/src/client/site/common/stack/stack.scss @@ -1,7 +1,7 @@ // // Crafting Guide - stack.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/stack/stack_controller.coffee b/src/client/site/common/stack/stack_controller.coffee index 74b6cf105..4e939ec36 100644 --- a/src/client/site/common/stack/stack_controller.coffee +++ b/src/client/site/common/stack/stack_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - stack_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/common/twitter/twitter.jade b/src/client/site/common/twitter/twitter.jade index ed8ac48db..76dbda843 100644 --- a/src/client/site/common/twitter/twitter.jade +++ b/src/client/site/common/twitter/twitter.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - twitter.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/common/twitter/twitter.scss b/src/client/site/common/twitter/twitter.scss index 39020fe52..ae8cf2c97 100644 --- a/src/client/site/common/twitter/twitter.scss +++ b/src/client/site/common/twitter/twitter.scss @@ -1,7 +1,7 @@ // // Crafting Guide - news_page.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/video/video.jade b/src/client/site/common/video/video.jade index 2a57537c2..bc6b1a16e 100644 --- a/src/client/site/common/video/video.jade +++ b/src/client/site/common/video/video.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - video.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/common/video/video.scss b/src/client/site/common/video/video.scss index 8fe922300..22564b12a 100644 --- a/src/client/site/common/video/video.scss +++ b/src/client/site/common/video/video.scss @@ -1,7 +1,7 @@ // // Crafting Guide - video.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/common/video/video_controller.coffee b/src/client/site/common/video/video_controller.coffee index 8c41cd8d5..f4f72bdab 100644 --- a/src/client/site/common/video/video_controller.coffee +++ b/src/client/site/common/video/video_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - video_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/craft_page/craft_page.jade b/src/client/site/craft_page/craft_page.jade index b7c14052d..2ad49735c 100644 --- a/src/client/site/craft_page/craft_page.jade +++ b/src/client/site/craft_page/craft_page.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - craft_page.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/craft_page/craft_page.scss b/src/client/site/craft_page/craft_page.scss index c570d8534..be36a9b03 100644 --- a/src/client/site/craft_page/craft_page.scss +++ b/src/client/site/craft_page/craft_page.scss @@ -1,7 +1,7 @@ // // Crafting Guide - craft_page.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/craft_page/craft_page_controller.coffee b/src/client/site/craft_page/craft_page_controller.coffee index 05b79758a..48467c18c 100644 --- a/src/client/site/craft_page/craft_page_controller.coffee +++ b/src/client/site/craft_page/craft_page_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - craft_page_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/craft_page/craftsman_working/craftsman_working.jade b/src/client/site/craft_page/craftsman_working/craftsman_working.jade index 44c252e4b..2afbf14ce 100644 --- a/src/client/site/craft_page/craftsman_working/craftsman_working.jade +++ b/src/client/site/craft_page/craftsman_working/craftsman_working.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - craftsman_working.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/craft_page/craftsman_working/craftsman_working_controller.coffee b/src/client/site/craft_page/craftsman_working/craftsman_working_controller.coffee index 934854582..f2e214538 100644 --- a/src/client/site/craft_page/craftsman_working/craftsman_working_controller.coffee +++ b/src/client/site/craft_page/craftsman_working/craftsman_working_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - craftsman_working_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/craft_page/step/step.jade b/src/client/site/craft_page/step/step.jade index 89a2fbdc0..913cb6ef9 100644 --- a/src/client/site/craft_page/step/step.jade +++ b/src/client/site/craft_page/step/step.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - step.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/craft_page/step/step.scss b/src/client/site/craft_page/step/step.scss index 7952dff2c..c79068001 100644 --- a/src/client/site/craft_page/step/step.scss +++ b/src/client/site/craft_page/step/step.scss @@ -1,7 +1,7 @@ // // Crafting Guide - step.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/craft_page/step/step_controller.coffee b/src/client/site/craft_page/step/step_controller.coffee index cf6df7d74..eef7e330e 100644 --- a/src/client/site/craft_page/step/step_controller.coffee +++ b/src/client/site/craft_page/step/step_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - step_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/feedback/feedback.jade b/src/client/site/feedback/feedback.jade index 93f10d0c3..e3a90bcf0 100644 --- a/src/client/site/feedback/feedback.jade +++ b/src/client/site/feedback/feedback.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - feedback.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/feedback/feedback.scss b/src/client/site/feedback/feedback.scss index acab624d1..18c3a13b0 100644 --- a/src/client/site/feedback/feedback.scss +++ b/src/client/site/feedback/feedback.scss @@ -1,7 +1,7 @@ // // Crafting Guide - feedback.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/feedback/feedback_controller.coffee b/src/client/site/feedback/feedback_controller.coffee index 70f8bcc6f..a8cb4eaf2 100644 --- a/src/client/site/feedback/feedback_controller.coffee +++ b/src/client/site/feedback/feedback_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - feedback_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/footer/footer.jade b/src/client/site/footer/footer.jade index aef3953c4..495828520 100644 --- a/src/client/site/footer/footer.jade +++ b/src/client/site/footer/footer.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - footer.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/footer/footer.scss b/src/client/site/footer/footer.scss index cf93c2252..f3856bc71 100644 --- a/src/client/site/footer/footer.scss +++ b/src/client/site/footer/footer.scss @@ -1,7 +1,7 @@ // // Crafting Guide - footer.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/footer/footer_controller.coffee b/src/client/site/footer/footer_controller.coffee index a1a39844f..ef13da5ce 100644 --- a/src/client/site/footer/footer_controller.coffee +++ b/src/client/site/footer/footer_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - footer_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/header/header.jade b/src/client/site/header/header.jade index 55a1329ac..70bc334f4 100644 --- a/src/client/site/header/header.jade +++ b/src/client/site/header/header.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - header.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/header/header.scss b/src/client/site/header/header.scss index d9b782a90..73773e1ae 100644 --- a/src/client/site/header/header.scss +++ b/src/client/site/header/header.scss @@ -1,7 +1,7 @@ // // Crafting Guide - header.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/header/header_controller.coffee b/src/client/site/header/header_controller.coffee index f08fad3e5..d00f9cf8c 100644 --- a/src/client/site/header/header_controller.coffee +++ b/src/client/site/header/header_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - header_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/image_loader.coffee b/src/client/site/image_loader.coffee index 9d99d69ef..8e0d2d77c 100644 --- a/src/client/site/image_loader.coffee +++ b/src/client/site/image_loader.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - image_loader.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/index.jade b/src/client/site/index.jade index 0ee4711ea..9c76ea7e2 100644 --- a/src/client/site/index.jade +++ b/src/client/site/index.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - index.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/index.scss b/src/client/site/index.scss index c150bfc35..7742b3cdd 100644 --- a/src/client/site/index.scss +++ b/src/client/site/index.scss @@ -1,7 +1,7 @@ // // Crafting Guide - index.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/item_page/item_page.jade b/src/client/site/item_page/item_page.jade index bd07f6ec1..afc42db4f 100644 --- a/src/client/site/item_page/item_page.jade +++ b/src/client/site/item_page/item_page.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - item_page.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/item_page/item_page.scss b/src/client/site/item_page/item_page.scss index cfe18adcc..4af4faa20 100644 --- a/src/client/site/item_page/item_page.scss +++ b/src/client/site/item_page/item_page.scss @@ -1,7 +1,7 @@ // // Crafting Guide - item_page.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/item_page/item_page_controller.coffee b/src/client/site/item_page/item_page_controller.coffee index ea811d61e..3012c864b 100644 --- a/src/client/site/item_page/item_page_controller.coffee +++ b/src/client/site/item_page/item_page_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - item_page_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/item_page/multiblock_viewer/multiblock/multiblock.scss b/src/client/site/item_page/multiblock_viewer/multiblock/multiblock.scss index 08a1a5f2f..6c152d5ec 100644 --- a/src/client/site/item_page/multiblock_viewer/multiblock/multiblock.scss +++ b/src/client/site/item_page/multiblock_viewer/multiblock/multiblock.scss @@ -1,7 +1,7 @@ // // Crafting Guide - multiblock.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/item_page/multiblock_viewer/multiblock_viewer.scss b/src/client/site/item_page/multiblock_viewer/multiblock_viewer.scss index 7dc65466e..e136353d4 100644 --- a/src/client/site/item_page/multiblock_viewer/multiblock_viewer.scss +++ b/src/client/site/item_page/multiblock_viewer/multiblock_viewer.scss @@ -1,7 +1,7 @@ // // Crafting Guide - multiblock_viewer.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/item_page/multiblock_viewer/multiblock_viewer_controller.coffee b/src/client/site/item_page/multiblock_viewer/multiblock_viewer_controller.coffee index 0b8fc25a0..068118934 100644 --- a/src/client/site/item_page/multiblock_viewer/multiblock_viewer_controller.coffee +++ b/src/client/site/item_page/multiblock_viewer/multiblock_viewer_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - multiblock_viewer_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/item_page/recipe_detail/recipe_detail.jade b/src/client/site/item_page/recipe_detail/recipe_detail.jade index 15a508c86..1b2ceb1c1 100644 --- a/src/client/site/item_page/recipe_detail/recipe_detail.jade +++ b/src/client/site/item_page/recipe_detail/recipe_detail.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - recipe_detail.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/item_page/recipe_detail/recipe_detail.scss b/src/client/site/item_page/recipe_detail/recipe_detail.scss index 127d38c92..f644a0452 100644 --- a/src/client/site/item_page/recipe_detail/recipe_detail.scss +++ b/src/client/site/item_page/recipe_detail/recipe_detail.scss @@ -1,7 +1,7 @@ // // Crafting Guide - recipe_detail.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/item_page/recipe_detail/recipe_detail_controller.coffee b/src/client/site/item_page/recipe_detail/recipe_detail_controller.coffee index 16a3bb99f..bcf81d47e 100644 --- a/src/client/site/item_page/recipe_detail/recipe_detail_controller.coffee +++ b/src/client/site/item_page/recipe_detail/recipe_detail_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - recipe_detail_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/login_page/login_page.jade b/src/client/site/login_page/login_page.jade index 0989023e8..998425307 100644 --- a/src/client/site/login_page/login_page.jade +++ b/src/client/site/login_page/login_page.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - login_page.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/login_page/login_page.scss b/src/client/site/login_page/login_page.scss index 7c4001177..f8727d1ab 100644 --- a/src/client/site/login_page/login_page.scss +++ b/src/client/site/login_page/login_page.scss @@ -1,7 +1,7 @@ // // Crafting Guide - login_page.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/login_page/login_page_controller.coffee b/src/client/site/login_page/login_page_controller.coffee index 8256258f2..a6c837d80 100644 --- a/src/client/site/login_page/login_page_controller.coffee +++ b/src/client/site/login_page/login_page_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - login_page_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/mod_page/mod_page.jade b/src/client/site/mod_page/mod_page.jade index 7faf1a0ea..1a2f5a6ed 100644 --- a/src/client/site/mod_page/mod_page.jade +++ b/src/client/site/mod_page/mod_page.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - item_page.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/mod_page/mod_page.scss b/src/client/site/mod_page/mod_page.scss index 0f059d5ab..2748545f4 100644 --- a/src/client/site/mod_page/mod_page.scss +++ b/src/client/site/mod_page/mod_page.scss @@ -1,7 +1,7 @@ // // Crafting Guide - item_page.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/mod_page/mod_page_controller.coffee b/src/client/site/mod_page/mod_page_controller.coffee index 1daf88343..570755bb8 100644 --- a/src/client/site/mod_page/mod_page_controller.coffee +++ b/src/client/site/mod_page/mod_page_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - mod_page_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/mod_page/tutorial/tutorial.jade b/src/client/site/mod_page/tutorial/tutorial.jade index 1e926fe51..ed21ba0d1 100644 --- a/src/client/site/mod_page/tutorial/tutorial.jade +++ b/src/client/site/mod_page/tutorial/tutorial.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - tutorial.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/mod_page/tutorial/tutorial.scss b/src/client/site/mod_page/tutorial/tutorial.scss index 91bd47610..16ed3fd93 100644 --- a/src/client/site/mod_page/tutorial/tutorial.scss +++ b/src/client/site/mod_page/tutorial/tutorial.scss @@ -1,7 +1,7 @@ // // Crafting Guide - tutorial.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/mod_page/tutorial/tutorial_controller.coffee b/src/client/site/mod_page/tutorial/tutorial_controller.coffee index 583ce6a5e..0cd70d884 100644 --- a/src/client/site/mod_page/tutorial/tutorial_controller.coffee +++ b/src/client/site/mod_page/tutorial/tutorial_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - tutorial_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/news_page/news_page.jade b/src/client/site/news_page/news_page.jade index 7f0632655..1c82967d8 100644 --- a/src/client/site/news_page/news_page.jade +++ b/src/client/site/news_page/news_page.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - news_page.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/news_page/news_page.scss b/src/client/site/news_page/news_page.scss index 55d653c60..6d0d647f8 100644 --- a/src/client/site/news_page/news_page.scss +++ b/src/client/site/news_page/news_page.scss @@ -1,7 +1,7 @@ // // Crafting Guide - news_page.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/news_page/news_page_controller.coffee b/src/client/site/news_page/news_page_controller.coffee index 64980a4a1..076e9f94d 100644 --- a/src/client/site/news_page/news_page_controller.coffee +++ b/src/client/site/news_page/news_page_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - news_page_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/page_controller.coffee b/src/client/site/page_controller.coffee index 593e18a9b..19a2cd69f 100644 --- a/src/client/site/page_controller.coffee +++ b/src/client/site/page_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - page_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/router.coffee b/src/client/site/router.coffee index e4b047710..bec2bf4de 100644 --- a/src/client/site/router.coffee +++ b/src/client/site/router.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - router.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/site_controller.coffee b/src/client/site/site_controller.coffee index 13f050d04..f44ef194b 100644 --- a/src/client/site/site_controller.coffee +++ b/src/client/site/site_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - site_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/tutorial_page/tutorial_page.jade b/src/client/site/tutorial_page/tutorial_page.jade index bbc60b9b5..69f692602 100644 --- a/src/client/site/tutorial_page/tutorial_page.jade +++ b/src/client/site/tutorial_page/tutorial_page.jade @@ -1,7 +1,7 @@ //- //- Crafting Guide - tutorial_page.jade //- -//- Copyright © 2014-2016 by Redwood Labs +//- Copyright © 2014-2017 by Redwood Labs //- All rights reserved. //- diff --git a/src/client/site/tutorial_page/tutorial_page.scss b/src/client/site/tutorial_page/tutorial_page.scss index ecdf17218..a3c0e7769 100644 --- a/src/client/site/tutorial_page/tutorial_page.scss +++ b/src/client/site/tutorial_page/tutorial_page.scss @@ -1,7 +1,7 @@ // // Crafting Guide - tutorial_page.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/site/tutorial_page/tutorial_page_controller.coffee b/src/client/site/tutorial_page/tutorial_page_controller.coffee index 311df9cbe..8bdf5bdda 100644 --- a/src/client/site/tutorial_page/tutorial_page_controller.coffee +++ b/src/client/site/tutorial_page/tutorial_page_controller.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - tutorial_page_controller.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/site/url_params.coffee b/src/client/site/url_params.coffee index 3b798889e..691f02c8b 100644 --- a/src/client/site/url_params.coffee +++ b/src/client/site/url_params.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - url_params.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/storage.coffee b/src/client/storage.coffee index 092a123bb..dd35eb1af 100644 --- a/src/client/storage.coffee +++ b/src/client/storage.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - storage.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/client/styles/classes.scss b/src/client/styles/classes.scss index 47db623e5..7aece700e 100644 --- a/src/client/styles/classes.scss +++ b/src/client/styles/classes.scss @@ -1,7 +1,7 @@ // // Crafting Guide - classes.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/styles/colors.scss b/src/client/styles/colors.scss index a8536559c..5d572f795 100644 --- a/src/client/styles/colors.scss +++ b/src/client/styles/colors.scss @@ -1,7 +1,7 @@ // // Crafting Guide - colors.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/styles/fonts.scss b/src/client/styles/fonts.scss index 141400218..6410c14f2 100644 --- a/src/client/styles/fonts.scss +++ b/src/client/styles/fonts.scss @@ -1,7 +1,7 @@ // // Crafting Guide - fonts.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/styles/layers.scss b/src/client/styles/layers.scss index c7c9d1e34..de2074674 100644 --- a/src/client/styles/layers.scss +++ b/src/client/styles/layers.scss @@ -1,7 +1,7 @@ // // Crafting Guide - layers.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/styles/main.scss b/src/client/styles/main.scss index 28b45e52a..af3904aca 100644 --- a/src/client/styles/main.scss +++ b/src/client/styles/main.scss @@ -1,7 +1,7 @@ // // Crafting Guide - main.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/styles/markdown.scss b/src/client/styles/markdown.scss index 96d405be9..730b905a4 100644 --- a/src/client/styles/markdown.scss +++ b/src/client/styles/markdown.scss @@ -1,7 +1,7 @@ // // Crafting Guide - markdown.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/styles/media.scss b/src/client/styles/media.scss index 677dc04b1..458d07356 100644 --- a/src/client/styles/media.scss +++ b/src/client/styles/media.scss @@ -1,7 +1,7 @@ // // Crafting Guide - fonts.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/styles/mixins.scss b/src/client/styles/mixins.scss index a1d74a23e..b37ae0ac8 100644 --- a/src/client/styles/mixins.scss +++ b/src/client/styles/mixins.scss @@ -1,7 +1,7 @@ // // Crafting Guide - mixins.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/styles/sizes.scss b/src/client/styles/sizes.scss index 5a16e4d81..748e8bd1c 100644 --- a/src/client/styles/sizes.scss +++ b/src/client/styles/sizes.scss @@ -1,7 +1,7 @@ // // Crafting Guide - sizes.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/styles/tags.scss b/src/client/styles/tags.scss index 9db57335f..ff35093cd 100644 --- a/src/client/styles/tags.scss +++ b/src/client/styles/tags.scss @@ -1,7 +1,7 @@ // // Crafting Guide - tags.scss // -// Copyright © 2014-2016 by Redwood Labs +// Copyright © 2014-2017 by Redwood Labs // All rights reserved. // diff --git a/src/client/tracker.coffee b/src/client/tracker.coffee index 2ad3edbe7..d480ddbac 100644 --- a/src/client/tracker.coffee +++ b/src/client/tracker.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - tracker.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/common/constants.coffee b/src/common/constants.coffee index b059663fe..9ba786139 100644 --- a/src/common/constants.coffee +++ b/src/common/constants.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - constants.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/common/fs.extensions.coffee b/src/common/fs.extensions.coffee index 602e6dada..422d8a513 100644 --- a/src/common/fs.extensions.coffee +++ b/src/common/fs.extensions.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - fs.extensions.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/common/fs.extensions.test.coffee b/src/common/fs.extensions.test.coffee index ed4e0e2ee..fd70772b1 100644 --- a/src/common/fs.extensions.test.coffee +++ b/src/common/fs.extensions.test.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - fs.extensions.test.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/common/underscore.coffee b/src/common/underscore.coffee index 776b89d24..8e9e07349 100644 --- a/src/common/underscore.coffee +++ b/src/common/underscore.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - underscore.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/index.coffee b/src/index.coffee index 8e32e472b..9c9920c33 100644 --- a/src/index.coffee +++ b/src/index.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - client.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/server/crafting_guide_server.coffee b/src/server/crafting_guide_server.coffee index 3459e7fac..3b8cd6423 100644 --- a/src/server/crafting_guide_server.coffee +++ b/src/server/crafting_guide_server.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - crafting_guide_server.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/server/middleware.coffee b/src/server/middleware.coffee index 216ce1083..200ec9ad1 100644 --- a/src/server/middleware.coffee +++ b/src/server/middleware.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - middleware.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/server/routes.coffee b/src/server/routes.coffee index d49699730..51ae87f21 100644 --- a/src/server/routes.coffee +++ b/src/server/routes.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - routes.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. # diff --git a/src/server/server.coffee b/src/server/server.coffee index 0cf899533..4c18a2478 100644 --- a/src/server/server.coffee +++ b/src/server/server.coffee @@ -1,7 +1,7 @@ # # Crafting Guide - server.coffee # -# Copyright © 2014-2016 by Redwood Labs +# Copyright © 2014-2017 by Redwood Labs # All rights reserved. #