Complete basic crafting functionality
This commit is contained in:
@@ -25,6 +25,7 @@ $color-brick-dark: #7A7A7A;
|
||||
$color-brick-light: #D9D9D9;
|
||||
$color-content: rgba(230, 230, 230, 0.90);
|
||||
$color-dirt: #906647;
|
||||
$color-ice: #8AA7E6;
|
||||
$color-footer: #3D1D09;
|
||||
$color-footer-text: white;
|
||||
$color-gray-faint: #F2F2F2;
|
||||
|
||||
@@ -7,6 +7,17 @@ All rights reserved.
|
||||
|
||||
.view__crafting_table {
|
||||
|
||||
.quantity {
|
||||
border-radius: 0.75em;
|
||||
background: $color-ice;
|
||||
color: $color-table-background;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
min-width: 3em;
|
||||
margin-right: 0.5em;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
.recipe_selector {
|
||||
background: $color-table-background;
|
||||
padding: 1em;
|
||||
@@ -28,18 +39,30 @@ All rights reserved.
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
table {
|
||||
background: white;
|
||||
width: 100%;
|
||||
li {
|
||||
margin: 0.25em 0;
|
||||
}
|
||||
|
||||
td { padding: 0 1em 1em 1em; width: 33%; }
|
||||
table { background: $color-table-background; width: 100%; }
|
||||
|
||||
td {
|
||||
border-left: 0.1em solid $color-gray-light;
|
||||
padding: 0 1em 1em 1em;
|
||||
width: 25%;
|
||||
|
||||
&:first-child { border-left: 0; }
|
||||
}
|
||||
|
||||
textarea, ul {
|
||||
border: 0.1em solid $color-gray-light;
|
||||
position: relative;
|
||||
font-size: $font-size-normal;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 6em;
|
||||
min-height: 12em;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
textarea { font-family: $font-family-input; resize: vertical; }
|
||||
|
||||
ul { font-family: $font-family-normal; }
|
||||
}
|
||||
|
||||
@@ -454,7 +454,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Bronze Ingot"]],
|
||||
"input": [[1, "Bronze Dust"], [125, "milli-coal"]],
|
||||
"input": [[1, "Bronze Dust"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Bronze Leggings"]],
|
||||
@@ -502,7 +502,7 @@
|
||||
"tools": ["Macerator"]
|
||||
}, {
|
||||
"output": [[1, "Copper Ingot"]],
|
||||
"input": [[1, "Copper Ore"], [125, "milli-coal"]],
|
||||
"input": [[1, "Copper Ore"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Crop-Matron"]],
|
||||
@@ -682,7 +682,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Refined Iron"]],
|
||||
"input": [[1, "Iron Ingot"], [125, "milli-coal"]],
|
||||
"input": [[1, "Iron Ingot"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[7, "Reinforced Glass"]],
|
||||
@@ -690,7 +690,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Rubber"]],
|
||||
"input": [[1, "Resin"], [125, "milli-coal"]],
|
||||
"input": [[1, "Resin"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Rubber Boots"]],
|
||||
@@ -706,7 +706,7 @@
|
||||
"tools": ["Macerator"]
|
||||
}, {
|
||||
"output": [[1, "Silver Ingot"]],
|
||||
"input": [[1, "Silver Ore"], [125, "milli-coal"]],
|
||||
"input": [[1, "Silver Ore"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Solar Helmet"]],
|
||||
@@ -726,7 +726,7 @@
|
||||
"tools": ["Macerator"]
|
||||
}, {
|
||||
"output": [[1, "Tin Ingot"]],
|
||||
"input": [[1, "Tin Ore"], [125, "milli-coal"]],
|
||||
"input": [[1, "Tin Ore"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Tree Tap"]],
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"tools": []
|
||||
}, {
|
||||
"output": [[1, "Electrum Ingot"]],
|
||||
"input": [[1, "Electrum Blend"], [125, "milli-coal"]],
|
||||
"input": [[1, "Electrum Blend"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Energetic Infuser"]],
|
||||
@@ -86,7 +86,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Invar Ingot"]],
|
||||
"input": [[1, "Invar Blend"], [125, "milli-coal"]],
|
||||
"input": [[1, "Invar Blend"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Item Tesseract"]],
|
||||
@@ -94,7 +94,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Lead Ingot"]],
|
||||
"input": [[1, "Lead Ore"], [125, "milli-coal"]],
|
||||
"input": [[1, "Lead Ore"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Liquid Tesseract"]],
|
||||
@@ -190,7 +190,7 @@
|
||||
"tools": ["Pulverizer"]
|
||||
}, {
|
||||
"output": [[1, "Shiny Ingot"]],
|
||||
"input": [[1, "Shiny Dust"], [125, "milli-coal"]],
|
||||
"input": [[1, "Shiny Dust"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Steam Engine"]],
|
||||
|
||||
+13
-13
@@ -10,7 +10,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Baked Potato"]],
|
||||
"input": [[1, "Potato"], [125, "milli-coal"]],
|
||||
"input": [[1, "Potato"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Beacon"]],
|
||||
@@ -62,7 +62,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Brick"]],
|
||||
"input": [[1, "Clay"], [125, "milli-coal"]],
|
||||
"input": [[1, "Clay"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Bricks"]],
|
||||
@@ -74,7 +74,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Cactus Green"]],
|
||||
"input": [[1, "Cactus"], [125, "milli-coal"]],
|
||||
"input": [[1, "Cactus"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Cake"], [1, "Bucket"]],
|
||||
@@ -106,7 +106,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Charcoal"]],
|
||||
"input": [[1, "Oak Log"], [125, "milli-coal"]],
|
||||
"input": [[1, "Oak Log"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Chest"]],
|
||||
@@ -126,15 +126,15 @@
|
||||
"tools": []
|
||||
}, {
|
||||
"output": [[1, "Cooked Chicken"]],
|
||||
"input": [[1, "Raw Chicken"], [125, "milli-coal"]],
|
||||
"input": [[1, "Raw Chicken"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Cooked Fish"]],
|
||||
"input": [[1, "Raw Fish"], [125, "milli-coal"]],
|
||||
"input": [[1, "Raw Fish"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Cooked Porkchop"]],
|
||||
"input": [[1, "Raw Porkchop"], [125, "milli-coal"]],
|
||||
"input": [[1, "Raw Porkchop"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Cookie"]],
|
||||
@@ -210,7 +210,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Glass"]],
|
||||
"input": [[1, "Sand"], [125, "milli-coal"]],
|
||||
"input": [[1, "Sand"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[3, "Glass Bottle"]],
|
||||
@@ -250,7 +250,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Gold Ingot"]],
|
||||
"input": [[1, "Gold Ore"], [125, "milli-coal"]],
|
||||
"input": [[1, "Gold Ore"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[9, "Gold Nugget"]],
|
||||
@@ -310,7 +310,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Iron Ingot"]],
|
||||
"input": [[1, "Iron Ore"], [125, "milli-coal"]],
|
||||
"input": [[1, "Iron Ore"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Iron Leggings"]],
|
||||
@@ -422,7 +422,7 @@
|
||||
"tools": []
|
||||
}, {
|
||||
"output": [[1, "Nether Brick (item)"]],
|
||||
"input": [[1, "Netherrack"], [125, "milli-coal"]],
|
||||
"input": [[1, "Netherrack"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Nether Brick (block)"]],
|
||||
@@ -486,7 +486,7 @@
|
||||
"tools": ["Crafting Table"]
|
||||
}, {
|
||||
"output": [[1, "Steak"]],
|
||||
"input": [[1, "Raw Beef"], [125, "milli-coal"]],
|
||||
"input": [[1, "Raw Beef"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[4, "Stick"]],
|
||||
@@ -502,7 +502,7 @@
|
||||
"tools": []
|
||||
}, {
|
||||
"output": [[1, "Stone"]],
|
||||
"input": [[1, "Cobblestone"], [125, "milli-coal"]],
|
||||
"input": [[1, "Cobblestone"], [1, "{furnace fuel}"]],
|
||||
"tools": ["Furnace"]
|
||||
}, {
|
||||
"output": [[1, "Stone Axe"]],
|
||||
|
||||
@@ -17,10 +17,7 @@ module.exports = class CraftingTableController extends BaseController
|
||||
options.templateName = 'crafting_table'
|
||||
super options
|
||||
|
||||
@_parser = new InventoryParser
|
||||
@_name = null
|
||||
@_quantity = null
|
||||
@_includingTools = null
|
||||
@_parser = new InventoryParser
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
@@ -29,22 +26,22 @@ module.exports = class CraftingTableController extends BaseController
|
||||
|
||||
@model.have.clear()
|
||||
@_parser.parse @$haveField.val(), @model.have
|
||||
@_recalculate()
|
||||
@model.craft()
|
||||
|
||||
onIncludingToolsFieldChanged: ->
|
||||
return unless @rendered
|
||||
@_includingTools = @$('input[name="including_tools"]:checked').length > 0
|
||||
@_recalculate()
|
||||
@model.includingTools = @$('input[name="including_tools"]:checked').length > 0
|
||||
@model.craft()
|
||||
|
||||
onNameFieldChanged: ->
|
||||
return unless @rendered
|
||||
@_name = @$nameField.val()
|
||||
@_recalculate()
|
||||
@model.name = @$nameField.val()
|
||||
@model.craft()
|
||||
|
||||
onQuantityFieldChanged: ->
|
||||
return unless @rendered
|
||||
@_quantity = parseInt @$quantityField.find(":selected").val()
|
||||
@_recalculate()
|
||||
@model.quantity = parseInt @$quantityField.find(":selected").val()
|
||||
@model.craft()
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
@@ -54,13 +51,34 @@ module.exports = class CraftingTableController extends BaseController
|
||||
@$nameField = @$('input[name="name"]')
|
||||
@$quantityField = @$('select[name="quantity"]')
|
||||
|
||||
@$needList = @$('td.need ul')
|
||||
@$makeList = @$('td.make ul')
|
||||
@$resultList = @$('td.result ul')
|
||||
|
||||
super
|
||||
|
||||
@onHaveFieldChanged()
|
||||
@onIncludingToolsFieldChanged()
|
||||
@onNameFieldChanged()
|
||||
@onQuantityFieldChanged()
|
||||
|
||||
# do last so others are no-op until all is set
|
||||
@onNameFieldChanged()
|
||||
|
||||
refresh: ->
|
||||
@$needList.empty()
|
||||
@$makeList.empty()
|
||||
@$resultList.empty()
|
||||
|
||||
if @model.plan?
|
||||
@model.plan.need.each (item)=>
|
||||
@$needList.append "<li><span class='quantity'>#{item.quantity}</span> #{item.name}</li>"
|
||||
@model.plan.make.each (item)=>
|
||||
@$makeList.append "<li><span class='quantity'>#{item.quantity}</span> #{item.name}</li>"
|
||||
@model.plan.result.each (item)=>
|
||||
@$resultList.append "<li><span class='quantity'>#{item.quantity}</span> #{item.name}</li>"
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events:
|
||||
@@ -68,9 +86,3 @@ module.exports = class CraftingTableController extends BaseController
|
||||
'input input[name="name"]': 'onNameFieldChanged'
|
||||
'input select[name="quantity"]': 'onQuantityFieldChanged'
|
||||
'change input[name="including_tools"]': 'onIncludingToolsFieldChanged'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_recalculate: ->
|
||||
return if not @_name? or @_name.length is 0
|
||||
@model.craft @_name, @_quantity, @_includingTools
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
###
|
||||
|
||||
BaseController = require './base_controller'
|
||||
{DefaultBookUrls} = require '../constants'
|
||||
RecipeBookController = require './recipe_book_controller'
|
||||
|
||||
########################################################################################################################
|
||||
@@ -21,6 +22,10 @@ module.exports = class RecipeCatalogController extends BaseController
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onWillRender: ->
|
||||
if @model.books.length is 0
|
||||
@model.loadAllBooks DefaultBookUrls
|
||||
|
||||
onDidRender: ->
|
||||
@$table = @$('table')
|
||||
super
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
###
|
||||
# Crafting Guide - crafting_plan.coffee
|
||||
#
|
||||
# Copyright (c) 2014 by Redwood Labs
|
||||
# All rights reserved.
|
||||
###
|
||||
|
||||
Inventory = require './inventory'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class CraftingPlan
|
||||
|
||||
constructor: (@catalog, includingTools)->
|
||||
if not @catalog then throw new Error 'catalog is required'
|
||||
@includingTools = if includingTools then true else false
|
||||
@clear()
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
clear: ->
|
||||
@make = new Inventory
|
||||
@need = new Inventory
|
||||
@result = new Inventory
|
||||
@steps = []
|
||||
@_expected = new Inventory
|
||||
@_pending = null
|
||||
|
||||
return this
|
||||
|
||||
craft: (name, quantity=1, have=null)->
|
||||
@clear()
|
||||
@result.addInventory(have) if have?
|
||||
|
||||
@_expected.add name, quantity
|
||||
@_pending = @_expected.clone()
|
||||
while not @_pending.isEmpty
|
||||
@_processPending()
|
||||
|
||||
@steps.reverse()
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
toString: ->
|
||||
return "#{@constructor.name} { need:#{@need}, make:#{@make}, result:#{@result} }"
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_processPending: ->
|
||||
targetItem = @_pending.pop()
|
||||
return unless targetItem?
|
||||
|
||||
recipes = @catalog.findRecipes targetItem.name
|
||||
return if not recipes.length > 0
|
||||
recipe = recipes[0]
|
||||
|
||||
if @includingTools
|
||||
for tool in recipe.tools
|
||||
totalExpected = @result.quantityOf(tool.name) + @_expected.quantityOf(tool.name)
|
||||
if totalExpected < tool.quantity
|
||||
@_pending.add tool.name, tool.quantity
|
||||
@_expected.add tool.name, tool.quantity
|
||||
|
||||
while @_totalQuantityOf(targetItem.name) < @_expected.quantityOf(targetItem.name)
|
||||
@steps.push recipe
|
||||
|
||||
for item in recipe.input
|
||||
@_processInputItem item
|
||||
|
||||
for item in recipe.output
|
||||
@_processOutputItem item
|
||||
|
||||
_processInputItem: (item)->
|
||||
quantityAvailable = @result.quantityOf item.name
|
||||
quantityUsed = Math.min quantityAvailable, item.quantity
|
||||
quantityNeeded = item.quantity - quantityUsed
|
||||
|
||||
@result.remove item.name, quantityUsed
|
||||
@_pending.add item.name, quantityNeeded
|
||||
@need.add item.name, quantityNeeded
|
||||
|
||||
_processOutputItem: (item)->
|
||||
quantityMissing = @need.quantityOf item.name
|
||||
quantityUsed = Math.min quantityMissing, item.quantity
|
||||
quantityLeft = item.quantity - quantityUsed
|
||||
|
||||
@need.remove item.name, quantityUsed
|
||||
@result.add item.name, quantityLeft
|
||||
@make.add item.name, item.quantity
|
||||
|
||||
_totalQuantityOf: (name)->
|
||||
return @result.quantityOf(name) - @need.quantityOf(name)
|
||||
@@ -5,8 +5,10 @@
|
||||
# All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
Inventory = require './inventory'
|
||||
BaseModel = require './base_model'
|
||||
CraftingPlan = require './crafting_plan'
|
||||
{Event} = require '../constants'
|
||||
Inventory = require './inventory'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -14,14 +16,36 @@ module.exports = class CraftingTable extends BaseModel
|
||||
|
||||
constructor: (attributes={}, options={})->
|
||||
if not attributes.catalog? then throw new Error "attributes.catalog is required"
|
||||
attributes.name ?= null
|
||||
attributes.quantity ?= 1
|
||||
attributes.includingTools ?= false
|
||||
attributes.have ?= new Inventory
|
||||
attributes.plan ?= null
|
||||
super attributes, options
|
||||
|
||||
@have = new Inventory
|
||||
@make = new Inventory
|
||||
@get = new Inventory
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
craft: (name, quantity=1, includingTools=false)->
|
||||
toolPhrase = if includingTools then ' includingTools' else ''
|
||||
logger.verbose "calculating build plan for #{quantity} #{name}#{toolPhrase} with inventory: #{@have}"
|
||||
craft: ->
|
||||
if not @name?.length
|
||||
@plan = null
|
||||
return
|
||||
|
||||
toolPhrase = if @includingTools then ' includingTools' else ''
|
||||
logger.verbose "calculating build plan for #{@quantity} #{@name}#{toolPhrase} with inventory: #{@have}"
|
||||
|
||||
plan = new CraftingPlan @catalog, @includingTools
|
||||
plan.includingTools = @includingTools
|
||||
plan.craft @name, @quantity, @have
|
||||
|
||||
@plan = plan
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
toString: ->
|
||||
return "#{@constructor.name} (#{@cid}) {
|
||||
name:#{@name},
|
||||
quantity:#{@quantity},
|
||||
includingTools:#{@includingTools},
|
||||
have:#{@have},
|
||||
plan:#{@plan}
|
||||
}"
|
||||
|
||||
@@ -17,14 +17,19 @@ module.exports = class Inventory extends BaseModel
|
||||
super attributes, options
|
||||
@clear()
|
||||
|
||||
Object.defineProperty this, 'isEmpty', get:-> @_names.length is 0
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
add: (name, quantity=1)->
|
||||
return if quantity is 0
|
||||
|
||||
item = @_items[name]
|
||||
if not item?
|
||||
item = new Item name:name, quantity:quantity
|
||||
@_items[name] = item
|
||||
@_names.push name
|
||||
@_names.sort()
|
||||
else
|
||||
item.quantity += quantity
|
||||
|
||||
@@ -32,10 +37,19 @@ module.exports = class Inventory extends BaseModel
|
||||
@trigger Event.change, this
|
||||
return this
|
||||
|
||||
addInventory: (inventory)->
|
||||
inventory.each (item)=> @add item.name, item.quantity
|
||||
return this
|
||||
|
||||
clear: ->
|
||||
@_items = {}
|
||||
@_names = []
|
||||
|
||||
clone: ->
|
||||
inventory = new Inventory
|
||||
@each (item)-> inventory.add item.name, item.quantity
|
||||
return inventory
|
||||
|
||||
each: (onItem)->
|
||||
for name in @_names
|
||||
item = @_items[name]
|
||||
@@ -48,7 +62,25 @@ module.exports = class Inventory extends BaseModel
|
||||
return false unless item?
|
||||
return item.quantity >= quantity
|
||||
|
||||
pop: ->
|
||||
name = @_names.pop()
|
||||
return null unless name?
|
||||
|
||||
item = @_items[name]
|
||||
delete @_items[name]
|
||||
|
||||
@trigger Event.remove, this, item.name, item.quantity
|
||||
@trigger Event.change, this
|
||||
return item
|
||||
|
||||
quantityOf: (name)->
|
||||
item = @_items[name]
|
||||
return 0 unless item?
|
||||
return item.quantity
|
||||
|
||||
remove: (name, quantity=1)->
|
||||
return if quantity is 0
|
||||
|
||||
item = @_items[name]
|
||||
if not item? then throw new Error "cannot remove #{name} since it is not in this inventory"
|
||||
if item.quantity < quantity
|
||||
@@ -63,13 +95,22 @@ module.exports = class Inventory extends BaseModel
|
||||
@trigger Event.change, this
|
||||
return this
|
||||
|
||||
toList: ->
|
||||
result = []
|
||||
@each (item)->
|
||||
if item.quantity > 1
|
||||
result.push [item.quantity, item.name]
|
||||
else
|
||||
result.push item.name
|
||||
return result
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
toString: ->
|
||||
result = [@constructor.name, " (", @cid, ") { items: ["]
|
||||
|
||||
needsDelimiter = false
|
||||
for name, item of @_items
|
||||
@each (item)->
|
||||
if needsDelimiter then result.push ', '
|
||||
result.push item.toString()
|
||||
needsDelimiter = true
|
||||
|
||||
@@ -19,6 +19,22 @@ module.exports = class Recipe extends BaseModel
|
||||
|
||||
Object.defineProperty this, 'name', get:-> @output[0].name
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
make: (inventory, missing)->
|
||||
for item in @input
|
||||
needed = item.quantity
|
||||
while needed > 0
|
||||
if inventory.hasAtLeast item.name
|
||||
inventory.remove item.name
|
||||
else
|
||||
missing.add item.name
|
||||
|
||||
for item in @output
|
||||
inventory.add item.name, item.quantity
|
||||
|
||||
return this
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
toString: ->
|
||||
|
||||
@@ -21,7 +21,7 @@ module.exports = class RecipeBook extends BaseModel
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
getRecipes: (name)->
|
||||
findRecipes: (name)->
|
||||
result = []
|
||||
for recipe in @recipes
|
||||
if recipe.name is name
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
{DefaultBookUrls} = require '../constants'
|
||||
{Event} = require '../constants'
|
||||
RecipeBookParser = require './recipe_book_parser'
|
||||
|
||||
@@ -19,11 +18,17 @@ module.exports = class RecipeCatalog extends BaseModel
|
||||
super attributes, options
|
||||
|
||||
@_parser = new RecipeBookParser
|
||||
if @books.length is 0
|
||||
@loadAllBooks DefaultBookUrls
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
findRecipes: (name)->
|
||||
result = []
|
||||
for book in @books
|
||||
for recipe in book.findRecipes name
|
||||
result.push recipe
|
||||
|
||||
return result
|
||||
|
||||
loadBook: (url)->
|
||||
w.promise (resolve, reject)=>
|
||||
@trigger Event.load.started, this, url
|
||||
@@ -35,6 +40,15 @@ module.exports = class RecipeCatalog extends BaseModel
|
||||
error: (xhr, status, error)=>
|
||||
reject @onBookLoadFailed(url, error, status, xhr)
|
||||
|
||||
loadBookData: (data)->
|
||||
book = @_parser.parse data
|
||||
@books.push book
|
||||
@books.sort (a, b)->
|
||||
return 0 if a.modName is b.modName
|
||||
return if a.modName < b.modName then -1 else +1
|
||||
|
||||
return book
|
||||
|
||||
loadAllBooks: (urlList)->
|
||||
promises = (@loadBook(url) for url in urlList)
|
||||
return w.settle promises
|
||||
@@ -43,11 +57,7 @@ module.exports = class RecipeCatalog extends BaseModel
|
||||
|
||||
onBookLoaded: (url, data, status, xhr)->
|
||||
try
|
||||
book = @_parser.parse data
|
||||
@books.push book
|
||||
@books.sort (a, b)->
|
||||
return 0 if a.modName is b.modName
|
||||
return if a.modName < b.modName then -1 else +1
|
||||
book = @loadBookData data
|
||||
|
||||
logger.info "loaded recipe book from #{url}: #{book}"
|
||||
@trigger Event.load.succeeded, this, book
|
||||
|
||||
@@ -8,7 +8,7 @@ var jade_mixins = {};
|
||||
var jade_interp;
|
||||
|
||||
var jade_indent = [];
|
||||
buf.push("\n<div class=\"view__crafting_table\">\n <h2><img src=\"/images/workbench_top.png\"/>\n <p>Crafting Table</p>\n </h2>\n <div class=\"recipe_selector\">\n <p>I want to make</p>\n <select name=\"quantity\">\n <option value=\"1\">1</option>\n <option value=\"2\">2</option>\n <option value=\"4\">4</option>\n <option value=\"8\">8</option>\n <option value=\"16\">16</option>\n <option value=\"64\">64</option>\n </select>\n <input name=\"name\" placeholder=\"enter an item name\"/>\n <label>\n <input name=\"including_tools\" type=\"checkbox\"/>including tools\n </label>\n </div>\n <table>\n <tr>\n <td class=\"have\">\n <h3>\n <p>You have...</p>\n </h3>\n <textarea name=\"have\" placeholder=\"enter what you have, for example:\n\n3 wool\n4 iron ingot\"></textarea>\n </td>\n <td class=\"make\">\n <h3>\n <p>You'll make...</p>\n </h3>\n <ul></ul>\n </td>\n <td class=\"get\">\n <h3>\n <p>You'll get...</p>\n </h3>\n <ul></ul>\n </td>\n </tr>\n </table>\n</div>");;return buf.join("");
|
||||
buf.push("\n<div class=\"view__crafting_table\">\n <h2><img src=\"/images/workbench_top.png\"/>\n <p>Crafting Table</p>\n </h2>\n <div class=\"recipe_selector\">\n <p>I want to make</p>\n <select name=\"quantity\">\n <option value=\"1\">1</option>\n <option value=\"2\">2</option>\n <option value=\"4\">4</option>\n <option value=\"8\">8</option>\n <option value=\"16\">16</option>\n <option value=\"64\">64</option>\n </select>\n <input name=\"name\" placeholder=\"enter an item name\"/>\n <label>\n <input name=\"including_tools\" type=\"checkbox\"/>including tools\n </label>\n </div>\n <table>\n <tr>\n <td class=\"have\">\n <h3>\n <p>You have...</p>\n </h3>\n <textarea name=\"have\" placeholder=\"For example:\n\n3 wool\n4 iron ingot\"></textarea>\n </td>\n <td class=\"need\">\n <h3>\n <p>You'll need...</p>\n </h3>\n <ul></ul>\n </td>\n <td class=\"make\">\n <h3>\n <p>You'll make...</p>\n </h3>\n <ul></ul>\n </td>\n <td class=\"result\">\n <h3>\n <p>You'll get...</p>\n </h3>\n <ul></ul>\n </td>\n </tr>\n </table>\n</div>");;return buf.join("");
|
||||
};
|
||||
|
||||
this["JST"]["landing_page"] = function template(locals) {
|
||||
@@ -17,7 +17,7 @@ var jade_mixins = {};
|
||||
var jade_interp;
|
||||
|
||||
var jade_indent = [];
|
||||
buf.push("\n<div class=\"view__landing_page\">\n <div class=\"view__recipe_catalog\"></div>\n <div class=\"view__crafting_table\"></div>\n</div>");;return buf.join("");
|
||||
buf.push("\n<div class=\"view__landing_page\">\n <div class=\"view__crafting_table\"></div>\n <div class=\"view__recipe_catalog\"></div>\n</div>");;return buf.join("");
|
||||
};
|
||||
|
||||
this["JST"]["recipe_book"] = function template(locals) {
|
||||
|
||||
@@ -28,10 +28,13 @@
|
||||
tr
|
||||
td.have
|
||||
h3: p You have...
|
||||
textarea(name="have", placeholder="enter what you have, for example:\n\n3 wool\n4 iron ingot")
|
||||
textarea(name="have", placeholder="For example:\n\n3 wool\n4 iron ingot")
|
||||
td.need
|
||||
h3: p You'll need...
|
||||
ul
|
||||
td.make
|
||||
h3: p You'll make...
|
||||
ul
|
||||
td.get
|
||||
td.result
|
||||
h3: p You'll get...
|
||||
ul
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
//-
|
||||
|
||||
.view__landing_page
|
||||
.view__recipe_catalog
|
||||
.view__crafting_table
|
||||
.view__recipe_catalog
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
###
|
||||
# Crafting Guide - crafting_plan.test.coffee
|
||||
#
|
||||
# Copyright (c) 2014 by Redwood Labs
|
||||
# All rights reserved.
|
||||
###
|
||||
|
||||
RecipeCatalog = require '../src/scripts/models/recipe_catalog'
|
||||
CraftingPlan = require '../src/scripts/models/crafting_plan'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
catalog = plan = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'CraftingPlan', ->
|
||||
|
||||
beforeEach ->
|
||||
catalog = new RecipeCatalog
|
||||
catalog.loadBookData {
|
||||
version: 1
|
||||
mod_name: 'Minecraft'
|
||||
mod_version: '1.7.10'
|
||||
recipes: [
|
||||
{ input:'Oak Log', output:[[4, 'Oak Plank']] }
|
||||
{ input:[[2, 'Oak Plank']], output:[[4, 'Stick']] }
|
||||
{ input:[[4, 'Oak Plank']], output:'Crafting Table' }
|
||||
{ input:[[8, 'Cobblestone']], tools:'Crafting Table', output:'Furnace' }
|
||||
{ input:['Iron Ore', '{furnace fuel}'], tools:'Furnace', output:'Iron Ingot' }
|
||||
{ input:[[2, 'Iron Ingot'], 'Stick'], tools:'Crafting Table', output:'Iron Sword' }
|
||||
]
|
||||
}
|
||||
|
||||
plan = new CraftingPlan catalog
|
||||
|
||||
describe 'craft', ->
|
||||
|
||||
describe 'under the simplest conditions', ->
|
||||
|
||||
it 'can craft a single step recipe', ->
|
||||
plan.craft 'Oak Plank'
|
||||
plan.need.toList().should.eql ['Oak Log']
|
||||
plan.result.toList().should.eql [[4, 'Oak Plank']]
|
||||
|
||||
it 'can craft a multi-step recipe', ->
|
||||
plan.craft 'Crafting Table'
|
||||
plan.need.toList().should.eql ['Oak Log']
|
||||
plan.result.toList().should.eql ['Crafting Table']
|
||||
|
||||
it 'can craft a multi-step recipe using tools', ->
|
||||
plan.craft 'Furnace'
|
||||
plan.need.toList().should.eql [[8, 'Cobblestone']]
|
||||
plan.result.toList().should.eql ['Furnace']
|
||||
|
||||
it 'can craft a multi-step recipe re-using tools', ->
|
||||
plan.craft 'Iron Sword'
|
||||
plan.need.toList().should.eql [[2, 'Iron Ore'], 'Oak Log', [2, '{furnace fuel}']]
|
||||
plan.result.toList().should.eql ['Iron Sword', [2, 'Oak Plank'], [3, 'Stick']]
|
||||
|
||||
describe 'with building tools', ->
|
||||
|
||||
it 'can craft a multi-step recipe using tools', ->
|
||||
plan.includingTools = true
|
||||
plan.craft 'Furnace'
|
||||
plan.need.toList().should.eql [[8, 'Cobblestone'], 'Oak Log']
|
||||
plan.result.toList().should.eql ['Crafting Table', 'Furnace']
|
||||
|
||||
it 'can craft a multi-step recipe re-using tools', ->
|
||||
plan.includingTools = true
|
||||
plan.craft 'Iron Sword'
|
||||
plan.need.toList().should.eql [
|
||||
[8, 'Cobblestone'], [2, 'Iron Ore'], [2, 'Oak Log'], [2, '{furnace fuel}'],
|
||||
]
|
||||
plan.result.toList().should.eql [
|
||||
'Crafting Table', 'Furnace', 'Iron Sword', [2, 'Oak Plank'], [3, 'Stick']
|
||||
]
|
||||
|
||||
describe 'using existing inventory', ->
|
||||
+47
-12
@@ -35,17 +35,42 @@ describe 'Inventory', ->
|
||||
|
||||
it 'can augment quantity of existing items', ->
|
||||
inventory.add 'wool', 2
|
||||
inventory._items.wool.quantity.should.equal 6
|
||||
inventory.toList().should.eql ['boat', [20, 'string'], [6, 'wool']]
|
||||
|
||||
it 'can add zero quantity', ->
|
||||
inventory.add 'wool', 0
|
||||
inventory._items.wool.quantity.should.equal 4
|
||||
inventory.toList().should.eql ['boat', [20, 'string'], [4, 'wool']]
|
||||
|
||||
it 'emits the proper events', ->
|
||||
events = new EventRecorder inventory
|
||||
inventory.add 'iron ingot', 10
|
||||
events.names.should.eql [Event.add, Event.change]
|
||||
|
||||
describe 'addInventory', ->
|
||||
|
||||
it 'can add to an empty inventory', ->
|
||||
newInventory = new Inventory
|
||||
newInventory.addInventory inventory
|
||||
newInventory._names.should.eql ['boat', 'string', 'wool']
|
||||
|
||||
it 'can add a mix of new and existing items', ->
|
||||
newInventory = new Inventory
|
||||
newInventory.add 'string', 2
|
||||
newInventory.addInventory inventory
|
||||
newInventory.toList().should.eql ['boat', [22, 'string'], [4, 'wool']]
|
||||
|
||||
describe 'clone', ->
|
||||
|
||||
it 'creates an empty inventory from an empty inventory', ->
|
||||
a = new Inventory
|
||||
b = a.clone()
|
||||
b._names.should.eql []
|
||||
|
||||
it 'faithfully copies an existing inventory', ->
|
||||
copy = inventory.clone()
|
||||
copy._names.should.eql ['boat', 'string', 'wool']
|
||||
(item.quantity for name, item of copy._items).should.eql [1, 20, 4]
|
||||
|
||||
describe 'each', ->
|
||||
|
||||
it 'works with an empty inventory', ->
|
||||
@@ -57,7 +82,7 @@ describe 'Inventory', ->
|
||||
it 'works when items have only been added', ->
|
||||
result = []
|
||||
inventory.each (item)-> result.push item.name
|
||||
result.should.eql ['wool', 'string', 'boat']
|
||||
result.should.eql ['boat', 'string', 'wool']
|
||||
|
||||
it 'works when items have been augmented', ->
|
||||
inventory.add 'iron ingot'
|
||||
@@ -66,15 +91,7 @@ describe 'Inventory', ->
|
||||
|
||||
result = []
|
||||
inventory.each (item)-> result.push item.name
|
||||
result.should.eql ['wool', 'string', 'boat', 'iron ingot']
|
||||
|
||||
it 'moves items to the end when removed and re-added', ->
|
||||
inventory.remove 'wool', 4
|
||||
inventory.add 'wool'
|
||||
|
||||
result = []
|
||||
inventory.each (item)-> result.push item.name
|
||||
result.should.eql ['string', 'boat', 'wool']
|
||||
result.should.eql ['boat', 'iron ingot', 'string', 'wool']
|
||||
|
||||
describe 'hasAtLeast', ->
|
||||
|
||||
@@ -91,6 +108,24 @@ describe 'Inventory', ->
|
||||
inventory.hasAtLeast('wool', 4).should.be.true
|
||||
inventory.hasAtLeast('wool', 5).should.be.false
|
||||
|
||||
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', ->
|
||||
result = inventory.pop()
|
||||
result.name.should.equal 'wool'
|
||||
result.quantity.should.equal 4
|
||||
inventory.toList().should.eql ['boat', [20, 'string']]
|
||||
|
||||
it 'triggers the right events', ->
|
||||
events = new EventRecorder inventory
|
||||
result = inventory.pop()
|
||||
events.names.should.eql [Event.remove, Event.change]
|
||||
|
||||
describe 'remove', ->
|
||||
|
||||
it 'throws when the item is absent', ->
|
||||
|
||||
@@ -32,15 +32,10 @@ describe 'InventoryParser', ->
|
||||
|
||||
it 'can parse multiple mixed-type items', ->
|
||||
result = parser.parse '4 wool\n10 string\nboat\n\n'
|
||||
result._items.wool.quantity.should.equal 4
|
||||
result._items.string.quantity.should.equal 10
|
||||
result._items.boat.quantity.should.equal 1
|
||||
result._names.should.eql ['wool', 'string', 'boat']
|
||||
result.toList().should.eql ['boat', [10, 'string'], [4, 'wool']]
|
||||
|
||||
it 're-uses the given inventory object', ->
|
||||
inventory = new Inventory
|
||||
inventory.add 'string', 8
|
||||
result = parser.parse '4 wool', inventory
|
||||
inventory._items.string.quantity.should.equal 8
|
||||
inventory._items.wool.quantity.should.equal 4
|
||||
inventory._names.should.eql ['string', 'wool']
|
||||
result.toList().should.eql [[8, 'string'], [4, 'wool']]
|
||||
|
||||
@@ -24,6 +24,7 @@ global.logger = new Logger level:Logger.TRACE
|
||||
|
||||
mocha.setup 'bdd'
|
||||
|
||||
require './crafting_plan.test'
|
||||
require './inventory.test'
|
||||
require './inventory_parser.test'
|
||||
require './recipe_book_parser.test'
|
||||
|
||||
Reference in New Issue
Block a user