Add back the "include tools" option

* Rewrite the crafting algorithm to recursively find all the steps
  involved and then resolve how many times each needs to be run.
* Update the base controller to debounce calls to refresh
* Fix a few erroneous recipes for IC2-Classic
This commit is contained in:
Andrew Miner
2015-01-11 16:01:51 -08:00
parent 16f17f62a1
commit a784f275ff
8 changed files with 125 additions and 98 deletions
+5
View File
@@ -20,4 +20,9 @@ All rights reserved.
.view__crafting_table { width: 50%; height: 26em; } .view__crafting_table { width: 50%; height: 26em; }
.view__mod_pack { margin-top: 2em; width: 100%; } .view__mod_pack { margin-top: 2em; width: 100%; }
.want label {
position: absolute; top: 50%; left: 0.5em;
@include transform(translateY(-50%));
}
} }
+2 -2
View File
@@ -436,7 +436,7 @@
"pattern": ".1. 020 .1." "pattern": ".1. 020 .1."
}, { }, {
"output": "Extractor", "output": "Extractor",
"input": [[4, "Tree Tap"], "Electronic Circuit", "Machine Block"], "input": [[4, "Treetap"], "Electronic Circuit", "Machine Block"],
"pattern": "... 020 010", "pattern": "... 020 010",
"tools": "Crafting Table" "tools": "Crafting Table"
}, { }, {
@@ -1108,7 +1108,7 @@
"tools": "Crafting Table" "tools": "Crafting Table"
}, { }, {
"output": "Wind Mill", "output": "Wind Mill",
"input": [[4, "Iron"], "Generator"], "input": [[4, "Iron Ingot"], "Generator"],
"pattern": "0.0 .1. 0.0", "pattern": "0.0 .1. 0.0",
"tools": "Crafting Table" "tools": "Crafting Table"
}, { }, {
@@ -23,6 +23,8 @@ module.exports = class BaseController extends Backbone.View
@_loadTemplate options.templateName @_loadTemplate options.templateName
super options super options
@_tryRefresh = _.debounce @_tryRefresh, 100
# Public Methods ############################################################################### # Public Methods ###############################################################################
addChild: (Controller, atSelector, options={})-> addChild: (Controller, atSelector, options={})->
@@ -22,6 +22,11 @@ module.exports = class ItemPageController extends BaseController
options.templateName = 'item_page' options.templateName = 'item_page'
super options super options
# Event Methods ################################################################################
onToolsBoxToggled: ->
@model.plan.includingTools = @$('.includeTools:checked').length isnt 0
# BaseController Overrides ##################################################################### # BaseController Overrides #####################################################################
onDidRender: -> onDidRender: ->
@@ -54,4 +59,19 @@ module.exports = class ItemPageController extends BaseController
modPack: @model.modPack modPack: @model.modPack
@modPackController = @addChild ModPackController, '.view__mod_pack', model:@model.modPack @modPackController = @addChild ModPackController, '.view__mod_pack', model:@model.modPack
@$('.want .toolbar').append '<label><input class="includeTools" type="checkbox"> include tools</label'
@$includeToolsBox = @$('.includeTools')
super super
refresh: ->
if @model.plan.includingTools
@$includeToolsBox.attr 'checked', 'checked'
else
@$includeToolsBox.removeAttr 'checked'
# Backbone.View Overrides ######################################################################
events:
'change .includeTools': 'onToolsBoxToggled'
+64 -56
View File
@@ -25,16 +25,14 @@ module.exports = class CraftingPlan extends BaseModel
@have.on 'change', => @craft() @have.on 'change', => @craft()
@want.on 'change', => @craft() @want.on 'change', => @craft()
@modPack.on 'change', => @craft() @modPack.on 'change', => @craft()
@on 'change:includingTools', => @craft()
@clear() @clear()
# Public Methods ############################################################################### # Public Methods ###############################################################################
clear: -> clear: ->
@steps = [] @steps = []
@_expected = new Inventory
@_pending = null
@need.clear() @need.clear()
@result.clear() @result.clear()
@@ -42,22 +40,18 @@ module.exports = class CraftingPlan extends BaseModel
return this return this
craft: -> craft: ->
logger.trace "starting crafting plan for: #{@want} starting with #{@have}"
@clear() @clear()
return if @want.isEmpty
@want.each (stack)=>
@_expected = @want.clone()
@_pending = @want.clone()
@result.addInventory @have @result.addInventory @have
@_need = new Inventory @steps = {}
while not @_pending.isEmpty @want.each (stack)=>
@_processPending() @_findSteps stack.itemSlug
@need.add stack.itemSlug, stack.quantity
@need.addInventory @_need @steps = _.values @steps
@steps.reverse() @_resolveNeeds()
@_removeExtraSteps()
@result.addInventory @want
@trigger 'change', this @trigger 'change', this
@@ -74,53 +68,67 @@ module.exports = class CraftingPlan extends BaseModel
# Private Methods ############################################################################## # Private Methods ##############################################################################
_processPending: -> _addStep: (recipe)->
targetStack = @_pending.pop() return if @_hasStep recipe.slug
targetItem = @modPack.findItem targetStack.itemSlug logger.verbose "adding step: #{recipe.slug}"
logger.verbose "processing item: #{targetStack}" @steps[recipe.slug] = recipe:recipe
return unless targetItem?
return if (not targetItem.isCraftable) or targetItem.isGatherable
recipe = targetItem.recipes[0] _chooseRecipe: (item)->
logger.verbose "recipe: #{recipe}" return item.recipes[0]
_findSteps: (itemSlug)->
item = @modPack.findItem itemSlug
return unless item?
return unless item.isCraftable
return if item.isGatherable
recipe = @_chooseRecipe item
if @includingTools if @includingTools
for toolStack in recipe.tools for toolStack in recipe.tools
slug = toolStack.itemSlug if not @_hasStep toolStack.itemSlug
totalExpected = @result.quantityOf(slug) + @_expected.quantityOf(slug) @_findSteps toolStack.itemSlug
if totalExpected < 1
@_pending.add slug
@_expected.add slug
while @_totalQuantityOf(targetItem.slug) < @_expected.quantityOf(targetItem.slug) for inputStack in recipe.input
@steps.push recipe @_findSteps inputStack.itemSlug
@_addStep recipe
_hasStep: (itemSlug)->
return @steps[itemSlug]?
_removeExtraSteps: ->
result = (step for step in @steps when step.multiplier > 0)
@steps = result
_resolveNeeds: ->
for i in [@steps.length-1..0] by -1
step = @steps[i]
recipe = step.recipe
step.multiplier = Math.ceil(@need.quantityOf(recipe.slug) / step.recipe.output[0].quantity)
if @includingTools
for stack in recipe.tools
slug = stack.itemSlug
available = @result.quantityOf(slug) + @need.quantityOf(slug)
needed = Math.max 0, stack.quantity - available
@need.add stack.itemSlug, needed
@result.add stack.itemSlug, needed
for stack in recipe.input for stack in recipe.input
@_processInputStack stack needed = step.multiplier * stack.quantity
consumed = Math.min needed, @result.quantityOf(stack.itemSlug)
remaining = needed - consumed
@result.remove stack.itemSlug, consumed
@need.add stack.itemSlug, remaining
for stack in recipe.output for stack in recipe.output
@_processOutputStack stack created = stack.quantity * step.multiplier
consumed = Math.min created, @need.quantityOf stack.itemSlug
remaining = created - consumed
_processInputStack: (stack)-> @result.add stack.itemSlug, remaining
quantityAvailable = @result.quantityOf stack.itemSlug @need.remove stack.itemSlug, consumed
quantityUsed = Math.min quantityAvailable, stack.quantity
quantityNeeded = stack.quantity - quantityUsed
logger.trace "processing input:#{stack.itemSlug},
a:#{quantityAvailable}, u:#{quantityUsed}, n:#{quantityNeeded}"
@result.remove stack.itemSlug, quantityUsed
@_pending.add stack.itemSlug, quantityNeeded
@_need.add stack.itemSlug, quantityNeeded
_processOutputStack: (stack)->
quantityMissing = @_need.quantityOf stack.itemSlug
quantityUsed = Math.min quantityMissing, stack.quantity
quantityLeft = stack.quantity - quantityUsed
logger.trace "processing output:#{stack.itemSlug},
m:#{quantityMissing}, u:#{quantityUsed}, l:#{quantityLeft}"
@_need.remove stack.itemSlug, quantityUsed
@result.add stack.itemSlug, quantityLeft
_totalQuantityOf: (itemSlug)->
return @result.quantityOf(itemSlug) - @_need.quantityOf(itemSlug)
+12 -27
View File
@@ -20,7 +20,6 @@ module.exports = class CraftingTable extends BaseModel
@plan.on 'change', => @reset() @plan.on 'change', => @reset()
@_step = 0 @_step = 0
@_steps = []
Object.defineProperties this, { Object.defineProperties this, {
hasNextStep: { get:@hasNextStep } hasNextStep: { get:@hasNextStep }
@@ -36,29 +35,27 @@ module.exports = class CraftingTable extends BaseModel
# Public Methods ############################################################################### # Public Methods ###############################################################################
reset: -> reset: ->
@_compactSteps()
@step = 0 @step = 0
return this return this
# Property Methods ############################################################################# # Property Methods #############################################################################
hasNextStep: -> hasNextStep: ->
return @_step + 1 < @_steps.length return @_step + 1 < @plan.steps.length
hasPrevStep: -> hasPrevStep: ->
return @_step > 0 return @_step > 0
hasSteps: -> hasSteps: ->
return @_steps.length > 0 return @plan.steps.length > 0
getMultiplier: -> getMultiplier: ->
step = @_steps[@_step] step = @plan.steps[@_step]
return unless step? return unless step?
return step.multiplier return step.multiplier
getOutput: -> getOutput: ->
step = @_steps[@_step] step = @plan.steps[@_step]
return unless step? return unless step?
return step.recipe.output[0] return step.recipe.output[0]
@@ -66,20 +63,23 @@ module.exports = class CraftingTable extends BaseModel
return @_step return @_step
setStep: (newStep)-> setStep: (newStep)->
newStep = Math.max 0, Math.min @_steps.length - 1, newStep oldStep = @_step
newStep = Math.max 0, Math.min @plan.steps.length - 1, newStep
@_step = newStep @_step = newStep
@grid.recipe = @_steps[@_step]?.recipe @grid.recipe = @plan.steps[@_step]?.recipe
@trigger 'change:step', this, oldStep, newStep
@trigger 'change', this @trigger 'change', this
return this return this
getStepCount: -> getStepCount: ->
return 0 unless @_steps? return 0 unless @plan.steps?
return @_steps.length return @plan.steps.length
getToolNames: -> getToolNames: ->
recipe = @_steps[@_step]?.recipe recipe = @plan.steps[@_step]?.recipe
return '' unless recipe? return '' unless recipe?
toolSlugs = (stack.itemSlug for stack in recipe.tools) toolSlugs = (stack.itemSlug for stack in recipe.tools)
toolNames = (@modPack.findName(slug) for slug in toolSlugs).join ', ' toolNames = (@modPack.findName(slug) for slug in toolSlugs).join ', '
@@ -89,18 +89,3 @@ module.exports = class CraftingTable extends BaseModel
toString: -> toString: ->
return "#{@constructor.name} (#{@cid}) { plan:#{@plan}, step:#{@_step} }" return "#{@constructor.name} (#{@cid}) { plan:#{@plan}, step:#{@_step} }"
# Private Methods ##############################################################################
_compactSteps: ->
steps = {}
for recipe in @plan.steps
step = steps[recipe.name]
if not step?
steps[recipe.name] = multiplier:1, recipe:recipe
else
step.multiplier += 1
@_steps = (step for name, step of steps)
return this
+17 -12
View File
@@ -22,23 +22,14 @@ module.exports = class Inventory extends BaseModel
# Public Methods ############################################################################### # Public Methods ###############################################################################
add: (itemSlug, quantity=1)-> add: (itemSlug, quantity=1)->
return if quantity is 0 @_add itemSlug, quantity
stack = @_stacks[itemSlug]
if not stack?
stack = new Stack itemSlug:itemSlug, quantity:quantity
@_stacks[itemSlug] = stack
@_slugs.push itemSlug
@_slugs.sort()
else
stack.quantity += quantity
@trigger Event.add, this, itemSlug, quantity @trigger Event.add, this, itemSlug, quantity
@trigger Event.change, this @trigger Event.change, this
return this return this
addInventory: (inventory)-> addInventory: (inventory)->
inventory.each (stack)=> @add stack.itemSlug, stack.quantity inventory.each (stack)=> @_add stack.itemSlug, stack.quantity
@trigger Event.change, this
return this return this
clear: -> clear: ->
@@ -122,3 +113,17 @@ module.exports = class Inventory extends BaseModel
result.push '}' result.push '}'
return result.join '' return result.join ''
# Private Methods ##############################################################################
_add: (itemSlug, quantity=1)->
return if quantity is 0
stack = @_stacks[itemSlug]
if not stack?
stack = new Stack itemSlug:itemSlug, quantity:quantity
@_stacks[itemSlug] = stack
@_slugs.push itemSlug
@_slugs.sort()
else
stack.quantity += quantity
+3 -1
View File
@@ -20,7 +20,9 @@ module.exports = class Recipe extends BaseModel
attributes.tools ?= [] attributes.tools ?= []
super attributes, options super attributes, options
Object.defineProperty @prototype, 'name', get:-> @item.name Object.defineProperties this,
'name': { get: -> @item.name }
'slug': { get: -> @item.slug }
# Public Methods ############################################################################### # Public Methods ###############################################################################