Added CraftingTable* components and UI
This commit is contained in:
@@ -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}"
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
@@ -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 ''
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user