Added CraftingTable* components and UI
This commit is contained in:
@@ -16,12 +16,14 @@ Opacity.hidden = 1e-6
|
||||
Opacity.shown = 1
|
||||
|
||||
exports.Event = Event = {}
|
||||
Event.change = 'change'
|
||||
Event.add = 'add' # collection, item...
|
||||
Event.change = 'change' # model
|
||||
Event.load = {}
|
||||
Event.load.started = 'book:load:started' # controller, url
|
||||
Event.load.succeeded = 'book:load:succeeded' # controller, book
|
||||
Event.load.failed = 'book:load:failed' # controller, error message
|
||||
Event.load.finished = 'book:load:finished' # controller
|
||||
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...
|
||||
|
||||
exports.DefaultBookUrls = DefaultBookUrls = [
|
||||
'/data/vanilla.json'
|
||||
|
||||
@@ -19,6 +19,7 @@ module.exports = class BaseController extends Backbone.View
|
||||
@_parent = options.parent
|
||||
@_children = []
|
||||
|
||||
Object.defineProperty this, 'rendered', get:-> return @_rendered
|
||||
@_loadTemplate options.templateName
|
||||
super options
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
###
|
||||
# Crafting Guide - crafting_table_controller.coffee
|
||||
#
|
||||
# Copyright (c) 2014 by Redwood Labs
|
||||
# All rights reserved.
|
||||
###
|
||||
|
||||
BaseController = require './base_controller'
|
||||
InventoryParser = require '../models/inventory_parser'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class CraftingTableController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.model? then throw new Error 'options.model is required'
|
||||
options.templateName = 'crafting_table'
|
||||
super options
|
||||
|
||||
@_parser = new InventoryParser
|
||||
@_name = null
|
||||
@_quantity = null
|
||||
@_includingTools = null
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onHaveFieldChanged: ->
|
||||
return unless @rendered
|
||||
|
||||
@model.have.clear()
|
||||
@_parser.parse @$haveField.val(), @model.have
|
||||
@_recalculate()
|
||||
|
||||
onIncludingToolsFieldChanged: ->
|
||||
return unless @rendered
|
||||
@_includingTools = @$('input[name="including_tools"]:checked').length > 0
|
||||
@_recalculate()
|
||||
|
||||
onNameFieldChanged: ->
|
||||
return unless @rendered
|
||||
@_name = @$nameField.val()
|
||||
@_recalculate()
|
||||
|
||||
onQuantityFieldChanged: ->
|
||||
return unless @rendered
|
||||
@_quantity = parseInt @$quantityField.find(":selected").val()
|
||||
@_recalculate()
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$haveField = @$('textarea[name="have"]')
|
||||
@$includingToolsField = @$('input[name="including_tools"]')
|
||||
@$nameField = @$('input[name="name"]')
|
||||
@$quantityField = @$('select[name="quantity"]')
|
||||
|
||||
super
|
||||
|
||||
@onHaveFieldChanged()
|
||||
@onIncludingToolsFieldChanged()
|
||||
@onNameFieldChanged()
|
||||
@onQuantityFieldChanged()
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events:
|
||||
'input textarea[name="have"]': 'onHaveFieldChanged'
|
||||
'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
|
||||
@@ -6,6 +6,7 @@
|
||||
###
|
||||
|
||||
BaseController = require './base_controller'
|
||||
CraftingTableController = require './crafting_table_controller'
|
||||
LandingPage = require '../models/landing_page'
|
||||
RecipeCatalogController = require './recipe_catalog_controller'
|
||||
|
||||
@@ -21,5 +22,6 @@ module.exports = class LandingPageController extends BaseController
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@recipeBooksController = @addChild RecipeCatalogController, '.view__recipe_catalog', model:@model.catalog
|
||||
@catalogController = @addChild RecipeCatalogController, '.view__recipe_catalog', model:@model.catalog
|
||||
@tableController = @addChild CraftingTableController, '.view__crafting_table', model:@model.table
|
||||
super
|
||||
|
||||
@@ -45,8 +45,8 @@ module.exports = class CraftingGuideRouter extends Backbone.Router
|
||||
controller.render()
|
||||
|
||||
$pageContent = $('.page')
|
||||
$pageContent.empty()
|
||||
$pageContent.append controller.$el
|
||||
controller.$el.addClass 'page'
|
||||
$pageContent.replaceWith controller.$el
|
||||
|
||||
controller.$el.fadeIn showDuration, ->
|
||||
controller.onDidShow()
|
||||
|
||||
@@ -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]
|
||||
|
||||
+10
-1
@@ -2,13 +2,22 @@ var jade = jade || require('jade').runtime;
|
||||
|
||||
this["JST"] = this["JST"] || {};
|
||||
|
||||
this["JST"]["crafting_table"] = function template(locals) {
|
||||
var buf = [];
|
||||
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("");
|
||||
};
|
||||
|
||||
this["JST"]["landing_page"] = function template(locals) {
|
||||
var buf = [];
|
||||
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__crafter\"></div>\n</div>");;return buf.join("");
|
||||
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("");
|
||||
};
|
||||
|
||||
this["JST"]["recipe_book"] = function template(locals) {
|
||||
|
||||
Reference in New Issue
Block a user