Added CraftingTable* components and UI

This commit is contained in:
Andrew Miner
2014-12-23 20:26:36 -08:00
parent b05fc4c5c9
commit 95c15abe10
30 changed files with 866 additions and 252 deletions
+27
View File
@@ -0,0 +1,27 @@
###
# Crafting Guide - crafting_table.coffee
#
# Copyright (c) 2014 by Redwood Labs
# All rights reserved.
###
BaseModel = require './base_model'
Inventory = require './inventory'
########################################################################################################################
module.exports = class CraftingTable extends BaseModel
constructor: (attributes={}, options={})->
if not attributes.catalog? then throw new Error "attributes.catalog is required"
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}"
+79
View File
@@ -0,0 +1,79 @@
###
# Crafting Guide - inventory.coffee
#
# Copyright (c) 2014 by Redwood Labs
# All rights reserved.
###
BaseModel = require './base_model'
{Event} = require '../constants'
Item = require './item'
########################################################################################################################
module.exports = class Inventory extends BaseModel
constructor: (attributes={}, options={})->
super attributes, options
@clear()
# Public Methods ###############################################################################
add: (name, quantity=1)->
item = @_items[name]
if not item?
item = new Item name:name, quantity:quantity
@_items[name] = item
@_names.push name
else
item.quantity += quantity
@trigger Event.add, this, name, quantity
@trigger Event.change, this
return this
clear: ->
@_items = {}
@_names = []
each: (onItem)->
for name in @_names
item = @_items[name]
onItem item
hasAtLeast: (name, quantity=1)->
if quantity is 0 then return true
item = @_items[name]
return false unless item?
return item.quantity >= quantity
remove: (name, quantity=1)->
item = @_items[name]
if not item? then throw new Error "cannot remove #{name} since it is not in this inventory"
if item.quantity < quantity
throw new Error "cannot remove #{quantity} #{name} because there is only #{item.quantity} in this inventory"
item.quantity -= quantity
if item.quantity is 0
delete @_items[name]
@_names = _(@_names).without name
@trigger Event.remove, this, name, quantity
@trigger Event.change, this
return this
# Object Overrides #############################################################################
toString: ->
result = [@constructor.name, " (", @cid, ") { items: ["]
needsDelimiter = false
for name, item of @_items
if needsDelimiter then result.push ', '
result.push item.toString()
needsDelimiter = true
result.push ']'
result.push '}'
return result.join ''
@@ -0,0 +1,29 @@
###
# Crafting Guide - inventory_parser.coffee
#
# Copyright (c) 2014 by Redwood Labs
# All rights reserved.
###
Inventory = require './inventory'
########################################################################################################################
module.exports = class InventoryParser
@ITEM_REGEX = /^([0-9]+)(.*)$/
parse: (data, inventory=null)->
inventory ?= new Inventory
return inventory if not data? or data.length is 0
lines = data.split '\n'
for line in lines
match = InventoryParser.ITEM_REGEX.exec line
name = if match? then match[2].trim() else line
quantity = if match? then parseInt(match[1]) else 1
if name.length > 0
inventory.add name, quantity
return inventory
+41 -3
View File
@@ -11,9 +11,47 @@ BaseModel = require './base_model'
module.exports = class Item extends BaseModel
@DEFAULT_STACK_SIZE = 64
constructor: (attributes={}, options={})->
attributes.name ?= ''
attributes.quantity ?= 1
attributes.name ?= ''
attributes.quantity ?= 1
attributes.stackSize ?= Item.DEFAULT_STACK_SIZE
super attributes, options
# Public Methods ###############################################################################
Object.defineProperty this, 'stackQuantity', get:@getStackQuantity
# Public Methods ###############################################################################
canMerge: (item)->
return item.name is @name
merge: (item)->
if not @canMerge(item) then throw new Error "cannot merge #{item} into #{this}"
@quantity += item.quantity
toFormatHash: ->
return result =
name: @name
quantity: @quantity
stackQuantity: @stackQuantity
# Property Methods #############################################################################
getStackQuantity: ->
count = 0
extra = @quantity
while extra > @stackSize
extra -= @stackSize
count += 1
return count:count extra:extra
# Object Overrides #############################################################################
toString: ->
result = "#{@constructor.name} (#{@cid}) { name:\"#{@name}\", quantity:#{@quantity}"
if @stackSize isnt Item.DEFAULT_STACK_SIZE
result += ", stackSize:#{@stackSize}"
result += ' }'
return result
+3 -1
View File
@@ -5,7 +5,8 @@
# All rights reserved.
###
BaseModel = require './base_model'
BaseModel = require './base_model'
CraftingTable = require './crafting_table'
RecipeCatalog = require './recipe_catalog'
########################################################################################################################
@@ -14,4 +15,5 @@ module.exports = class LandingPage extends BaseModel
constructor: (attributes={}, options={})->
attributes.catalog ?= new RecipeCatalog
attributes.table ?= new CraftingTable catalog:attributes.catalog
super attributes, options
@@ -1,81 +0,0 @@
###
# Crafting Guide - v1.coffee
#
# Copyright (c) 2014 by Redwood Labs
# All rights reserved.
###
Item = require '../item'
Recipe = require '../recipe'
RecipeBook = require '../recipe_book'
########################################################################################################################
module.exports = class V1
constructor: ->
@_errorLocation = 'the header information'
parse: (data)->
return @_parseRecipeBook data
# Private Methods ##############################################################################
_parseRecipeBook: (data)->
if not data? then throw new Error 'recipe book data is missing'
if not data.version? then throw new Error 'version is required'
if not data.mod_name? then throw new Error 'mod_name is required'
if not data.mod_version? then throw new Error 'mod_version is required'
if not _.isArray(data.recipes) then throw new Error 'recipes must be an array'
book = new RecipeBook version:data.version, modName:data.mod_name, modVersion:data.mod_version
book.description = data.description or ''
for index in [0...data.recipes.length]
@_errorLocation = "recipe #{index + 1}"
recipeData = data.recipes[index]
book.recipes.push @_parseRecipe recipeData
return book
_parseRecipe: (data, options={})->
if not data? then throw new Error "recipe data is missing for #{@_errorLocation}"
if not data.output? then throw new Error "#{@_errorLocation} is missing output"
if not data.input? then throw new Error "#{@_errorLocation} is missing input"
output = @_parseItemList data.output, field:'output', canBeEmpty:false
@_errorLocation = "recipe for output[0].name"
data.tools ?= []
input = @_parseItemList data.input, field:'input', canBeEmpty:false
tools = @_parseItemList data.tools, field:'tools', canBeEmpty:true
return new Recipe input:input, output:output, tools:tools
_parseItemList: (data, options={})->
if not data? then throw new Error "#{@_errorLocation} must have an #{options.field} field"
if not _.isArray(data) then data = [data]
if data.length is 0 and not options.canBeEmpty
throw new Error "#{options.field} for #{@_errorLocation} cannot be empty"
result = []
for index in [0...data.length]
itemData = data[index]
result.push @_parseItem itemData, field:options.field, index:index
return result
_parseItem: (data, options={})->
errorBase = "#{options.field} element #{options.index} for #{@_errorLocation}"
if not data? then throw new Error "#{errorBase} is missing"
if _.isString(data) then data = [1, data]
if not _.isArray(data) then throw new Error "#{errorBase} must be an array"
if data.length is 1 then data.unshift 1
if data.length isnt 2 then throw new Error "#{errorBase} must have at least one element"
if not _.isNumber(data[0]) then throw new Error "#{errorBase} must start with a number"
return new Item quantity:data[0], name:data[1]
+36
View File
@@ -12,6 +12,42 @@ BaseModel = require './base_model'
module.exports = class Recipe extends BaseModel
constructor: (attributes={}, options={})->
if not attributes.output? then throw new Error "attributes.output is required"
if not attributes.input? then throw new Error "attributes.input is required"
attributes.tools ?= []
super attributes, options
Object.defineProperty this, 'name', get:-> @output[0].name
# Object Overrides #############################################################################
toString: ->
result = [@constructor.name, " (", @cid, ") { name:", @name]
result.push ", input:["
needsDelimiter = false
for inputItem in @input
if needsDelimiter then result.push ', '
result.push inputItem.toString()
needsDelimiter = false
result.push ']'
result.push ", output:["
needsDelimiter = false
for outputItem in @output
if needsDelimiter then result.push ', '
result.push outputItem.toString()
needsDelimiter = false
result.push ']'
if @tools.length > 0
result.push ", tools:["
needsDelimiter = false
for tool in @tools
if needsDelimiter then result.push ', '
result.push tool.toString()
needsDelimiter = false
result.push ']'
result.push '}'
return result.join ''
+73 -1
View File
@@ -5,7 +5,9 @@
# All rights reserved.
###
V1 = require './parser_versions/v1'
Item = require './item'
Recipe = require './recipe'
RecipeBook = require './recipe_book'
########################################################################################################################
@@ -24,3 +26,73 @@ module.exports = class RecipeBookParser
throw new Error "cannot parse version #{data.version} recipe books"
return parser.parse data
########################################################################################################################
module.exports.V1 = class V1
constructor: ->
@_errorLocation = 'the header information'
parse: (data)->
return @_parseRecipeBook data
# Private Methods ##############################################################################
_parseRecipeBook: (data)->
if not data? then throw new Error 'recipe book data is missing'
if not data.version? then throw new Error 'version is required'
if not data.mod_name? then throw new Error 'mod_name is required'
if not data.mod_version? then throw new Error 'mod_version is required'
if not _.isArray(data.recipes) then throw new Error 'recipes must be an array'
book = new RecipeBook version:data.version, modName:data.mod_name, modVersion:data.mod_version
book.description = data.description or ''
for index in [0...data.recipes.length]
@_errorLocation = "recipe #{index + 1}"
recipeData = data.recipes[index]
book.recipes.push @_parseRecipe recipeData
return book
_parseRecipe: (data, options={})->
if not data? then throw new Error "recipe data is missing for #{@_errorLocation}"
if not data.output? then throw new Error "#{@_errorLocation} is missing output"
if not data.input? then throw new Error "#{@_errorLocation} is missing input"
output = @_parseItemList data.output, field:'output', canBeEmpty:false
@_errorLocation = "recipe for output[0].name"
data.tools ?= []
input = @_parseItemList data.input, field:'input', canBeEmpty:false
tools = @_parseItemList data.tools, field:'tools', canBeEmpty:true
return new Recipe input:input, output:output, tools:tools
_parseItemList: (data, options={})->
if not data? then throw new Error "#{@_errorLocation} must have an #{options.field} field"
if not _.isArray(data) then data = [data]
if data.length is 0 and not options.canBeEmpty
throw new Error "#{options.field} for #{@_errorLocation} cannot be empty"
result = []
for index in [0...data.length]
itemData = data[index]
result.push @_parseItem itemData, field:options.field, index:index
return result
_parseItem: (data, options={})->
errorBase = "#{options.field} element #{options.index} for #{@_errorLocation}"
if not data? then throw new Error "#{errorBase} is missing"
if _.isString(data) then data = [1, data]
if not _.isArray(data) then throw new Error "#{errorBase} must be an array"
if data.length is 1 then data.unshift 1
if data.length isnt 2 then throw new Error "#{errorBase} must have at least one element"
if not _.isNumber(data[0]) then throw new Error "#{errorBase} must start with a number"
return new Item quantity:data[0], name:data[1]