Move model files to crafting-guide-common
This commit is contained in:
@@ -17,29 +17,6 @@ EXTERNAL_LIBS = [
|
|||||||
'./vendor/email.js:emailjs'
|
'./vendor/email.js:emailjs'
|
||||||
]
|
]
|
||||||
|
|
||||||
PUBLISHED_FILES = [
|
|
||||||
'./client/models/base_model.coffee'
|
|
||||||
'./client/models/game/inventory.coffee'
|
|
||||||
'./client/models/game/item.coffee'
|
|
||||||
'./client/models/game/item_slug.coffee'
|
|
||||||
'./client/models/game/mod.coffee'
|
|
||||||
'./client/models/game/mod_version.coffee'
|
|
||||||
'./client/models/game/multiblock.coffee'
|
|
||||||
'./client/models/game/recipe.coffee'
|
|
||||||
'./client/models/game/stack.coffee'
|
|
||||||
'./client/models/game/simple_stack.coffee'
|
|
||||||
'./client/models/parsing/command_parser_version_base.coffee'
|
|
||||||
'./client/models/parsing/mod_parser.coffee'
|
|
||||||
'./client/models/parsing/mod_parser_v1.coffee'
|
|
||||||
'./client/models/parsing/mod_version_parser.coffee'
|
|
||||||
'./client/models/parsing/mod_version_parser_v1.coffee'
|
|
||||||
'./client/models/parsing/versioned_parser_base.coffee'
|
|
||||||
'./client/models/site/tutorial.coffee'
|
|
||||||
'./common/constants.coffee'
|
|
||||||
'./common/underscore.coffee'
|
|
||||||
'./index.coffee'
|
|
||||||
]
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
module.exports = (grunt)->
|
module.exports = (grunt)->
|
||||||
@@ -61,15 +38,6 @@ module.exports = (grunt)->
|
|||||||
{expand: true, cwd:'./build/static', src:'**/*.txt', dest:'./dist/'}
|
{expand: true, cwd:'./build/static', src:'**/*.txt', dest:'./dist/'}
|
||||||
]
|
]
|
||||||
|
|
||||||
coffee:
|
|
||||||
npm_package:
|
|
||||||
options: {
|
|
||||||
bare: true
|
|
||||||
}
|
|
||||||
files: [
|
|
||||||
{expand:true, cwd:'./src', src:PUBLISHED_FILES, dest:'./dist', ext:'.js'}
|
|
||||||
]
|
|
||||||
|
|
||||||
copy:
|
copy:
|
||||||
assets_build:
|
assets_build:
|
||||||
files: [
|
files: [
|
||||||
@@ -216,9 +184,6 @@ module.exports = (grunt)->
|
|||||||
grunt.registerTask 'dist', 'build the project to be run from Amazon S3',
|
grunt.registerTask 'dist', 'build the project to be run from Amazon S3',
|
||||||
['build', 'copy:build_to_dist', 'uglify', 'compress']
|
['build', 'copy:build_to_dist', 'uglify', 'compress']
|
||||||
|
|
||||||
grunt.registerTask 'prepublish', 'build the project to be published to NPM as shared code',
|
|
||||||
['clean', 'coffee']
|
|
||||||
|
|
||||||
grunt.registerTask 'publish', 'publish the project to NPM',
|
grunt.registerTask 'publish', 'publish the project to NPM',
|
||||||
['prepublish', 'script:publish', 'clean', 'build']
|
['prepublish', 'script:publish', 'clean', 'build']
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -6,7 +6,6 @@
|
|||||||
"repository": "http://github.com/andrewminer/crafting-guide",
|
"repository": "http://github.com/andrewminer/crafting-guide",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "grunt build",
|
"build": "grunt build",
|
||||||
"prepublish": "grunt prepublish",
|
|
||||||
"start": "grunt start",
|
"start": "grunt start",
|
||||||
"test": "grunt test"
|
"test": "grunt test"
|
||||||
},
|
},
|
||||||
@@ -37,7 +36,7 @@
|
|||||||
"body-parser": "^1.13.3",
|
"body-parser": "^1.13.3",
|
||||||
"client-sessions": "^0.7.0",
|
"client-sessions": "^0.7.0",
|
||||||
"cookie-parser": "^1.3.5",
|
"cookie-parser": "^1.3.5",
|
||||||
"crafting-guide-common": "^2.0.0",
|
"crafting-guide-common": "^3.2.0",
|
||||||
"dotenv": "^2.0.0",
|
"dotenv": "^2.0.0",
|
||||||
"express": "^4.13.3",
|
"express": "^4.13.3",
|
||||||
"express-session": "^1.11.3",
|
"express-session": "^1.11.3",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ global.π = Math.PI
|
|||||||
global.ε = 0.0001
|
global.ε = 0.0001
|
||||||
global.w = require 'when'
|
global.w = require 'when'
|
||||||
|
|
||||||
{Logger} = require 'crafting-guide-common'
|
{Logger} = require('crafting-guide-common').util
|
||||||
global.logger = new Logger
|
global.logger = new Logger
|
||||||
|
|
||||||
Tracker = require './tracker'
|
Tracker = require './tracker'
|
||||||
@@ -63,7 +63,7 @@ storage = new Storage storage:global.localStorage
|
|||||||
|
|
||||||
tracker.trackPageView()
|
tracker.trackPageView()
|
||||||
|
|
||||||
{CraftingGuideClient} = require 'crafting-guide-common'
|
{CraftingGuideClient} = require('crafting-guide-common').api
|
||||||
client = _(new CraftingGuideClient(baseUrl:apiBaseUrl)).extend Backbone.Events
|
client = _(new CraftingGuideClient(baseUrl:apiBaseUrl)).extend Backbone.Events
|
||||||
client.onStatusChanged = (client, oldStatus, newStatus)->
|
client.onStatusChanged = (client, oldStatus, newStatus)->
|
||||||
logger.info "Crafting Guide server status changed from #{oldStatus} to #{newStatus}"
|
logger.info "Crafting Guide server status changed from #{oldStatus} to #{newStatus}"
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - base_model.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class BaseModel extends Backbone.Model
|
|
||||||
|
|
||||||
@_loadingQueue = []
|
|
||||||
@_isDraining = false
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
options.logEvents ?= true
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
makeGetter = (name)-> return -> @get name
|
|
||||||
makeSetter = (name)-> return (value)-> @set name, value
|
|
||||||
for name, value of attributes
|
|
||||||
continue if name is 'id'
|
|
||||||
Object.defineProperty this, name, get:makeGetter(name), set:makeSetter(name)
|
|
||||||
|
|
||||||
@fileCache = options.fileCache or null
|
|
||||||
@loading = null
|
|
||||||
@logEvents = options.logEvents or false
|
|
||||||
@state = c.modelState.unloaded
|
|
||||||
|
|
||||||
Object.defineProperties this,
|
|
||||||
isUnloaded: { get:-> @state is c.modelState.unloaded }
|
|
||||||
isLoading: { get:-> @state is c.modelState.loading }
|
|
||||||
isLoaded: { get:-> @state is c.modelState.loaded }
|
|
||||||
isError: { get:-> @state is c.modelState.error }
|
|
||||||
|
|
||||||
# Event Methods ################################################################################
|
|
||||||
|
|
||||||
onLoadSucceeded: (text, status, xhr)->
|
|
||||||
try
|
|
||||||
@set @parse text
|
|
||||||
|
|
||||||
@state = c.modelState.loaded
|
|
||||||
@trigger c.event.change, this
|
|
||||||
@trigger c.event.sync, this
|
|
||||||
logger.info => "#{@constructor.name}.#{@cid} loaded successfully"
|
|
||||||
catch e
|
|
||||||
logger.error -> "A parsing error occured: #{e.stack}"
|
|
||||||
@onLoadFailed e.message, 'parsing failed', xhr
|
|
||||||
|
|
||||||
onLoadFailed: (error, status, xhr)->
|
|
||||||
@state = c.modelState.error
|
|
||||||
logger.error => "#{@constructor.name}.#{@cid} failed to load: status:#{status}, message:#{error}"
|
|
||||||
@trigger c.event.error, this, error
|
|
||||||
|
|
||||||
# Backbone.Model Overrides #####################################################################
|
|
||||||
|
|
||||||
fetch: (options={})->
|
|
||||||
options.force ?= false
|
|
||||||
return if (@isLoading or @isLoaded) and not options.force
|
|
||||||
|
|
||||||
url = @url()
|
|
||||||
logger.info => "#{@constructor.name}.#{@cid} reading from url: #{url}"
|
|
||||||
|
|
||||||
@state = c.modelState.loading
|
|
||||||
@trigger c.event.request, this
|
|
||||||
|
|
||||||
loadFromServer = =>
|
|
||||||
w.promise (resolve, reject)=>
|
|
||||||
$.ajax
|
|
||||||
url: url
|
|
||||||
dataType: 'text'
|
|
||||||
success: (text, status, xhr)=> resolve @onLoadSucceeded text, status, xhr
|
|
||||||
error: (xhr, status, error)=> reject @onLoadFailed error, status, xhr
|
|
||||||
|
|
||||||
if @fileCache?
|
|
||||||
@loading = @fileCache.loading.then =>
|
|
||||||
if @fileCache.hasFile url
|
|
||||||
return @_addToLoadingQueue @fileCache.getFile(url), 'success', {url:url}
|
|
||||||
else
|
|
||||||
@loading = loadFromServer()
|
|
||||||
else
|
|
||||||
@loading = loadFromServer()
|
|
||||||
|
|
||||||
@loading.catch (e)-> # do nothing
|
|
||||||
return @loading
|
|
||||||
|
|
||||||
parse: (text)->
|
|
||||||
return JSON.parse text
|
|
||||||
|
|
||||||
sync: (method, model)->
|
|
||||||
throw new Error "#{@constructor.name}.#{@cid} is not permitted to #{method}"
|
|
||||||
|
|
||||||
trigger: (name, model, args...)->
|
|
||||||
if @logEvents
|
|
||||||
argText = ("#{arg}"[0..50] for arg in args).join ", "
|
|
||||||
logger.trace => "#{@constructor.name}.#{@cid} triggered event #{name} with args: #{argText}"
|
|
||||||
super
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "#{@constructor.name}.#{@cid}"
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_addToLoadingQueue: (text, status, xhr)->
|
|
||||||
deferred = w.defer()
|
|
||||||
|
|
||||||
BaseModel._loadingQueue.push resolve:deferred.resolve, func:(=> @onLoadSucceeded text, status, xhr)
|
|
||||||
@_drainLoadingQueue()
|
|
||||||
return deferred.promise
|
|
||||||
|
|
||||||
_drainLoadingQueue: ->
|
|
||||||
return if @_isDraining
|
|
||||||
@_isDraining = true
|
|
||||||
|
|
||||||
drainDelay = 50
|
|
||||||
drain = =>
|
|
||||||
toLoad = BaseModel._loadingQueue.shift()
|
|
||||||
if not toLoad?
|
|
||||||
@_isDraining = false
|
|
||||||
else
|
|
||||||
toLoad.func()
|
|
||||||
toLoad.resolve(true)
|
|
||||||
|
|
||||||
_.delay drain, drainDelay
|
|
||||||
|
|
||||||
_.delay drain, drainDelay
|
|
||||||
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - converter.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Item = require "../models/game/item"
|
|
||||||
Mod = require "../models/game/mod"
|
|
||||||
ModPack = require "../models/game/mod_pack"
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Converter
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
convert: (id, displayName, oldModPack)->
|
|
||||||
modSlugToIdMap = {}
|
|
||||||
itemSlugToIdMap = {}
|
|
||||||
|
|
||||||
newModPack = new ModPack id:id, displayName:displayName
|
|
||||||
oldModPack.eachMod (oldMod)=>
|
|
||||||
newMod = new Mod id:_.uniqueId("mod-"), displayName:oldMod.name, modPack:newModPack
|
|
||||||
modSlugToIdMap[oldMod.slug.toString()] = newMod.id
|
|
||||||
|
|
||||||
oldMod.eachItem (oldItem)=>
|
|
||||||
newItem = new Item id:_.uniqueId("item-"), displayName:oldItem.name, mod:newMod
|
|
||||||
itemSlugToIdMap[oldItem.slug.toString()] = newItem.id
|
|
||||||
|
|
||||||
if oldItem.isGatherable?
|
|
||||||
newItem.isGatherable = oldItem.isGatherable
|
|
||||||
|
|
||||||
oldModPack.eachMod (oldMod)=>
|
|
||||||
newMod = newModPack.mods[modSlugToIdMap[oldMod.slug.toString()]]
|
|
||||||
|
|
||||||
|
|
||||||
return newModPack
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_convertItem: (oldItem, newMod)->
|
|
||||||
|
|
||||||
_convertMod: (oldMod, newModPack)->
|
|
||||||
return newMod
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - event_recorder.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
util = require 'util'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class EventRecorder
|
|
||||||
|
|
||||||
constructor: (model)->
|
|
||||||
if not model? then throw new Error 'model is required'
|
|
||||||
|
|
||||||
@model = model
|
|
||||||
@events = []
|
|
||||||
|
|
||||||
@model.on 'all', (event, model, args...)=>
|
|
||||||
logger.verbose -> "#{model?.constructor?.name}(#{model?.cid}) emitted #{event}
|
|
||||||
with args: #{util.inspect(args)}"
|
|
||||||
@events.push id:model?.cid, event:event, args:args
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
reset: ->
|
|
||||||
@events = []
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
getNames: ->
|
|
||||||
return (e.event for e in @events)
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
names: {get:@prototype.getNames}
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - inventory.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
Stack = require './stack'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Inventory extends BaseModel
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
super attributes, options
|
|
||||||
attributes.modPack ?= null
|
|
||||||
@clear()
|
|
||||||
|
|
||||||
if options.clone?
|
|
||||||
@addInventory options.clone
|
|
||||||
|
|
||||||
# Class Methods ################################################################################
|
|
||||||
|
|
||||||
@Delimiters =
|
|
||||||
Item: '.'
|
|
||||||
Stack: ':'
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
add: (itemSlug, quantity=1, options={})->
|
|
||||||
return this unless quantity > 0
|
|
||||||
|
|
||||||
@_add itemSlug, quantity, options
|
|
||||||
@trigger c.event.add, this, itemSlug, quantity
|
|
||||||
@trigger c.event.change, this
|
|
||||||
return this
|
|
||||||
|
|
||||||
addInventory: (inventory)->
|
|
||||||
inventory.each (stack)=> @_add stack.itemSlug, stack.quantity
|
|
||||||
|
|
||||||
@trigger c.event.change, this
|
|
||||||
return this
|
|
||||||
|
|
||||||
clear: (options={})->
|
|
||||||
@_stacks = {}
|
|
||||||
@_itemSlugs = []
|
|
||||||
|
|
||||||
@trigger c.event.change, this
|
|
||||||
|
|
||||||
clone: ->
|
|
||||||
inventory = new Inventory
|
|
||||||
inventory.addInventory this
|
|
||||||
return inventory
|
|
||||||
|
|
||||||
each: (callback)->
|
|
||||||
for itemSlug in @_itemSlugs
|
|
||||||
stack = @_stacks[itemSlug]
|
|
||||||
continue unless stack?
|
|
||||||
callback stack
|
|
||||||
|
|
||||||
getSlugs: ->
|
|
||||||
return @_itemSlugs[..]
|
|
||||||
|
|
||||||
hasAtLeast: (itemSlug, quantity=1)->
|
|
||||||
if quantity is 0 then return true
|
|
||||||
|
|
||||||
stack = @_stacks[itemSlug]
|
|
||||||
return false unless stack?
|
|
||||||
return stack.quantity >= quantity
|
|
||||||
|
|
||||||
localize: ->
|
|
||||||
if not @modPack? then throw new Error 'localize requires @modPack'
|
|
||||||
|
|
||||||
changed = false
|
|
||||||
newSlugs = []
|
|
||||||
newStacks = []
|
|
||||||
for itemSlug in @_itemSlugs
|
|
||||||
stack = @_stacks[itemSlug]
|
|
||||||
continue unless stack?
|
|
||||||
|
|
||||||
qualifiedSlug = if itemSlug.isQualified then itemSlug else null
|
|
||||||
if not qualifiedSlug?
|
|
||||||
qualifiedSlug = @modPack.findItem(itemSlug)?.slug
|
|
||||||
changed = qualifiedSlug?
|
|
||||||
|
|
||||||
if qualifiedSlug?
|
|
||||||
newSlugs.push qualifiedSlug
|
|
||||||
newStacks.push new Stack itemSlug:qualifiedSlug, quantity:stack.quantity
|
|
||||||
else
|
|
||||||
newSlugs.push itemSlug
|
|
||||||
newStacks.push stack
|
|
||||||
|
|
||||||
if changed
|
|
||||||
for itemSlug, stack of @_stacks
|
|
||||||
@stopListening stack
|
|
||||||
|
|
||||||
@_itemSlugs = newSlugs
|
|
||||||
@_stacks = {}
|
|
||||||
for stack in newStacks
|
|
||||||
@_stacks[stack.itemSlug] = stack
|
|
||||||
@listenTo stack, c.event.change, => @trigger c.event.change, this
|
|
||||||
|
|
||||||
@_sort()
|
|
||||||
@trigger c.event.change, this
|
|
||||||
|
|
||||||
pop: ->
|
|
||||||
itemSlug = @_itemSlugs.pop()
|
|
||||||
return null unless itemSlug?
|
|
||||||
|
|
||||||
stack = @_stacks[itemSlug]
|
|
||||||
delete @_stacks[itemSlug]
|
|
||||||
|
|
||||||
@trigger c.event.remove, this, stack.itemSlug, stack.quantity
|
|
||||||
@trigger c.event.change, this
|
|
||||||
return stack
|
|
||||||
|
|
||||||
quantityOf: (itemSlug)->
|
|
||||||
stack = @_stacks[itemSlug]
|
|
||||||
return stack.quantity if stack
|
|
||||||
return 0
|
|
||||||
|
|
||||||
remove: (itemSlug, quantity=null)->
|
|
||||||
stack = @_stacks[itemSlug]
|
|
||||||
return this unless stack?
|
|
||||||
|
|
||||||
quantity ?= stack.quantity
|
|
||||||
return this unless quantity > 0
|
|
||||||
|
|
||||||
if stack.quantity < quantity
|
|
||||||
throw new Error "cannot remove #{quantity}: only #{stack.quantity} #{itemSlug} in this inventory"
|
|
||||||
|
|
||||||
stack.quantity -= quantity
|
|
||||||
if stack.quantity is 0
|
|
||||||
@stopListening stack
|
|
||||||
delete @_stacks[itemSlug]
|
|
||||||
@_itemSlugs = (s for s in @_itemSlugs when not ItemSlug.equal(s, itemSlug))
|
|
||||||
|
|
||||||
@trigger c.event.remove, this, itemSlug, quantity
|
|
||||||
@trigger c.event.change, this
|
|
||||||
return this
|
|
||||||
|
|
||||||
toDescription: ->
|
|
||||||
return null if @isEmpty
|
|
||||||
return null unless @modPack?
|
|
||||||
|
|
||||||
item = @modPack.findItem @_itemSlugs[0]
|
|
||||||
extras = @_itemSlugs.length - 1
|
|
||||||
|
|
||||||
result = "#{item.name}"
|
|
||||||
if extras > 0 then result += " and #{extras} more..."
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Parsing Methods ##############################################################################
|
|
||||||
|
|
||||||
parse: (data)->
|
|
||||||
return this if not data? or data.length is 0
|
|
||||||
|
|
||||||
stacks = data.split Inventory.Delimiters.Stack
|
|
||||||
for stackText in stacks
|
|
||||||
stackParts = stackText.split Inventory.Delimiters.Item
|
|
||||||
if stackParts.length is 2
|
|
||||||
quantity = parseInt stackParts[0], 10
|
|
||||||
itemSlug = ItemSlug.slugify stackParts[1]
|
|
||||||
else if stackParts.length is 1
|
|
||||||
quantity = 1
|
|
||||||
itemSlug = ItemSlug.slugify stackParts[0]
|
|
||||||
else
|
|
||||||
throw new Error "expected #{stackText} to have 0 or 1 parts"
|
|
||||||
|
|
||||||
if itemSlug.qualified.length > 0
|
|
||||||
@add itemSlug, quantity
|
|
||||||
|
|
||||||
return this
|
|
||||||
|
|
||||||
unparse: (options={})->
|
|
||||||
parts = []
|
|
||||||
@each (stack)=>
|
|
||||||
slugText = stack.itemSlug.item
|
|
||||||
if @modPack?
|
|
||||||
item = @modPack.findItem ItemSlug.slugify slugText
|
|
||||||
if item? and item.slug.qualified isnt stack.itemSlug.qualified
|
|
||||||
slugText = stack.itemSlug.qualified
|
|
||||||
|
|
||||||
if stack.quantity is 1
|
|
||||||
parts.push slugText
|
|
||||||
else
|
|
||||||
parts.push "#{stack.quantity}#{Inventory.Delimiters.Item}#{slugText}"
|
|
||||||
|
|
||||||
return parts.join Inventory.Delimiters.Stack
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
isEmpty:
|
|
||||||
get: -> @_itemSlugs.length is 0
|
|
||||||
|
|
||||||
totalQuantity:
|
|
||||||
get: ->
|
|
||||||
total = 0
|
|
||||||
@each (stack)->
|
|
||||||
total += stack.quantity
|
|
||||||
return total
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
result = [@constructor.name, " (", @cid, ") {items: ["]
|
|
||||||
|
|
||||||
needsDelimiter = false
|
|
||||||
@each (stack)->
|
|
||||||
if needsDelimiter then result.push ', '
|
|
||||||
result.push stack.toString()
|
|
||||||
needsDelimiter = true
|
|
||||||
result.push ']'
|
|
||||||
|
|
||||||
result.push '}'
|
|
||||||
return result.join ''
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_add: (itemSlug, quantity=1, options={})->
|
|
||||||
options.insert ?= false
|
|
||||||
return unless itemSlug?
|
|
||||||
return unless quantity > 0
|
|
||||||
|
|
||||||
stack = @_stacks[itemSlug]
|
|
||||||
if not stack?
|
|
||||||
stack = new Stack itemSlug:itemSlug, quantity:quantity
|
|
||||||
@listenTo stack, c.event.change, => @trigger c.event.change, this
|
|
||||||
@_stacks[itemSlug] = stack
|
|
||||||
if options.insert
|
|
||||||
@_itemSlugs.unshift itemSlug
|
|
||||||
else
|
|
||||||
@_itemSlugs.push itemSlug
|
|
||||||
@_sort()
|
|
||||||
else
|
|
||||||
stack.quantity += quantity
|
|
||||||
|
|
||||||
_sort: ->
|
|
||||||
@_itemSlugs.sort (a, b)-> ItemSlug.compare a, b
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - item.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
Recipe = require './recipe'
|
|
||||||
{StringBuilder} = require 'crafting-guide-common'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Item extends BaseModel
|
|
||||||
|
|
||||||
@Group = Other:'Other'
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
if not attributes.name? then throw new Error 'attributes.name is required'
|
|
||||||
|
|
||||||
attributes.description ?= null
|
|
||||||
attributes.group ?= Item.Group.Other
|
|
||||||
attributes.ignoreDuringCrafting ?= false
|
|
||||||
attributes.isGatherable ?= false
|
|
||||||
attributes.modVersion ?= null
|
|
||||||
attributes.officialUrl ?= null
|
|
||||||
attributes.slug ?= ItemSlug.slugify attributes.name
|
|
||||||
attributes.videos ?= []
|
|
||||||
|
|
||||||
options.logEvents ?= false
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
@on c.event.change + ':modVersion', =>
|
|
||||||
@_isCraftable = null
|
|
||||||
@slug.mod = @modVersion?.modSlug
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
compareTo: (that)->
|
|
||||||
if this.slug isnt that.slug
|
|
||||||
return if this.slug < that.slug then -1 else +1
|
|
||||||
if this.name isnt that.name
|
|
||||||
return if this.name < that.name then -1 else +1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
unparse: ->
|
|
||||||
ItemParser = require '../parsing/item_parser' # to avoid require cycles
|
|
||||||
@_parser ?= new ItemParser model:this
|
|
||||||
return @_parser.unparse()
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
getIsCraftable: ->
|
|
||||||
if not @_isCraftable?
|
|
||||||
@_isCraftable = false
|
|
||||||
if @modVersion?
|
|
||||||
@_isCraftable = @modVersion.hasRecipes @slug
|
|
||||||
|
|
||||||
return @_isCraftable
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
isCraftable: {get:@prototype.getIsCraftable}
|
|
||||||
|
|
||||||
# Backbone.Model Overrides #####################################################################
|
|
||||||
|
|
||||||
parse: (text)->
|
|
||||||
ItemParser = require '../parsing/item_parser' # to avoid require cycles
|
|
||||||
@_parser ?= new ItemParser model:this
|
|
||||||
@_parser.parse text
|
|
||||||
|
|
||||||
return null # prevent calling `set`
|
|
||||||
|
|
||||||
url: ->
|
|
||||||
return c.url.itemData modSlug:@slug.mod, itemSlug:@slug.item
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
builder = new StringBuilder
|
|
||||||
return builder
|
|
||||||
.push @constructor.name, ' (', @cid, ') { '
|
|
||||||
.push 'name:"', @name, '", '
|
|
||||||
.push 'isCraftable:', @isCraftable, ', '
|
|
||||||
.push 'isGatherable:', @isGatherable, ', '
|
|
||||||
.onlyIf (@group isnt Item.Group.Other), (b)=>
|
|
||||||
b.push 'group:"', @group, '", '
|
|
||||||
.push 'slug:"', @slug, '", '
|
|
||||||
.push '}'
|
|
||||||
.toString()
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - item_slug.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ItemSlug
|
|
||||||
|
|
||||||
constructor: ->
|
|
||||||
@_item = @_mod = null
|
|
||||||
|
|
||||||
if arguments.length is 1
|
|
||||||
parts = _.decomposeSlug arguments[0]
|
|
||||||
@_mod = _.slugify parts[0]
|
|
||||||
@item = _.slugify parts[1]
|
|
||||||
else if arguments.length is 2
|
|
||||||
@_mod = arguments[0]
|
|
||||||
@item = arguments[1]
|
|
||||||
else
|
|
||||||
throw new Error 'expected arguments to be "modSlug, itemSlug" or just "itemSlug"'
|
|
||||||
|
|
||||||
# Class Methods ################################################################################
|
|
||||||
|
|
||||||
@compare: (a, b)->
|
|
||||||
if a.item isnt b.item
|
|
||||||
return if a.item < b.item then -1 else +1
|
|
||||||
if a.mod isnt b.mod
|
|
||||||
return if a.mod < b.mod then -1 else +1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
@equal: (a, b)->
|
|
||||||
return true if not a? and not b?
|
|
||||||
return false unless a? and b?
|
|
||||||
return false unless a.mod is b.mod
|
|
||||||
return false unless a.item is b.item
|
|
||||||
return true
|
|
||||||
|
|
||||||
@slugify: (arg)->
|
|
||||||
return arg if arg?.constructor?.name is 'ItemSlug'
|
|
||||||
|
|
||||||
[modSlug, itemSlug] = _.decomposeSlug arg
|
|
||||||
itemSlug = _.slugify itemSlug
|
|
||||||
|
|
||||||
if modSlug?
|
|
||||||
return new ItemSlug modSlug, itemSlug
|
|
||||||
else
|
|
||||||
return new ItemSlug itemSlug
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
compareTo: (that)->
|
|
||||||
return ItemSlug.compare this, that
|
|
||||||
|
|
||||||
matches: (slug, options={exact:false})->
|
|
||||||
return false unless slug?
|
|
||||||
return false unless typeof(slug.matches) is 'function'
|
|
||||||
|
|
||||||
if slug.isQualified and this.isQualified
|
|
||||||
return slug.qualified is this.qualified
|
|
||||||
else
|
|
||||||
return slug.item is this.item
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
isQualified:
|
|
||||||
get: -> @_mod?
|
|
||||||
|
|
||||||
item:
|
|
||||||
get: -> @_item
|
|
||||||
|
|
||||||
set: (newItem)->
|
|
||||||
if not newItem? then throw new Error 'item is required'
|
|
||||||
@_item = newItem
|
|
||||||
@mod = @mod # reset @_qualified
|
|
||||||
|
|
||||||
mod:
|
|
||||||
get: -> @_mod
|
|
||||||
|
|
||||||
set: (newMod)->
|
|
||||||
@_mod = newMod
|
|
||||||
@_qualified = if @_mod? then _.composeSlugs(@_mod, @_item) else @_item
|
|
||||||
|
|
||||||
qualified:
|
|
||||||
get: -> @_qualified
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return @_qualified
|
|
||||||
|
|
||||||
valueOf: ->
|
|
||||||
return @_qualified.valueOf()
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Mod extends BaseModel
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
if not attributes.slug? then throw new Error 'attributes.slug is required'
|
|
||||||
|
|
||||||
attributes.author ?= ''
|
|
||||||
attributes.description ?= ''
|
|
||||||
attributes.documentationUrl ?= null
|
|
||||||
attributes.downloadUrl ?= null
|
|
||||||
attributes.homePageUrl ?= null
|
|
||||||
attributes.modPack ?= null
|
|
||||||
attributes.name ?= ''
|
|
||||||
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
@_activeModVersion = null
|
|
||||||
@_activeVersion = null
|
|
||||||
@_modVersions = []
|
|
||||||
@_tutorials = []
|
|
||||||
|
|
||||||
# Class Methods ##################################################################################
|
|
||||||
|
|
||||||
@Version: Version =
|
|
||||||
None: 'none'
|
|
||||||
Latest: 'latest'
|
|
||||||
|
|
||||||
# Public Methods #################################################################################
|
|
||||||
|
|
||||||
compareTo: (that)->
|
|
||||||
thisRequired = this.slug in c.requiredMods
|
|
||||||
thatRequired = that.slug in c.requiredMods
|
|
||||||
|
|
||||||
if thisRequired isnt thatRequired
|
|
||||||
return -1 if thisRequired
|
|
||||||
return +1 if thatRequired
|
|
||||||
else if this.slug isnt that.slug
|
|
||||||
return if this.slug < that.slug then -1 else +1
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
activeModVersion:
|
|
||||||
get: -> @_activeModVersion
|
|
||||||
|
|
||||||
activeVersion:
|
|
||||||
get: ->
|
|
||||||
return @_activeVersion
|
|
||||||
|
|
||||||
set: (version)->
|
|
||||||
return if version is @_activeVersion
|
|
||||||
|
|
||||||
version ?= Mod.Version.None
|
|
||||||
if version is Mod.Version.Latest then version = _.last(@_modVersions).version
|
|
||||||
|
|
||||||
if version is Mod.Version.None
|
|
||||||
@_activeVersion = version
|
|
||||||
@_activateModVersion null
|
|
||||||
|
|
||||||
@trigger c.event.change + ':activeVersion', this, @_activeVersion
|
|
||||||
@trigger c.event.change, this
|
|
||||||
else
|
|
||||||
for modVersion in @_modVersions
|
|
||||||
if version is modVersion.version
|
|
||||||
@_activateModVersion modVersion
|
|
||||||
break
|
|
||||||
|
|
||||||
@_activeVersion = version
|
|
||||||
@trigger c.event.change + ':activeVersion', this, @_activeVersion
|
|
||||||
@trigger c.event.change, this
|
|
||||||
|
|
||||||
enabled:
|
|
||||||
get: -> @_activeModVersion?
|
|
||||||
|
|
||||||
modVersions:
|
|
||||||
get: -> @_modVersions[..]
|
|
||||||
|
|
||||||
tutorials:
|
|
||||||
get: -> @getAllTutorials()
|
|
||||||
|
|
||||||
# Item Methods #################################################################################
|
|
||||||
|
|
||||||
chooseRandomItem: ->
|
|
||||||
effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest
|
|
||||||
return null unless effectiveModVersion?
|
|
||||||
|
|
||||||
return effectiveModVersion.chooseRandomItem()
|
|
||||||
|
|
||||||
eachItem: (callback)->
|
|
||||||
effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest
|
|
||||||
effectiveModVersion.eachItem callback
|
|
||||||
|
|
||||||
findItem: (slug, options={})->
|
|
||||||
options.includeDisabled ?= false
|
|
||||||
options.enableAsNeeded ?= false
|
|
||||||
|
|
||||||
if not options.includeDisabled
|
|
||||||
return unless @_activeModVersion?
|
|
||||||
return @_activeModVersion.findItem slug
|
|
||||||
else
|
|
||||||
for modVersion in @_modVersions
|
|
||||||
modVersion.fetch()
|
|
||||||
|
|
||||||
item = modVersion.findItem slug
|
|
||||||
if item?
|
|
||||||
if options.enableAsNeeded then @setActiveVersion modVersion.version
|
|
||||||
return item
|
|
||||||
|
|
||||||
return null
|
|
||||||
|
|
||||||
findItemByName: (name)->
|
|
||||||
return unless @_activeModVersion?
|
|
||||||
@_activeModVersion.findItemByName name
|
|
||||||
|
|
||||||
# ModVersion Methods ###########################################################################
|
|
||||||
|
|
||||||
addModVersion: (modVersion)->
|
|
||||||
return unless modVersion?
|
|
||||||
return if @_modVersions.indexOf(modVersion) isnt -1
|
|
||||||
|
|
||||||
@_modVersions.push modVersion
|
|
||||||
@listenTo modVersion, c.event.change, => @trigger c.event.change, this
|
|
||||||
modVersion.fileCache = this.fileCache
|
|
||||||
modVersion.mod = this
|
|
||||||
|
|
||||||
@trigger c.event.add + ':modVersion', modVersion, this
|
|
||||||
@trigger c.event.change + ':version', modVersion, this
|
|
||||||
@trigger c.event.change, this
|
|
||||||
|
|
||||||
if not @activeVersion? then @activeVersion = modVersion.version
|
|
||||||
if modVersion.version is @_activeVersion then @_activateModVersion modVersion
|
|
||||||
return this
|
|
||||||
|
|
||||||
eachModVersion: (callback)->
|
|
||||||
for modVersion in @_modVersions
|
|
||||||
callback modVersion
|
|
||||||
|
|
||||||
getAllModVersions: ->
|
|
||||||
return @_modVersions[..]
|
|
||||||
|
|
||||||
getModVersion: (version)->
|
|
||||||
return null if version is Mod.Version.None
|
|
||||||
return @_modVersions[0] if version is Mod.Version.Latest
|
|
||||||
|
|
||||||
for modVersion in @_modVersions
|
|
||||||
return modVersion if modVersion.version is version
|
|
||||||
|
|
||||||
return null
|
|
||||||
|
|
||||||
# Name Methods #################################################################################
|
|
||||||
|
|
||||||
eachName: (callback)->
|
|
||||||
effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest
|
|
||||||
effectiveModVersion.eachName callback
|
|
||||||
|
|
||||||
findName: (itemSlug)->
|
|
||||||
return unless @_activeModVersion?
|
|
||||||
@_activeModVersion.findName itemSlug
|
|
||||||
|
|
||||||
# Recipe Methods ###############################################################################
|
|
||||||
|
|
||||||
eachRecipe: (callback)->
|
|
||||||
effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest
|
|
||||||
effectiveModVersion.eachRecipe callback
|
|
||||||
|
|
||||||
findRecipes: (itemSlug, result=[], options={})->
|
|
||||||
options.alwaysFromOwningMod ?= false
|
|
||||||
|
|
||||||
if @_activeModVersion?
|
|
||||||
return @_activeModVersion.findRecipes itemSlug, result, options
|
|
||||||
else if options.alwaysFromOwningMod and itemSlug.mod is @slug
|
|
||||||
return @getModVersion(Mod.Version.Latest).findRecipes itemSlug, result, options
|
|
||||||
|
|
||||||
return null
|
|
||||||
|
|
||||||
# Tutorial Methods #############################################################################
|
|
||||||
|
|
||||||
addTutorial: (tutorial)->
|
|
||||||
return unless tutorial?
|
|
||||||
if @getTutorial(tutorial.slug)? then throw new Error "duplicate tutorial: #{tutorial.name}"
|
|
||||||
@_tutorials.push tutorial
|
|
||||||
tutorial.modSlug = @slug
|
|
||||||
|
|
||||||
getAllTutorials: ->
|
|
||||||
return @_tutorials[..]
|
|
||||||
|
|
||||||
getTutorial: (tutorialSlug)->
|
|
||||||
for tutorial in @_tutorials
|
|
||||||
return tutorial if tutorial.slug is tutorialSlug
|
|
||||||
return null
|
|
||||||
|
|
||||||
# Backbone.Model Overrides #####################################################################
|
|
||||||
|
|
||||||
parse: (text)->
|
|
||||||
ModParser = require '../parsing/mod_parser' # to avoid require cycles
|
|
||||||
@_parser ?= new ModParser model:this
|
|
||||||
@_parser.parse text
|
|
||||||
|
|
||||||
@_verifyActiveModVersion()
|
|
||||||
|
|
||||||
return null # prevent calling `set`
|
|
||||||
|
|
||||||
url: ->
|
|
||||||
return c.url.modData modSlug:@slug
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_activateModVersion: (modVersion)->
|
|
||||||
if @_activeModVersion? then @stopListening @_activeModVersion
|
|
||||||
@_activeModVersion = modVersion
|
|
||||||
@trigger c.event.change + ':activeModVersion', this, @_activeModVersion
|
|
||||||
|
|
||||||
logger.verbose => "#{@slug} switched to version #{@_activeVersion}"
|
|
||||||
|
|
||||||
if @_activeModVersion?
|
|
||||||
@listenTo @_activeModVersion, 'all', -> @trigger.apply this, arguments
|
|
||||||
|
|
||||||
_verifyActiveModVersion: ->
|
|
||||||
if (@_activeVersion isnt Version.None) and (not @_activeModVersion?)
|
|
||||||
logger.warning => "#{@slug} no longer has a version #{@_activeVersion}, using latest instead"
|
|
||||||
@activeVersion = Version.Latest
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_pack.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
Mod = require './mod'
|
|
||||||
ModVersionParser = require '../parsing/mod_version_parser'
|
|
||||||
Recipe = require './recipe'
|
|
||||||
SimpleInventory = require '../crafting/simple_inventory'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ModPack extends BaseModel
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
@_mods = []
|
|
||||||
@_cache = {}
|
|
||||||
|
|
||||||
@on c.event.change, => @_cache = {}
|
|
||||||
|
|
||||||
# Item Methods #################################################################################
|
|
||||||
|
|
||||||
chooseRandomItem: ->
|
|
||||||
return null unless @_mods.length > 0
|
|
||||||
|
|
||||||
modIndex = Math.floor Math.random() * @_mods.length
|
|
||||||
return @_mods[modIndex].chooseRandomItem()
|
|
||||||
|
|
||||||
findItem: (itemSlug, options={})->
|
|
||||||
options.includeDisabled ?= false
|
|
||||||
|
|
||||||
key = "#{itemSlug}-#{options.includeDisabled}"
|
|
||||||
@_cache.itemBySlug ?= {}
|
|
||||||
item = @_cache.itemBySlug[key]
|
|
||||||
return item if item?
|
|
||||||
|
|
||||||
if itemSlug.isQualified
|
|
||||||
mod = @getMod itemSlug.mod
|
|
||||||
if mod?
|
|
||||||
item = mod.findItem itemSlug, options
|
|
||||||
|
|
||||||
if not item?
|
|
||||||
for mod in @_mods
|
|
||||||
continue unless mod.enabled or options.includeDisabled
|
|
||||||
item = mod.findItem itemSlug, options
|
|
||||||
break if item?
|
|
||||||
|
|
||||||
if item?
|
|
||||||
@_cache.itemBySlug[key] = item
|
|
||||||
|
|
||||||
return item
|
|
||||||
|
|
||||||
findItemByName: (name, options={})->
|
|
||||||
options.enableAsNeeded ?= false
|
|
||||||
options.includeDisabled = true if options.enableAsNeeded
|
|
||||||
|
|
||||||
for mod in @_mods
|
|
||||||
continue unless mod.enabled or options.includeDisabled
|
|
||||||
item = mod.findItemByName name, options
|
|
||||||
return item if item?
|
|
||||||
|
|
||||||
return null
|
|
||||||
|
|
||||||
findItemDisplay: (itemSlug)->
|
|
||||||
if not itemSlug? then throw new Error 'itemSlug is required'
|
|
||||||
|
|
||||||
result = {slug:itemSlug}
|
|
||||||
item = @findItem itemSlug, includeDisabled:true
|
|
||||||
if item?
|
|
||||||
result.itemName = item.name
|
|
||||||
result.itemSlug = item.slug.item
|
|
||||||
result.modSlug = item.slug.mod
|
|
||||||
result.modVersion = item.modVersion.version
|
|
||||||
else
|
|
||||||
result.itemName = @findName itemSlug, includeDisabled:true
|
|
||||||
result.itemSlug = itemSlug.item
|
|
||||||
result.modSlug = @_mods[0].slug
|
|
||||||
result.modVersion = @_mods[0].activeVersion
|
|
||||||
|
|
||||||
craftingUrlInventory = new SimpleInventory modPack:this
|
|
||||||
if item?.multiblock?
|
|
||||||
craftingUrlInventory.addInventory item.multiblock.inventory
|
|
||||||
else
|
|
||||||
craftingUrlInventory.add itemSlug
|
|
||||||
|
|
||||||
result.craftingUrl = c.url.crafting inventoryText:craftingUrlInventory.unparse()
|
|
||||||
result.iconUrl = c.url.itemIcon result
|
|
||||||
result.itemUrl = c.url.item result
|
|
||||||
result.modName = @getMod(result.modSlug).name
|
|
||||||
return result
|
|
||||||
|
|
||||||
qualifySlug: (itemSlug)->
|
|
||||||
return itemSlug if itemSlug.isQualified
|
|
||||||
|
|
||||||
item = @findItem itemSlug
|
|
||||||
return item.slug if item?
|
|
||||||
return itemSlug
|
|
||||||
|
|
||||||
# Mod Methods ##################################################################################
|
|
||||||
|
|
||||||
addMod: (mod)->
|
|
||||||
if not mod? then throw new Error 'mod is required'
|
|
||||||
return if @_mods.indexOf(mod) isnt -1
|
|
||||||
|
|
||||||
mod.modPack = this
|
|
||||||
@_mods.push mod
|
|
||||||
@listenTo mod, c.event.change, (modVersion)=> @_onModVersionLoaded modVersion
|
|
||||||
@trigger c.event.add + ':mod', mod, this
|
|
||||||
|
|
||||||
@_mods.sort (a, b)-> a.compareTo b
|
|
||||||
@trigger c.event.sort + ':mod', this
|
|
||||||
@trigger c.event.change, this
|
|
||||||
|
|
||||||
return this
|
|
||||||
|
|
||||||
eachMod: (callback)->
|
|
||||||
for mod in @_mods
|
|
||||||
callback mod
|
|
||||||
|
|
||||||
getMod: (slug)->
|
|
||||||
for mod in @_mods
|
|
||||||
return mod if mod.slug is slug
|
|
||||||
return null
|
|
||||||
|
|
||||||
getAllMods: ->
|
|
||||||
return @_mods[..]
|
|
||||||
|
|
||||||
removeMod: (mod)->
|
|
||||||
index = @_mods.indexOf mod
|
|
||||||
return unless index >= 0
|
|
||||||
|
|
||||||
@_mods.splice index, 1
|
|
||||||
|
|
||||||
@trigger c.event.remove, this, mod.slug
|
|
||||||
@trigger c.event.change, this
|
|
||||||
|
|
||||||
# Name Methods #################################################################################
|
|
||||||
|
|
||||||
findName: (slug, options={})->
|
|
||||||
options.includeDisabled ?= false
|
|
||||||
|
|
||||||
for mod in @_mods
|
|
||||||
continue unless mod.enabled or options.includeDisabled
|
|
||||||
name = mod.findName slug
|
|
||||||
return name if name
|
|
||||||
|
|
||||||
return null
|
|
||||||
|
|
||||||
# Recipe Methods ###############################################################################
|
|
||||||
|
|
||||||
findRecipes: (itemSlug, options={})->
|
|
||||||
options.alwaysFromOwningMod ?= false
|
|
||||||
return null unless itemSlug?
|
|
||||||
|
|
||||||
key = "#{itemSlug}-#{options.alwaysFromOwningMod}"
|
|
||||||
@_cache.recipesBySlug ?= {}
|
|
||||||
result = @_cache.recipesBySlug[key]
|
|
||||||
return result if result?
|
|
||||||
|
|
||||||
result = []
|
|
||||||
for mod in @_mods
|
|
||||||
if not mod.enabled
|
|
||||||
owningMod = itemSlug.isQualified and (itemSlug.mod is mod.slug)
|
|
||||||
continue unless owningMod and options.alwaysFromOwningMod
|
|
||||||
|
|
||||||
mod.findRecipes itemSlug, result, options
|
|
||||||
|
|
||||||
@_cache.recipesBySlug[key] = result
|
|
||||||
return if result.length > 0 then result else null
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "ModPack (#{@cid}) {modVersions:«#{@_mods.length} items»}"
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_onModVersionLoaded: (modVersion)->
|
|
||||||
mods = @getAllMods()
|
|
||||||
return true unless mods.length > 0
|
|
||||||
|
|
||||||
for mod in mods
|
|
||||||
if mod.isError
|
|
||||||
@removeMod mod
|
|
||||||
continue
|
|
||||||
|
|
||||||
modVersions = mod.getAllModVersions()
|
|
||||||
return true unless modVersions.length > 0
|
|
||||||
continue if mod.activeVersion is Mod.Version.None
|
|
||||||
|
|
||||||
activeModVersion = mod.activeModVersion
|
|
||||||
return true unless activeModVersion?
|
|
||||||
return true if activeModVersion.isUnloaded
|
|
||||||
return true if activeModVersion.isLoading
|
|
||||||
|
|
||||||
@trigger c.event.change, this
|
|
||||||
@trigger c.event.sync, this
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_version.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
Item = require './item'
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
Recipe = require './recipe'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ModVersion extends BaseModel
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
if not attributes.modSlug? then throw new Error 'attributes.modSlug is required'
|
|
||||||
if not attributes.version? then throw new Error 'attributes.version is required'
|
|
||||||
attributes.mod ?= null
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
@_groups = {}
|
|
||||||
@_items = {}
|
|
||||||
@_names = {}
|
|
||||||
@_recipes = {}
|
|
||||||
@_slugs = []
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
compareTo: (that)->
|
|
||||||
if this.mod? and that.mod?
|
|
||||||
return this.mod.compareTo that.mod
|
|
||||||
|
|
||||||
if this.modSlug isnt that.modSlug
|
|
||||||
return if this.modSlug < that.modSlug then -1 else +1
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
sort: ->
|
|
||||||
@_slugs.sort (a, b)-> ItemSlug.compare a, b
|
|
||||||
|
|
||||||
# Item Methods #################################################################################
|
|
||||||
|
|
||||||
addItem: (item)->
|
|
||||||
if @findItem(item.slug)? then throw new Error "duplicate item for #{item.name}"
|
|
||||||
|
|
||||||
item.modVersion = this
|
|
||||||
@_items[item.slug.item] = item
|
|
||||||
@_groups[item.group] ?= {}
|
|
||||||
@_groups[item.group][item.slug.item] = item
|
|
||||||
|
|
||||||
@registerName item.slug, item.name
|
|
||||||
return this
|
|
||||||
|
|
||||||
allItemsInGroup: (group)->
|
|
||||||
result = []
|
|
||||||
@eachItemInGroup group, (item)-> result.push item
|
|
||||||
return null if result.length is 0
|
|
||||||
return result
|
|
||||||
|
|
||||||
chooseRandomItem: ->
|
|
||||||
itemIndex = Math.floor Math.random() * @_slugs.length
|
|
||||||
return @_slugs[itemIndex]
|
|
||||||
|
|
||||||
eachItem: (callback)->
|
|
||||||
for slug in @_slugs
|
|
||||||
item = @findItem slug
|
|
||||||
continue unless item?
|
|
||||||
callback item
|
|
||||||
return this
|
|
||||||
|
|
||||||
eachItemInGroup: (group, callback)->
|
|
||||||
itemMap = @_groups[group]
|
|
||||||
return unless itemMap?
|
|
||||||
|
|
||||||
items = _.values(itemMap).sort (a, b)-> a.compareTo b
|
|
||||||
for item in items
|
|
||||||
callback item
|
|
||||||
|
|
||||||
findItem: (itemSlug)->
|
|
||||||
return @_items[itemSlug.item]
|
|
||||||
|
|
||||||
findItemByName: (name)->
|
|
||||||
for itemSlug, item of @_items
|
|
||||||
return item if item.name is name
|
|
||||||
return null
|
|
||||||
|
|
||||||
# Group Methods ################################################################################
|
|
||||||
|
|
||||||
getAllGroups: ->
|
|
||||||
result = []
|
|
||||||
@eachGroup (group)-> result.push group
|
|
||||||
return result
|
|
||||||
|
|
||||||
eachGroup: (callback)->
|
|
||||||
groupNames = _.keys @_groups
|
|
||||||
groupNames.sort (a, b)->
|
|
||||||
if a is b then return 0
|
|
||||||
if a is Item.Group.Other then return -1
|
|
||||||
if b is Item.Group.Other then return +1
|
|
||||||
return if a < b then -1 else +1
|
|
||||||
|
|
||||||
for groupName in groupNames
|
|
||||||
callback groupName
|
|
||||||
|
|
||||||
# Name Methods #################################################################################
|
|
||||||
|
|
||||||
eachName: (callback)->
|
|
||||||
for slug in @_slugs
|
|
||||||
callback @_names[slug.item], slug
|
|
||||||
return this
|
|
||||||
|
|
||||||
findName: (itemSlug)->
|
|
||||||
return @_names[itemSlug.item]
|
|
||||||
|
|
||||||
registerName: (itemSlug, name)->
|
|
||||||
return if @_names[itemSlug.item]
|
|
||||||
@_names[itemSlug.item] = name
|
|
||||||
@_slugs.push itemSlug
|
|
||||||
return this
|
|
||||||
|
|
||||||
# Recipe Methods ###############################################################################
|
|
||||||
|
|
||||||
addRecipe: (recipe)->
|
|
||||||
return unless recipe?
|
|
||||||
|
|
||||||
recipe.modVersion = this
|
|
||||||
if @_recipes[recipe.slug]? then throw new Error "duplicate recipe: #{recipe.slug}"
|
|
||||||
|
|
||||||
@_recipes[recipe.slug] = recipe
|
|
||||||
return this
|
|
||||||
|
|
||||||
eachRecipe: (callback)->
|
|
||||||
recipes = _.values(@_recipes).sort (a, b)-> Recipe.compareFor a, b
|
|
||||||
for recipe in recipes
|
|
||||||
callback recipe
|
|
||||||
return this
|
|
||||||
|
|
||||||
findRecipes: (itemSlug, result=[], options={})->
|
|
||||||
options.onlyPrimary ?= false
|
|
||||||
options.forCrafting ?= false
|
|
||||||
|
|
||||||
primaryRecipes = []
|
|
||||||
otherRecipes = []
|
|
||||||
|
|
||||||
for recipe in _.values @_recipes
|
|
||||||
continue unless recipe.isConditionSatisfied()
|
|
||||||
continue unless recipe.hasAllTools()
|
|
||||||
continue if options.forCrafting and recipe.ignoreDuringCrafting
|
|
||||||
|
|
||||||
if recipe.itemSlug.matches itemSlug
|
|
||||||
primaryRecipes.push recipe
|
|
||||||
else if recipe.produces itemSlug
|
|
||||||
otherRecipes.push recipe
|
|
||||||
|
|
||||||
for recipe in primaryRecipes
|
|
||||||
result.push recipe
|
|
||||||
|
|
||||||
if not options.onlyPrimary and result.length is 0
|
|
||||||
for recipe in otherRecipes
|
|
||||||
result.push recipe
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
findExternalRecipes: ->
|
|
||||||
result = {}
|
|
||||||
for k, recipe of @_recipes
|
|
||||||
continue if recipe.itemSlug.isQualified
|
|
||||||
|
|
||||||
recipeList = result[recipe.itemSlug]
|
|
||||||
if not recipeList then recipeList = result[recipe.itemSlug] = []
|
|
||||||
|
|
||||||
recipeList.push recipe
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
hasRecipes: (itemSlug)->
|
|
||||||
for k, recipe of @_recipes
|
|
||||||
return true if recipe.produces itemSlug
|
|
||||||
return false
|
|
||||||
|
|
||||||
# Backbone.Model Overrides #####################################################################
|
|
||||||
|
|
||||||
parse: (text)->
|
|
||||||
ModVersionParser = require '../parsing/mod_version_parser' # to avoid require cycles
|
|
||||||
@_parser ?= new ModVersionParser model:this
|
|
||||||
@_parser.parse text
|
|
||||||
|
|
||||||
return null # prevent calling `set`
|
|
||||||
|
|
||||||
url: ->
|
|
||||||
return c.url.modVersionData modSlug:@modSlug, modVersion:@version
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "ModVersion (#{@cid}) {
|
|
||||||
modSlug:#{@modSlug}, version:#{@version}, items:«#{@_slugs.length} items»
|
|
||||||
}"
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - multiblock.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
Inventory = require './inventory'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Multiblock extends BaseModel
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
if not attributes.input? then throw new Error 'attributes.input is required'
|
|
||||||
if not attributes.layers? then throw new Error 'attributes.layers is required'
|
|
||||||
if attributes.layers.length < 1 then throw new Error 'attributes.layers.length must be >= 1'
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
@_analyzePattern()
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
getStackAt: (x, y, z)->
|
|
||||||
value = @_stackCache[y]?[z]?[x]
|
|
||||||
return null if value is undefined
|
|
||||||
return value
|
|
||||||
|
|
||||||
getLayerInventory: (y)->
|
|
||||||
@_layerInventories ?= []
|
|
||||||
result = @_layerInventories[y]
|
|
||||||
if not result? then result = @_layerInventories[y] = new Inventory
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
depth:
|
|
||||||
get: -> @_depth
|
|
||||||
|
|
||||||
height:
|
|
||||||
get: -> @_height
|
|
||||||
|
|
||||||
inventory:
|
|
||||||
get: -> @_inventory
|
|
||||||
|
|
||||||
width:
|
|
||||||
get: -> @_width
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_analyzeLayer: (layer, y, stackCacheLayer)->
|
|
||||||
rows = layer.split ' '
|
|
||||||
@_depth = Math.max @_depth, rows.length
|
|
||||||
|
|
||||||
for row in rows
|
|
||||||
@_width = Math.max @_width, row.length
|
|
||||||
stackCacheRow = []
|
|
||||||
stackCacheLayer.push stackCacheRow
|
|
||||||
@_analyzeRow row, stackCacheRow, @getLayerInventory(y)
|
|
||||||
|
|
||||||
_analyzePattern: ->
|
|
||||||
@_depth = @_height = @_width = 0
|
|
||||||
@_inventory = new Inventory
|
|
||||||
@_stackCache = []
|
|
||||||
|
|
||||||
for layer, y in @layers
|
|
||||||
stackCacheLayer = []
|
|
||||||
@_stackCache.push stackCacheLayer
|
|
||||||
@_analyzeLayer layer, y, stackCacheLayer
|
|
||||||
|
|
||||||
@_height = @layers.length
|
|
||||||
|
|
||||||
_analyzeRow: (row, stackCacheRow, layerInventory)->
|
|
||||||
for cell, x in row.split ''
|
|
||||||
index = parseInt cell
|
|
||||||
stack = null
|
|
||||||
|
|
||||||
if not _.isNaN index
|
|
||||||
stack = @input[index]
|
|
||||||
@_inventory.add stack.itemSlug, stack.quantity
|
|
||||||
layerInventory.add stack.itemSlug, stack.quantity
|
|
||||||
|
|
||||||
stackCacheRow.push stack
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - recipe.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
Stack = require './stack'
|
|
||||||
{StringBuilder} = require 'crafting-guide-common'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Recipe extends BaseModel
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
if not attributes.input? then throw new Error 'attributes.input is required'
|
|
||||||
if not attributes.pattern? then throw new Error 'attributes.pattern is required'
|
|
||||||
|
|
||||||
if attributes.itemSlug? and not attributes.output?
|
|
||||||
attributes.output = [new Stack itemSlug:attributes.itemSlug, quantity:1]
|
|
||||||
else if attributes.output? and not attributes.itemSlug?
|
|
||||||
if attributes.output.length is 0 then throw new Error 'attributes.output cannot be empty'
|
|
||||||
attributes.itemSlug = attributes.output[0].itemSlug
|
|
||||||
else
|
|
||||||
throw new Error 'attributes.itemSlug or attributes.output is required'
|
|
||||||
|
|
||||||
attributes.pattern = @_parsePattern attributes.pattern
|
|
||||||
|
|
||||||
attributes.condition ?= null
|
|
||||||
attributes.ignoreDuringCrafting ?= false
|
|
||||||
attributes.modVersion ?= null
|
|
||||||
attributes.tools ?= []
|
|
||||||
options.logEvents ?= false
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
@_computeQuantities attributes.pattern
|
|
||||||
|
|
||||||
@on c.event.change + ':modVersion', => @_slug = null
|
|
||||||
@on c.event.change + ':pattern', => @_patternCache = null
|
|
||||||
|
|
||||||
# Class Methods ################################################################################
|
|
||||||
|
|
||||||
@compareFor: (a, b, itemSlug)->
|
|
||||||
if itemSlug?
|
|
||||||
aValue = a.itemSlug.matches itemSlug
|
|
||||||
bValue = b.itemSlug.matches itemSlug
|
|
||||||
if aValue isnt bValue
|
|
||||||
return -1 if aValue
|
|
||||||
return +1 if bValue
|
|
||||||
|
|
||||||
aValue = a.getQuantityProduced itemSlug
|
|
||||||
bValue = b.getQuantityProduced itemSlug
|
|
||||||
if aValue isnt bValue
|
|
||||||
return if aValue > bValue then -1 else +1
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
getStackAtSlot: (patternSlot)->
|
|
||||||
trueIndex = 0:0, 1:1, 2:2, 3:4, 4:5, 5:6, 6:8, 7:9, 8:10
|
|
||||||
patternDigit = @pattern[trueIndex[patternSlot]]
|
|
||||||
return null unless patternDigit?
|
|
||||||
return null unless patternDigit.match /[0-9]/
|
|
||||||
|
|
||||||
stack = @input[parseInt(patternDigit)]
|
|
||||||
return null unless stack?
|
|
||||||
|
|
||||||
return stack
|
|
||||||
|
|
||||||
getQuantityProduced: (itemSlug)->
|
|
||||||
total = 0
|
|
||||||
for stack in @output
|
|
||||||
if stack.itemSlug.matches itemSlug
|
|
||||||
total += stack.quantity
|
|
||||||
|
|
||||||
return total
|
|
||||||
|
|
||||||
getQuantityRequired: (itemSlug)->
|
|
||||||
total = 0
|
|
||||||
for stack, index in @input
|
|
||||||
if ItemSlug.equal stack.itemSlug, itemSlug
|
|
||||||
total += @_quantities[index] * stack.quantity
|
|
||||||
|
|
||||||
return total
|
|
||||||
|
|
||||||
hasAllTools: (modPack)->
|
|
||||||
modPack ?= @modVersion?.mod?.modPack
|
|
||||||
return true unless modPack?
|
|
||||||
|
|
||||||
for stack in @tools
|
|
||||||
return false unless modPack.findItem stack.itemSlug
|
|
||||||
return true
|
|
||||||
|
|
||||||
isConditionSatisfied: (modPack)->
|
|
||||||
return true unless @condition?
|
|
||||||
modPack ?= @modVersion?.mod?.modPack
|
|
||||||
|
|
||||||
result = false
|
|
||||||
if @condition.verb is 'item'
|
|
||||||
if modPack?.findItemByName(@condition.noun)?
|
|
||||||
result = true
|
|
||||||
else if @condition.verb is 'mod'
|
|
||||||
modPack.eachMod (mod)=>
|
|
||||||
if mod.name is @condition.noun
|
|
||||||
result = true
|
|
||||||
|
|
||||||
if @condition.inverted then result = not result
|
|
||||||
return result
|
|
||||||
|
|
||||||
isPassThroughFor: (itemSlug)->
|
|
||||||
return @getQuantityProduced(itemSlug) is @getQuantityRequired(itemSlug)
|
|
||||||
|
|
||||||
produces: (itemSlug)->
|
|
||||||
if not @_produces?
|
|
||||||
@_produces = {}
|
|
||||||
|
|
||||||
for stack in @output
|
|
||||||
actuallyProduces = not @isPassThroughFor stack.itemSlug
|
|
||||||
@_produces[stack.itemSlug.qualified] = actuallyProduces
|
|
||||||
|
|
||||||
result = @_produces[itemSlug.qualified] or @_produces[itemSlug.item]
|
|
||||||
return result
|
|
||||||
|
|
||||||
requires: (itemSlug)->
|
|
||||||
for stack in @input
|
|
||||||
if stack.itemSlug.matches itemSlug
|
|
||||||
return true
|
|
||||||
return false
|
|
||||||
|
|
||||||
requiresTool: (itemSlug)->
|
|
||||||
for stack in @tools
|
|
||||||
if stack.itemSlug.matches itemSlug
|
|
||||||
return true
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
slug:
|
|
||||||
get: ->
|
|
||||||
if not @_slug?
|
|
||||||
builder = new StringBuilder
|
|
||||||
delimiterNeeded = false
|
|
||||||
for stack in @input
|
|
||||||
if delimiterNeeded then builder.push ','
|
|
||||||
delimiterNeeded = true
|
|
||||||
|
|
||||||
if stack.quantity > 1 then builder.push stack.quantity, ' '
|
|
||||||
builder.push stack.itemSlug.qualified
|
|
||||||
|
|
||||||
builder.push '>'
|
|
||||||
builder.push @pattern
|
|
||||||
builder.push '>'
|
|
||||||
for stack in @tools
|
|
||||||
builder.push stack.itemSlug.qualified
|
|
||||||
builder.push '>'
|
|
||||||
|
|
||||||
delimiterNeeded = false
|
|
||||||
for stack in @output
|
|
||||||
if delimiterNeeded then builder.push ','
|
|
||||||
delimiterNeeded = true
|
|
||||||
|
|
||||||
if stack.quantity > 1 then builder.push stack.quantity, ' '
|
|
||||||
builder.push stack.itemSlug.qualified
|
|
||||||
|
|
||||||
@_slug = builder.toString()
|
|
||||||
|
|
||||||
return @_slug
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
result = [@constructor.name, " (", @cid, ") { name:", @name]
|
|
||||||
|
|
||||||
result.push ", input:["
|
|
||||||
needsDelimiter = false
|
|
||||||
for stack in @input
|
|
||||||
if needsDelimiter then result.push ', '
|
|
||||||
result.push @getQuantityRequired stack.itemSlug
|
|
||||||
result.push ' '
|
|
||||||
result.push stack.itemSlug
|
|
||||||
needsDelimiter = true
|
|
||||||
result.push ']'
|
|
||||||
|
|
||||||
result.push ", output:["
|
|
||||||
needsDelimiter = false
|
|
||||||
for stack in @output
|
|
||||||
if needsDelimiter then result.push ', '
|
|
||||||
result.push @getQuantityProduced stack.itemSlug
|
|
||||||
result.push ' '
|
|
||||||
result.push stack.itemSlug
|
|
||||||
needsDelimiter = true
|
|
||||||
result.push ']'
|
|
||||||
|
|
||||||
if @tools.length > 0
|
|
||||||
result.push ", tools:["
|
|
||||||
needsDelimiter = false
|
|
||||||
for stack in @tools
|
|
||||||
if needsDelimiter then result.push ', '
|
|
||||||
result.push stack.toString()
|
|
||||||
needsDelimiter = true
|
|
||||||
result.push ']'
|
|
||||||
|
|
||||||
result.push '}'
|
|
||||||
return result.join ''
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_computeQuantities: (pattern)->
|
|
||||||
quantityMap = {}
|
|
||||||
|
|
||||||
index = 0
|
|
||||||
while index < pattern.length
|
|
||||||
c = pattern[index]
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
continue if c is '.'
|
|
||||||
continue if c is ' '
|
|
||||||
|
|
||||||
if quantityMap[c]?
|
|
||||||
quantityMap[c] += 1
|
|
||||||
else
|
|
||||||
quantityMap[c] = 1
|
|
||||||
|
|
||||||
@_quantities = []
|
|
||||||
for i in [0...@input.length]
|
|
||||||
@_quantities.push quantityMap["#{i}"]
|
|
||||||
|
|
||||||
_parsePattern: (pattern)->
|
|
||||||
return unless pattern?
|
|
||||||
|
|
||||||
pattern = pattern.replace /\ /g, ''
|
|
||||||
return if pattern.length is 0
|
|
||||||
|
|
||||||
pattern = pattern.replace /[^0-9]/g, '.'
|
|
||||||
|
|
||||||
array = pattern.split ''
|
|
||||||
array = array[0...9]
|
|
||||||
while array.length isnt 9
|
|
||||||
array.push '.'
|
|
||||||
|
|
||||||
pattern = array.join ''
|
|
||||||
pattern = pattern.replace /(...)(...)(...)/, '$1 $2 $3'
|
|
||||||
return pattern
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - simple_stack.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Stack
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required'
|
|
||||||
attributes.quantity ?= 1
|
|
||||||
|
|
||||||
@itemSlug = attributes.itemSlug
|
|
||||||
@quantity = attributes.quantity
|
|
||||||
|
|
||||||
# Class Methods ################################################################################
|
|
||||||
|
|
||||||
@compare: (a, b)->
|
|
||||||
if a? and not b? then return -1
|
|
||||||
if not a? and b? then return +1
|
|
||||||
if a.quantity isnt b.quantity
|
|
||||||
return if a.quantity > b.quantity then -1 else +1
|
|
||||||
return ItemSlug.compare a.itemSlug, b.itemSlug
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "#{@quantity} #{@itemSlug}"
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - stack.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Stack extends BaseModel
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required'
|
|
||||||
attributes.quantity ?= 1
|
|
||||||
options.logEvents ?= false
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "#{@quantity} #{@itemSlug}"
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - command_parser_version_base.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
{StringBuilder} = require 'crafting-guide-common'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class CommandParserVersionBase
|
|
||||||
|
|
||||||
constructor: (options={})->
|
|
||||||
if not options.model? then throw new Error 'options.model is required'
|
|
||||||
options.showAllErrors ?= false
|
|
||||||
|
|
||||||
@_model = options.model
|
|
||||||
@_showAllErrors = options.showAllErrors
|
|
||||||
@errors = []
|
|
||||||
|
|
||||||
# Class Methods ################################################################################
|
|
||||||
|
|
||||||
@COMMAND = /\ *([^:]*):?(.*)/
|
|
||||||
|
|
||||||
@COMMENT = /([^\\]?)#.*/
|
|
||||||
|
|
||||||
@simplify: (text)->
|
|
||||||
text = text.trim()
|
|
||||||
text = text.replace /\ */g , ' '
|
|
||||||
text = text.replace /\n/g, ';'
|
|
||||||
text = text.replace /; */g, ';'
|
|
||||||
text = text.replace /;;*/g, ';'
|
|
||||||
text = text.replace /: /g, ':'
|
|
||||||
return text
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
parse: (text)->
|
|
||||||
@_rawData = {}
|
|
||||||
@_lineNumber = 1
|
|
||||||
@errors = []
|
|
||||||
|
|
||||||
@_lines = text.split '\n'
|
|
||||||
@_lineNumber = 0
|
|
||||||
while @_lineNumber < @_lines.length
|
|
||||||
@_lineNumber += 1
|
|
||||||
commands = @_parseLine @_lines[@_lineNumber - 1]
|
|
||||||
for command in commands
|
|
||||||
@_handleErrors @_execute, command
|
|
||||||
|
|
||||||
@_handleErrors @_buildModel, @_rawData, @_model
|
|
||||||
return @_model
|
|
||||||
|
|
||||||
unparse: ->
|
|
||||||
builder = new StringBuilder context:@_model
|
|
||||||
@_unparseModel builder, @_model
|
|
||||||
return builder.toString()
|
|
||||||
|
|
||||||
# Subclass Methods #############################################################################
|
|
||||||
|
|
||||||
_buildModel: (rawData, model)->
|
|
||||||
throw new Error 'Subclasses must override this method'
|
|
||||||
|
|
||||||
_unparseModel: (builder, model)->
|
|
||||||
throw new Error 'Subclasses must override this method'
|
|
||||||
|
|
||||||
_command_schema: -> # do nothing
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_execute: (command)->
|
|
||||||
method = this["_command_#{command.name}"]
|
|
||||||
if not method? then throw new Error "Unknown command: #{command.name}"
|
|
||||||
|
|
||||||
@_handleErrors method, command.args
|
|
||||||
|
|
||||||
_parseLine: (line)->
|
|
||||||
line = line.replace CommandParserVersionBase.COMMENT, '$1'
|
|
||||||
line = line.trim()
|
|
||||||
return [] if line.length is 0
|
|
||||||
|
|
||||||
[line, hereDoc] = @_parseHereDoc line
|
|
||||||
|
|
||||||
lineParts = (part.trim() for part in line.split(';'))
|
|
||||||
commands = []
|
|
||||||
for linePart in lineParts
|
|
||||||
continue if linePart.length is 0
|
|
||||||
|
|
||||||
match = CommandParserVersionBase.COMMAND.exec linePart
|
|
||||||
if not match? then throw new Error "Expected <command>: <args>, but found: \"#{linePart}\""
|
|
||||||
|
|
||||||
args = []
|
|
||||||
args = (s for s in match[2].split(',') when s.length > 0) if match[2]?
|
|
||||||
args = (s.trim() for s in args)
|
|
||||||
args = (s for s in args when s.length > 0)
|
|
||||||
args.push hereDoc if hereDoc?
|
|
||||||
commands.push name:match[1], args:args
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
_handleErrors: (callback, args...)->
|
|
||||||
if args.length is 1 and _.isArray(args[0]) then args = args[0]
|
|
||||||
|
|
||||||
try
|
|
||||||
callback.apply this, args
|
|
||||||
catch e
|
|
||||||
e.message = "line #{@_lineNumber}: #{e.message}"
|
|
||||||
if not @_showAllErrors then throw e
|
|
||||||
@errors.push e
|
|
||||||
logger.error -> e.message
|
|
||||||
|
|
||||||
_parseHereDoc: (line)->
|
|
||||||
hereDocIndex = line.indexOf '<<-'
|
|
||||||
return [line, null] unless hereDocIndex isnt -1
|
|
||||||
|
|
||||||
hereDocStopText = line[hereDocIndex+3...line.length]
|
|
||||||
line = line[0...hereDocIndex]
|
|
||||||
|
|
||||||
hereDocLines = []
|
|
||||||
while true
|
|
||||||
@_lineNumber += 1
|
|
||||||
break if @_lineNumber >= @_lines.length
|
|
||||||
|
|
||||||
nextLine = @_lines[@_lineNumber-1]
|
|
||||||
break if nextLine.trim() is hereDocStopText
|
|
||||||
hereDocLines.push nextLine
|
|
||||||
|
|
||||||
shortestIndent = Number.MAX_VALUE
|
|
||||||
for hereDocLine in hereDocLines
|
|
||||||
continue if hereDocLine.trim().length is 0
|
|
||||||
shortestIndent = Math.min hereDocLine.match(/( *).*/)[1].length, shortestIndent
|
|
||||||
|
|
||||||
for i in [0...hereDocLines.length]
|
|
||||||
hereDocLines[i] = hereDocLines[i][shortestIndent..]
|
|
||||||
|
|
||||||
return [line, null] unless hereDocLines.length > 0
|
|
||||||
return [line, hereDocLines.join('\n')]
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - command_parser_version_base.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
CommandParserVersionBase = require './command_parser_version_base'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
parser = null
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe 'command_parser_version_base.coffee', ->
|
|
||||||
|
|
||||||
beforeEach -> parser = new CommandParserVersionBase model:{}
|
|
||||||
|
|
||||||
describe '_parseHereDoc', ->
|
|
||||||
|
|
||||||
it 'returns null for non-heredoc lines', ->
|
|
||||||
result = parser._parseHereDoc 'foobar: baz'
|
|
||||||
expect(result[1]).to.be.null
|
|
||||||
|
|
||||||
it 'identifies the right text for a real heredoc', ->
|
|
||||||
parser._lines = ['command: <<-END', 'alpha', 'bravo', 'charlie', 'END', 'command1: arg2']
|
|
||||||
parser._lineNumber = 1
|
|
||||||
|
|
||||||
result = parser._parseHereDoc parser._lines[0]
|
|
||||||
result[0].should.equal 'command: '
|
|
||||||
result[1].should.equal 'alpha\nbravo\ncharlie'
|
|
||||||
|
|
||||||
it 'identifies an empty heredoc', ->
|
|
||||||
parser._lines = ['command: <<-END', 'END']
|
|
||||||
parser._lineNumber = 1
|
|
||||||
|
|
||||||
result = parser._parseHereDoc parser._lines[0]
|
|
||||||
result[0].should.equal 'command: '
|
|
||||||
expect(result[1]).to.be.null
|
|
||||||
|
|
||||||
it 'trims smallest leading whitespace', ->
|
|
||||||
parser._lines = ['command: <<-END', ' alpha', ' bravo', '', ' charlie', 'END', 'command1: arg2']
|
|
||||||
parser._lineNumber = 1
|
|
||||||
|
|
||||||
result = parser._parseHereDoc parser._lines[0]
|
|
||||||
result[0].should.equal 'command: '
|
|
||||||
result[1].should.equal 'alpha\n bravo\n\ncharlie'
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - item_parser.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
VersionedParserBase = require './versioned_parser_base'
|
|
||||||
ItemParserV1 = require './item_parser_v1'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ItemParser extends VersionedParserBase
|
|
||||||
|
|
||||||
# VersionedParserBase Overrides ################################################################
|
|
||||||
|
|
||||||
_createParsers: (options)->
|
|
||||||
return result =
|
|
||||||
'1': new ItemParserV1 options
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - item_parser_v1.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
CommandParserVersionBase = require './command_parser_version_base'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ItemParserV1 extends CommandParserVersionBase
|
|
||||||
|
|
||||||
# CommandParserVersionBase Overrides ###########################################################
|
|
||||||
|
|
||||||
_buildModel: (rawData, model)->
|
|
||||||
@_buildItem rawData, model
|
|
||||||
|
|
||||||
_unparseModel: (builder, model)->
|
|
||||||
builder.line 'schema: ', 1
|
|
||||||
builder.line()
|
|
||||||
|
|
||||||
@_unparseItem builder, model
|
|
||||||
|
|
||||||
# Command Methods ##############################################################################
|
|
||||||
|
|
||||||
_command_description: (textParts...)->
|
|
||||||
if not @_rawData.description?
|
|
||||||
@_rawData.description = ''
|
|
||||||
else
|
|
||||||
@_rawData.description += '\n'
|
|
||||||
@_rawData.description += textParts.join ', '
|
|
||||||
|
|
||||||
_command_officialUrl: (officialUrl)->
|
|
||||||
if @_rawData.officialUrl? then throw new Error 'duplicate declaration of "officialUrl"'
|
|
||||||
if not officialUrl? or (officialUrl.length is 0) then throw new Error 'officialUrl cannot be empty'
|
|
||||||
@_rawData.officialUrl = officialUrl
|
|
||||||
|
|
||||||
_command_video: (youTubeId, nameParts...)->
|
|
||||||
if not youTubeId?.length then throw new Error 'video declaration requires a YouTubeID'
|
|
||||||
name = nameParts.join ', '
|
|
||||||
if not name?.length then throw new Error 'video declaration requires a name'
|
|
||||||
|
|
||||||
@_rawData.videos ?= []
|
|
||||||
@_rawData.videos.push youTubeId:youTubeId, name:name
|
|
||||||
|
|
||||||
# Object Building Methods ######################################################################
|
|
||||||
|
|
||||||
_buildItem: (rawData, model)->
|
|
||||||
model.description = rawData.description if rawData.description?
|
|
||||||
model.officialUrl = rawData.officialUrl if rawData.officialUrl?
|
|
||||||
model.videos = rawData.videos if rawData.videos?
|
|
||||||
|
|
||||||
# Un-parsing Methods ###########################################################################
|
|
||||||
|
|
||||||
_unparseItem: (builder, model)->
|
|
||||||
if model.officialUrl?
|
|
||||||
builder.line 'officialUrl: ', model.officialUrl
|
|
||||||
builder.line()
|
|
||||||
|
|
||||||
if model.description?
|
|
||||||
if model.description.indexOf('\n') isnt -1
|
|
||||||
builder.line 'description: <<-END'
|
|
||||||
builder.line model.description
|
|
||||||
builder.line 'END'
|
|
||||||
else
|
|
||||||
builder.line 'description: ', model.description
|
|
||||||
builder.line()
|
|
||||||
|
|
||||||
for video in model.videos
|
|
||||||
builder.line 'video: ', video.youTubeId, ', ', video.name
|
|
||||||
builder.line()
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - item_parser_v1.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Item = require '../game/item'
|
|
||||||
ItemParserV1 = require './item_parser_v1'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
baseText = item = parser = null
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe 'item_parser_v1.coffee', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
item = new Item name:'alpha'
|
|
||||||
parser = new ItemParserV1 model:item
|
|
||||||
|
|
||||||
describe 'officialUrl', ->
|
|
||||||
|
|
||||||
it 'may be omitted', ->
|
|
||||||
parser.parse 'schema: 1\nvideo: youtubeid, Video Alpha\ndescription: Bravo, Charlie'
|
|
||||||
expect(item.officialUrl).to.be.null
|
|
||||||
|
|
||||||
it 'is assigned properly when given', ->
|
|
||||||
parser.parse 'schema: 1\nofficialUrl: http://testurl.com'
|
|
||||||
item.officialUrl.should.equal 'http://testurl.com'
|
|
||||||
|
|
||||||
it 'does not allow duplicate declarations', ->
|
|
||||||
func = -> parser.parse 'schema: 1\nofficialUrl: http://testurl.com\nofficialUrl: http://testurl2.com'
|
|
||||||
expect(func).to.throw Error, 'duplicate'
|
|
||||||
|
|
||||||
it 'does not allow an empty value if given', ->
|
|
||||||
func = -> parser.parse 'schema: 1\nofficialUrl:'
|
|
||||||
expect(func).to.throw Error, 'empty'
|
|
||||||
|
|
||||||
describe 'description', ->
|
|
||||||
|
|
||||||
it 'may be omitted', ->
|
|
||||||
parser.parse 'schema: 1\nvideo: youTubeId, Video Alpha\nofficialUrl: http://testurl.com'
|
|
||||||
expect(item.description).to.be.null
|
|
||||||
|
|
||||||
it 'is assigned properly when given', ->
|
|
||||||
parser.parse 'schema: 1\ndescription: Alpha Bravo Charlie'
|
|
||||||
item.description.should.equal 'Alpha Bravo Charlie'
|
|
||||||
|
|
||||||
it 'can be a heredoc', ->
|
|
||||||
parser.parse 'schema: 1\ndescription: <<-END\nAlpha\nBravo\nCharlie\nEND'
|
|
||||||
item.description.should.equal 'Alpha\nBravo\nCharlie'
|
|
||||||
|
|
||||||
it 'concatenates multiple declarations', ->
|
|
||||||
parser.parse 'schema: 1\ndescription: Alpha\ndescription: Bravo'
|
|
||||||
item.description.should.equal 'Alpha\nBravo'
|
|
||||||
|
|
||||||
describe 'video', ->
|
|
||||||
|
|
||||||
it 'may be omitted', ->
|
|
||||||
parser.parse 'schema: 1\nofficialUrl: http://testurl.com\ndescription: Alpha Bravo Charlie'
|
|
||||||
item.videos.should.eql []
|
|
||||||
|
|
||||||
it 'is assigned properly when given', ->
|
|
||||||
parser.parse 'schema: 1\nvideo: youtubeid, Alpha Bravo'
|
|
||||||
item.videos[0].should.eql youTubeId:'youtubeid', name:'Alpha Bravo'
|
|
||||||
item.videos.length.should.equal 1
|
|
||||||
|
|
||||||
it 'may be included multiple times', ->
|
|
||||||
parser.parse 'schema: 1\nvideo: youtubeid1, Alpha\nvideo: youtubeid2, Bravo'
|
|
||||||
item.videos[0].should.eql youTubeId:'youtubeid1', name:'Alpha'
|
|
||||||
item.videos[1].should.eql youTubeId:'youtubeid2', name:'Bravo'
|
|
||||||
item.videos.length.should.equal 2
|
|
||||||
|
|
||||||
it 'requires a YouTubeId and name', ->
|
|
||||||
func = -> parser.parse 'schema: 1\nvideo: alpha'
|
|
||||||
expect(func).to.throw Error, 'requires a name'
|
|
||||||
|
|
||||||
describe 'unparsing', ->
|
|
||||||
|
|
||||||
it 'can round-trip a fully described item', ->
|
|
||||||
text = """
|
|
||||||
schema: 1
|
|
||||||
|
|
||||||
officialUrl: http://testurl.com
|
|
||||||
|
|
||||||
description: <<-END
|
|
||||||
Alpha
|
|
||||||
Bravo
|
|
||||||
END
|
|
||||||
|
|
||||||
video: youtubeid1, Alpha Bravo
|
|
||||||
video: youtubeid2, Charlie Delta
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
parser.parse text
|
|
||||||
parser.unparse().should.equal text
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_parser.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
VersionedParserBase = require './versioned_parser_base'
|
|
||||||
ModParserV1 = require './mod_parser_v1'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ModParser extends VersionedParserBase
|
|
||||||
|
|
||||||
# VersionedParserBase Overrides ################################################################
|
|
||||||
|
|
||||||
_createParsers: (options)->
|
|
||||||
return result =
|
|
||||||
'1': new ModParserV1 options
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_parser_v1.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
CommandParserVersionBase = require './command_parser_version_base'
|
|
||||||
Mod = require '../game/mod'
|
|
||||||
ModVersion = require '../game/mod_version'
|
|
||||||
Tutorial = require '../site/tutorial'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ModParserV1 extends CommandParserVersionBase
|
|
||||||
|
|
||||||
# CommandParserVersionBase Overrides ###########################################################
|
|
||||||
|
|
||||||
_buildModel: (rawData, model)->
|
|
||||||
@_buildMod rawData, model
|
|
||||||
|
|
||||||
_unparseModel: (builder, model)->
|
|
||||||
@_unparseMod builder, model
|
|
||||||
|
|
||||||
# Command Methods ##############################################################################
|
|
||||||
|
|
||||||
_command_author: (authorParts...)->
|
|
||||||
if @_rawData.author? then throw new Error 'duplicate declaration of "author"'
|
|
||||||
author = authorParts.join ', '
|
|
||||||
if author.length is 0 then throw new Error '"author" cannot be empty, but may be omitted'
|
|
||||||
|
|
||||||
@_rawData.author = author
|
|
||||||
|
|
||||||
_command_description: (descriptionParts...)->
|
|
||||||
if @_rawData.description? then throw new Error 'duplicate declaration of "description"'
|
|
||||||
description = descriptionParts.join ', '
|
|
||||||
if description.length is 0 then throw new Error '"description" cannot be empty, but may be omitted'
|
|
||||||
|
|
||||||
@_rawData.description = description
|
|
||||||
|
|
||||||
_command_documentationUrl: (documentationUrl)->
|
|
||||||
documentationUrl ?= ''
|
|
||||||
if @_rawData.documentationUrl? then throw new Error 'duplicate declaration of "documentationUrl"'
|
|
||||||
if documentationUrl.length is 0 then throw new Error 'documentationUrl cannot be empty (omit it instead)'
|
|
||||||
@_rawData.documentationUrl = documentationUrl
|
|
||||||
|
|
||||||
_command_downloadUrl: (downloadUrl)->
|
|
||||||
if @_rawData.downloadUrl? then throw new Error 'duplicate declaration of "downloadUrl"'
|
|
||||||
if downloadUrl.length is 0 then throw new Error 'downloadUrl cannot be empty (omit it instead)'
|
|
||||||
@_rawData.downloadUrl = downloadUrl
|
|
||||||
|
|
||||||
_command_homePageUrl: (homePageUrl='')->
|
|
||||||
if @_rawData.homePageUrl? then throw new Error 'duplicate declaration of "homePageUrl"'
|
|
||||||
if homePageUrl.length is 0 then throw new Error 'homePageUrl cannot be empty'
|
|
||||||
|
|
||||||
@_rawData.homePageUrl = homePageUrl
|
|
||||||
|
|
||||||
_command_name: (name)->
|
|
||||||
if @_rawData.name? then throw new Error 'duplicate declaration of "name"'
|
|
||||||
if name.length is 0 then throw new Error '"name" cannot be empty'
|
|
||||||
@_rawData.name = name
|
|
||||||
|
|
||||||
_command_tutorial: (nameParts...)->
|
|
||||||
name = nameParts.join(', ').trim()
|
|
||||||
if name.length is 0 then throw new Error '"name" cannot be empty'
|
|
||||||
@_rawData.tutorialNames ?= []
|
|
||||||
@_rawData.tutorialNames.push name
|
|
||||||
|
|
||||||
_command_version: (version='')->
|
|
||||||
if version.length is 0 then throw new Error 'version cannot be empty'
|
|
||||||
|
|
||||||
@_rawData.versions ?= []
|
|
||||||
@_rawData.versions.push version
|
|
||||||
|
|
||||||
# Object Building Methods ######################################################################
|
|
||||||
|
|
||||||
_buildMod: (rawData, model)->
|
|
||||||
if not rawData.name? then throw new Error 'the "name" declaration is required'
|
|
||||||
if not rawData.homePageUrl? then throw new Error 'the "homePageUrl" declaration is required'
|
|
||||||
if not rawData.versions? then throw new Error 'at least one "version" declaration is required'
|
|
||||||
|
|
||||||
model.author = rawData.author if rawData.author?
|
|
||||||
model.description = rawData.description if rawData.description?
|
|
||||||
model.documentationUrl = rawData.documentationUrl if rawData.documentationUrl?
|
|
||||||
model.downloadUrl = rawData.downloadUrl if rawData.downloadUrl?
|
|
||||||
model.name = rawData.name
|
|
||||||
model.homePageUrl = rawData.homePageUrl
|
|
||||||
|
|
||||||
if rawData.tutorialNames?
|
|
||||||
for tutorialName in rawData.tutorialNames
|
|
||||||
model.addTutorial new Tutorial name:tutorialName
|
|
||||||
|
|
||||||
for version in rawData.versions
|
|
||||||
model.addModVersion new ModVersion modSlug:model.slug, version:version
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_version_parser.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
VersionedParserBase = require './versioned_parser_base'
|
|
||||||
ModVersionParserV1 = require './mod_version_parser_v1'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ModVersionParser extends VersionedParserBase
|
|
||||||
|
|
||||||
# VersionedParserBase Overrides ################################################################
|
|
||||||
|
|
||||||
_createParsers: (options)->
|
|
||||||
return result =
|
|
||||||
'1': new ModVersionParserV1 options
|
|
||||||
@@ -1,380 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_version_parser_v1.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
CommandParserVersionBase = require './command_parser_version_base'
|
|
||||||
Item = require '../game/item'
|
|
||||||
ItemSlug = require '../game/item_slug'
|
|
||||||
ModVersion = require '../game/mod_version'
|
|
||||||
Multiblock = require '../game/multiblock'
|
|
||||||
Recipe = require '../game/recipe'
|
|
||||||
Stack = require '../game/simple_stack'
|
|
||||||
{StringBuilder} = require 'crafting-guide-common'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ModVersionParserV1 extends CommandParserVersionBase
|
|
||||||
|
|
||||||
# Class Methods ################################################################################
|
|
||||||
|
|
||||||
@INTEGER = /[0-9]+/
|
|
||||||
|
|
||||||
@PATTERN = /^[0-9.]{3} ?[0-9.]{3} ?[0-9.]{3}$/
|
|
||||||
|
|
||||||
@STACK = /^([0-9]+) +(.*)$/
|
|
||||||
|
|
||||||
# CommandParserVersionBase Overrides ###########################################################
|
|
||||||
|
|
||||||
_buildModel: (rawData, model)->
|
|
||||||
@_buildModVersion rawData, model
|
|
||||||
|
|
||||||
_unparseModel: (builder, model)->
|
|
||||||
@_unparseModVersion builder, model
|
|
||||||
|
|
||||||
# Command Methods ##############################################################################
|
|
||||||
|
|
||||||
_command_extras: (extraTerms...)->
|
|
||||||
if not @_recipeData? then throw new Error 'cannot declare "extras" before "recipe"'
|
|
||||||
if @_recipeData.output.length isnt 1 then throw new Error 'duplicate declaration of "extras"'
|
|
||||||
|
|
||||||
for term in extraTerms
|
|
||||||
@_recipeData.output.push @_parseStack term
|
|
||||||
|
|
||||||
_command_gatherable: (gatherable)->
|
|
||||||
if not @_itemData? then throw new Error 'cannot declare "gatherable" before "item"'
|
|
||||||
if @_itemData.gatherable? then throw new Error 'duplicate declaration of "gatherable"'
|
|
||||||
if not (gatherable in ['yes', 'no']) then throw new Error 'gatherable must be either "yes" or "no"'
|
|
||||||
|
|
||||||
@_itemData.gatherable = (gatherable is 'yes')
|
|
||||||
|
|
||||||
_command_group: (group)->
|
|
||||||
if group.length is 0 then throw new Error 'a group name cannot be empty'
|
|
||||||
@_rawData.group = group
|
|
||||||
|
|
||||||
_command_item: (name='')->
|
|
||||||
if not name.length > 0 then throw new Error 'the item name cannot be empty'
|
|
||||||
|
|
||||||
@_itemData = name:name, line:@_lineNumber, group:@_rawData.group, type:'new'
|
|
||||||
@_rawData.items ?= {}
|
|
||||||
@_rawData.items[name] = @_itemData
|
|
||||||
|
|
||||||
@_recipeData = null
|
|
||||||
|
|
||||||
_command_ignoreDuringCrafting: (value)->
|
|
||||||
if not @_recipeData? then throw new Error 'cannot declare "ignoreDuringCrafting" before "recipe"'
|
|
||||||
if @_recipeData.ignoreDuringCrafting? then throw new Error 'duplicate declaration of "ignoreDuringCrafting"'
|
|
||||||
if not (value in ['yes', 'no']) then throw new Error 'ignoreDuringCrafting must be either "yes" or "no"'
|
|
||||||
|
|
||||||
@_recipeData.ignoreDuringCrafting = (value is 'yes')
|
|
||||||
|
|
||||||
_command_input: (stackDescriptions...)->
|
|
||||||
activeData = if @_recipeData? then @_recipeData else @_multiblockData
|
|
||||||
|
|
||||||
if not activeData? then throw new Error 'cannot declare "input" before "recipe" or "multiblock"'
|
|
||||||
if activeData.input.length isnt 0 then throw new Error 'duplicate declaration of "input"'
|
|
||||||
|
|
||||||
for stackDescription in stackDescriptions
|
|
||||||
activeData.input.push @_parseStack stackDescription
|
|
||||||
|
|
||||||
_command_layer: (layerText)->
|
|
||||||
if not @_multiblockData? then throw new Error 'cannot declare "layer" before "multiblock"'
|
|
||||||
if not layerText? then throw new Error 'cannot have an empty layer'
|
|
||||||
if layerText.length is 0 then throw new Error 'cannot have an empty layer'
|
|
||||||
|
|
||||||
@_multiblockData.layers.push layerText
|
|
||||||
|
|
||||||
_command_multiblock: ->
|
|
||||||
if not @_itemData? then throw new Error 'cannot declare "multiblock" before "item"'
|
|
||||||
if @_itemData.multiblockData? then throw new Error 'duplicate declaration of "multiblock"'
|
|
||||||
|
|
||||||
@_recipeData = null
|
|
||||||
@_multiblockData = input:[], layers:[], line:@_lineNumber
|
|
||||||
@_itemData.multiblockData = @_multiblockData
|
|
||||||
|
|
||||||
_command_onlyIf: (condition)->
|
|
||||||
words = condition.split ' '
|
|
||||||
if words.length < 2 then throw new Error 'condition must include a verb followed by a noun'
|
|
||||||
|
|
||||||
inverted = false
|
|
||||||
if words[0] is 'not'
|
|
||||||
inverted = true
|
|
||||||
words.shift()
|
|
||||||
|
|
||||||
verb = words[0]
|
|
||||||
noun = words[1..].join ' '
|
|
||||||
|
|
||||||
if not (verb in ['item', 'mod']) then throw new Error "unknown verb: #{verb}"
|
|
||||||
@_recipeData.condition = verb:verb, noun:noun, inverted:inverted
|
|
||||||
|
|
||||||
_command_pattern: (pattern='')->
|
|
||||||
if not @_recipeData? then throw new Error 'cannot declare "pattern" before "recipe"'
|
|
||||||
if @_recipeData.pattern? then throw new Error 'duplicate declaration of "pattern"'
|
|
||||||
if not ModVersionParserV1.PATTERN.test pattern
|
|
||||||
throw new Error 'a pattern must have 9 digits using 0-9 for items and "." for an empty spot;
|
|
||||||
spaces are optional'
|
|
||||||
|
|
||||||
@_recipeData.pattern = pattern
|
|
||||||
|
|
||||||
_command_quantity: (quantity)->
|
|
||||||
if not @_recipeData? then throw new Error 'cannot declare "quantity" before "recipe"'
|
|
||||||
if @_recipeData.quantity? then throw new Error 'duplicate declaration of "quantity"'
|
|
||||||
if not ModVersionParserV1.INTEGER.test(quantity) then throw new Error 'quantity must be an integer'
|
|
||||||
|
|
||||||
@_recipeData.quantity = quantity
|
|
||||||
@_recipeData.output[0].quantity = parseInt quantity
|
|
||||||
|
|
||||||
_command_recipe: ->
|
|
||||||
if not @_itemData? then throw new Error 'cannot delcare "recipe" before "item"'
|
|
||||||
|
|
||||||
@_multiblockData = null
|
|
||||||
@_recipeData = line:@_lineNumber, input:[], output:[{quantity:1, name:@_itemData.name}], tools:[]
|
|
||||||
@_itemData.recipes ?= []
|
|
||||||
@_itemData.recipes.push @_recipeData
|
|
||||||
|
|
||||||
_command_tools: (toolNames...)->
|
|
||||||
if not @_recipeData? then throw new Error 'cannot declare "tools" before "recipe"'
|
|
||||||
if @_recipeData.tools.length isnt 0 then throw new Error 'duplicate declaration of "tools"'
|
|
||||||
|
|
||||||
for name in toolNames
|
|
||||||
if name.length is 0 then throw new Error 'tool names cannot be empty'
|
|
||||||
@_recipeData.tools.push name:name, quantity:1
|
|
||||||
|
|
||||||
_command_update: (name='')->
|
|
||||||
if not name.length > 0 then throw new 'the item name cannot be empty'
|
|
||||||
|
|
||||||
@_itemData = name:name, line:@_lineNumber, type:'update'
|
|
||||||
@_recipeData = null
|
|
||||||
|
|
||||||
@_rawData.items ?= {}
|
|
||||||
@_rawData.items[name] = @_itemData
|
|
||||||
|
|
||||||
# Parsing Helpers ##############################################################################
|
|
||||||
|
|
||||||
_parseStack: (stackText)->
|
|
||||||
match = ModVersionParserV1.STACK.exec stackText
|
|
||||||
if match?
|
|
||||||
return quantity:parseInt(match[1]), name:match[2]
|
|
||||||
else
|
|
||||||
return quantity:1, name:stackText
|
|
||||||
|
|
||||||
# Object Creation Methods ######################################################################
|
|
||||||
|
|
||||||
_buildModVersion: (modVersionData, modVersion)->
|
|
||||||
modVersionData.items ?= []
|
|
||||||
|
|
||||||
for itemName, itemData of modVersionData.items
|
|
||||||
@_handleErrors @_buildItem, modVersion, itemData
|
|
||||||
|
|
||||||
modVersion.sort()
|
|
||||||
return modVersion
|
|
||||||
|
|
||||||
_buildItem: (modVersion, itemData)->
|
|
||||||
@_lineNumber = itemData.line
|
|
||||||
itemData.gatherable ?= false
|
|
||||||
itemData.ignoreDuringCrafting ?= false
|
|
||||||
itemData.recipes ?= []
|
|
||||||
|
|
||||||
if itemData.type is 'new'
|
|
||||||
item = new Item
|
|
||||||
name: itemData.name,
|
|
||||||
ignoreDuringCrafting: itemData.ignoreDuringCrafting,
|
|
||||||
isGatherable: itemData.gatherable,
|
|
||||||
group: itemData.group
|
|
||||||
modVersion.addItem item
|
|
||||||
itemData.slug = item.slug
|
|
||||||
else
|
|
||||||
itemData.slug = ItemSlug.slugify itemData.name
|
|
||||||
modVersion.registerName itemData.slug, itemData.name
|
|
||||||
|
|
||||||
if itemData.multiblockData
|
|
||||||
item.multiblock = @_handleErrors @_buildMultiblock, modVersion, item, itemData.multiblockData
|
|
||||||
|
|
||||||
for recipeData in itemData.recipes
|
|
||||||
@_handleErrors @_buildRecipe, modVersion, itemData, recipeData
|
|
||||||
|
|
||||||
return item
|
|
||||||
|
|
||||||
_buildMultiblock: (modVersion, item, multiblockData)->
|
|
||||||
@_lineNumber = multiblockData.line
|
|
||||||
if multiblockData.layers.length is 0 then throw new Error '"multiblock" requires at least one "layer"'
|
|
||||||
if multiblockData.input.length is 0 then throw new Error '"multiblock" requires at least one "input"'
|
|
||||||
|
|
||||||
input = @_buildStackList modVersion, multiblockData.input
|
|
||||||
multiblock = new Multiblock input:input, layers:multiblockData.layers
|
|
||||||
return multiblock
|
|
||||||
|
|
||||||
_buildRecipe: (modVersion, itemData, recipeData)->
|
|
||||||
@_lineNumber = recipeData.line
|
|
||||||
if recipeData.input.length is 0 then throw new Error 'the "input" declaration is required'
|
|
||||||
if not recipeData.pattern? then throw new Error 'the "pattern" declaration is required'
|
|
||||||
|
|
||||||
recipe = new Recipe
|
|
||||||
condition: recipeData.condition
|
|
||||||
ignoreDuringCrafting: recipeData.ignoreDuringCrafting
|
|
||||||
input: @_buildStackList modVersion, recipeData.input, recipeData.pattern
|
|
||||||
output: @_buildStackList modVersion, recipeData.output
|
|
||||||
pattern: recipeData.pattern
|
|
||||||
tools: @_buildStackList modVersion, recipeData.tools
|
|
||||||
|
|
||||||
modVersion.addRecipe recipe
|
|
||||||
return recipe
|
|
||||||
|
|
||||||
_buildStackList: (modVersion, data, pattern=null)->
|
|
||||||
createSlug = (name)=>
|
|
||||||
item = @_rawData.items[name]
|
|
||||||
if item? and item.type isnt 'update'
|
|
||||||
slug = new ItemSlug modVersion.modSlug, _.slugify name
|
|
||||||
else
|
|
||||||
slug = new ItemSlug name
|
|
||||||
modVersion.registerName slug, name
|
|
||||||
return slug
|
|
||||||
|
|
||||||
stacks = []
|
|
||||||
for stackData in data
|
|
||||||
itemSlug = createSlug stackData.name
|
|
||||||
stacks.push new Stack itemSlug:itemSlug, quantity:stackData.quantity
|
|
||||||
|
|
||||||
if pattern?
|
|
||||||
expectedIndexes = _.reduce [0...stacks.length], ((obj, i)-> obj[i] = true; return obj), {}
|
|
||||||
for c in pattern
|
|
||||||
continue if c is '.'
|
|
||||||
continue if c is ' '
|
|
||||||
delete expectedIndexes[c]
|
|
||||||
if not stacks[parseInt(c)]? then throw new Error "there is no item #{c} in this recipe"
|
|
||||||
|
|
||||||
unusedNames = _.map(_.keys(expectedIndexes), ((i)-> data[parseInt(i)].name))
|
|
||||||
if unusedNames.length > 1
|
|
||||||
throw new Error "#{unusedNames.join(', ')} are listed for this recipe, but do not appear in the pattern"
|
|
||||||
else if unusedNames.length is 1
|
|
||||||
throw new Error "#{unusedNames[0]} is listed for this recipe, but does not appear in the pattern"
|
|
||||||
|
|
||||||
return stacks
|
|
||||||
|
|
||||||
# Un-parsing Methods ###########################################################################
|
|
||||||
|
|
||||||
_unparseModVersion: (builder, modVersion)->
|
|
||||||
builder
|
|
||||||
.line 'schema: ', 1
|
|
||||||
.line()
|
|
||||||
|
|
||||||
modVersion.eachGroup (group)=>
|
|
||||||
@_unparseGroup builder, modVersion, group
|
|
||||||
|
|
||||||
externalRecipes = modVersion.findExternalRecipes()
|
|
||||||
keys = _.keys(externalRecipes).sort()
|
|
||||||
for itemSlugText in keys
|
|
||||||
recipeList = externalRecipes[itemSlugText]
|
|
||||||
|
|
||||||
builder
|
|
||||||
.line 'update: ', modVersion.findName ItemSlug.slugify(itemSlugText)
|
|
||||||
.indent()
|
|
||||||
.loop(recipeList, delimiter:'', onEach:(b, r)=> @_unparseRecipe(b, r))
|
|
||||||
.outdent()
|
|
||||||
.line()
|
|
||||||
|
|
||||||
_unparseGroup: (builder, modVersion, group)->
|
|
||||||
if group isnt Item.Group.Other
|
|
||||||
builder
|
|
||||||
.line 'group: ', group
|
|
||||||
.line()
|
|
||||||
.indent()
|
|
||||||
|
|
||||||
modVersion.eachItemInGroup group, (item)=>
|
|
||||||
@_unparseItem builder, modVersion, item
|
|
||||||
builder.line()
|
|
||||||
|
|
||||||
if group isnt Item.Group.Other
|
|
||||||
builder.outdent()
|
|
||||||
|
|
||||||
_unparseItem: (builder, modVersion, item)->
|
|
||||||
recipes = modVersion.findRecipes item.slug, [], onlyPrimary:true
|
|
||||||
|
|
||||||
builder
|
|
||||||
.line 'item: ', item.name
|
|
||||||
.indent()
|
|
||||||
.onlyIf item.isGatherable, => builder.line 'gatherable: yes'
|
|
||||||
.onlyIf item.multiblock?, =>
|
|
||||||
builder
|
|
||||||
.indent()
|
|
||||||
.call => @_unparseMultiblock builder, item.multiblock
|
|
||||||
.outdent()
|
|
||||||
.onlyIf recipes.length > 0, =>
|
|
||||||
builder.loop recipes, delimiter:'', onEach:(b, r)=> @_unparseRecipe(b, r)
|
|
||||||
.outdent()
|
|
||||||
|
|
||||||
_unparseMultiblock: (builder, multiblock)->
|
|
||||||
builder
|
|
||||||
.line 'multiblock:'
|
|
||||||
.indent()
|
|
||||||
.push "input: "
|
|
||||||
.call => @_unparseStackList builder, multiblock.input
|
|
||||||
.push ";\n"
|
|
||||||
.loop(multiblock.layers, delimiter:'', onEach:(builder, layer)=> builder.line "layer: #{layer}")
|
|
||||||
.outdent()
|
|
||||||
|
|
||||||
_unparseRecipe: (builder, recipe)->
|
|
||||||
inputStacks = recipe.input[..]
|
|
||||||
inputStacks.sort (a, b)-> Stack.compare a, b
|
|
||||||
inputNames = []
|
|
||||||
for stack in inputStacks
|
|
||||||
name = builder.context.findName stack.itemSlug
|
|
||||||
if stack.quantity > 1
|
|
||||||
inputNames.push "#{stack.quantity} #{name}"
|
|
||||||
else
|
|
||||||
inputNames.push name
|
|
||||||
|
|
||||||
patternMap = {'.', '.'}
|
|
||||||
for i in [0...recipe.input.length]
|
|
||||||
stack = recipe.input[i]
|
|
||||||
name = builder.context.findName(stack.itemSlug)
|
|
||||||
name = "#{stack.quantity} #{name}" if stack.quantity > 1
|
|
||||||
patternMap["#{i}"] = "#{inputNames.indexOf name}"
|
|
||||||
|
|
||||||
pattern = recipe.pattern or recipe.defaultPattern
|
|
||||||
newPattern = []
|
|
||||||
for c in pattern.split('')
|
|
||||||
newPattern.push patternMap[c]
|
|
||||||
newPattern = newPattern.join ''
|
|
||||||
newPattern = newPattern.replace /(...)(...)(...)/, '$1 $2 $3'
|
|
||||||
|
|
||||||
quantity = recipe.output[0].quantity
|
|
||||||
|
|
||||||
extraOutputs = recipe.output[0...recipe.output.length]
|
|
||||||
extraOutputs.shift()
|
|
||||||
|
|
||||||
builder
|
|
||||||
.line 'recipe:'
|
|
||||||
.indent()
|
|
||||||
.onlyIf recipe.condition?, =>
|
|
||||||
builder.push 'onlyIf: '
|
|
||||||
.onlyIf recipe.condition.inverted, -> builder.push 'not '
|
|
||||||
.line recipe.condition.verb, ' ', recipe.condition.noun
|
|
||||||
.onlyIf extraOutputs.length > 0, =>
|
|
||||||
builder
|
|
||||||
.push 'extras: '
|
|
||||||
.call => @_unparseStackList builder, extraOutputs
|
|
||||||
.line()
|
|
||||||
.onlyIf recipe.ignoreDuringCrafting, => builder.line 'ignoreDuringCrafting: yes'
|
|
||||||
.push 'input: '
|
|
||||||
.loop inputNames
|
|
||||||
.line()
|
|
||||||
.line 'pattern: ', newPattern
|
|
||||||
.onlyIf quantity > 1, => builder.line 'quantity: ', quantity
|
|
||||||
.onlyIf recipe.tools.length > 0, =>
|
|
||||||
builder
|
|
||||||
.push 'tools: '
|
|
||||||
.call => @_unparseStackList builder, recipe.tools
|
|
||||||
.line()
|
|
||||||
.outdent()
|
|
||||||
|
|
||||||
_unparseStackList: (builder, stackList)->
|
|
||||||
if stackList.length is 1 and stackList[0].quantity is 1
|
|
||||||
builder.push builder.context.findName(stackList[0].itemSlug)
|
|
||||||
else
|
|
||||||
builder.loop stackList, onEach:(b, stack)=>
|
|
||||||
builder
|
|
||||||
.onlyIf stack.quantity > 1, => builder.push stack.quantity, ' '
|
|
||||||
.push builder.context.findName stack.itemSlug
|
|
||||||
@@ -1,344 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_version_parser_v1.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
CommandParserVersionBase = require './command_parser_version_base'
|
|
||||||
ItemSlug = require '../game/item_slug'
|
|
||||||
ModVersion = require '../game/mod_version'
|
|
||||||
ModVersionParserV1 = require './mod_version_parser_v1'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
baseText = modVersion = parser = null
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe 'mod_version_parser_v1.coffee', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
modVersion = new ModVersion modSlug:'test', version:'0.0'
|
|
||||||
parser = new ModVersionParserV1 model:modVersion
|
|
||||||
|
|
||||||
describe 'Item', ->
|
|
||||||
|
|
||||||
it 'allows multiple recipes', ->
|
|
||||||
recipes = "item: Charlie;
|
|
||||||
recipe:; input:Alpha; pattern:... .0. ...;
|
|
||||||
recipe:; input:Bravo; pattern:... 0.0 ...;"
|
|
||||||
modVersion = parser.parse recipes
|
|
||||||
recipes = modVersion.findRecipes ItemSlug.slugify 'test__charlie'
|
|
||||||
recipes[0].input[0].itemSlug.qualified.should.equal 'alpha'
|
|
||||||
recipes[1].input[0].itemSlug.qualified.should.equal 'bravo'
|
|
||||||
|
|
||||||
describe 'name', ->
|
|
||||||
|
|
||||||
it 'adds the name when present', ->
|
|
||||||
modVersion = parser.parse 'item: Charlie'
|
|
||||||
modVersion._items.charlie.name.should.equal 'Charlie'
|
|
||||||
|
|
||||||
it 'requires a non-empty name', ->
|
|
||||||
func = -> parser.parse 'item: \n'
|
|
||||||
expect(func).to.throw Error, 'cannot be empty'
|
|
||||||
|
|
||||||
describe 'gatherable', ->
|
|
||||||
|
|
||||||
it 'adds "gatherable" when present', ->
|
|
||||||
modVersion = parser.parse 'item: Alpha Bravo; gatherable: yes'
|
|
||||||
modVersion._items.alpha_bravo.isGatherable.should.be.true
|
|
||||||
|
|
||||||
it 'does not allow a duplicate "gatherable" declaration', ->
|
|
||||||
func = -> parser.parse 'item: Alpha Bravo; gatherable: yes; gatherable: yes'
|
|
||||||
expect(func).to.throw Error, 'duplicate'
|
|
||||||
|
|
||||||
it 'requires "gatherable" to be "yes" or "no"', ->
|
|
||||||
func = -> parser.parse 'item: Alpha Bravo; gatherable: true'
|
|
||||||
expect(func).to.throw Error, 'gatherable must be'
|
|
||||||
|
|
||||||
it 'does not allow "gatherable" before "item"', ->
|
|
||||||
func = -> parser.parse 'gatherable: yes; item: Alpha Bravo; gatherable: yes'
|
|
||||||
expect(func).to.throw Error, '"gatherable" before "item"'
|
|
||||||
|
|
||||||
describe 'Multiblock', ->
|
|
||||||
|
|
||||||
it 'adds a "multiblock" when present', ->
|
|
||||||
modVersion = parser.parse 'item: Alpha; multiblock:; input:Bravo; layer: 0'
|
|
||||||
item = modVersion.findItemByName 'Alpha'
|
|
||||||
item.multiblock.height.should.equal 1
|
|
||||||
|
|
||||||
it 'requires "item" be declared before "multiblock"', ->
|
|
||||||
func = -> parser.parse "multiblock:"
|
|
||||||
expect(func).to.throw Error, '"multiblock" before "item"'
|
|
||||||
|
|
||||||
it 'prohibits multiple "multiblock" commands per item"', ->
|
|
||||||
func = -> parser.parse "item: Alpha; multiblock:; multiblock:"
|
|
||||||
expect(func).to.throw Error, 'duplicate'
|
|
||||||
|
|
||||||
it 'prohibits multiblocks with no inputs', ->
|
|
||||||
func = -> parser.parse "item: Alpha; multiblock:; layer: 000"
|
|
||||||
expect(func).to.throw Error, 'at least one "input"'
|
|
||||||
|
|
||||||
describe 'layer', ->
|
|
||||||
|
|
||||||
it 'allows multiple "layer" commands', ->
|
|
||||||
modVersion = parser.parse 'item: Alpha; multiblock:; input:Bravo, Charlie; layer: 01 10; layer: 10 01'
|
|
||||||
item = modVersion.findItemByName 'Alpha'
|
|
||||||
item.multiblock.depth.should.equal 2
|
|
||||||
item.multiblock.height.should.equal 2
|
|
||||||
item.multiblock.width.should.equal 2
|
|
||||||
|
|
||||||
it 'prohibits empty layers', ->
|
|
||||||
func = -> parser.parse 'item: Alpha; multiblock:; input: Bravo; layer:; layer: 00 00'
|
|
||||||
expect(func).to.throw Error, 'empty layer'
|
|
||||||
|
|
||||||
it 'prohibits multiblocks with no layers', ->
|
|
||||||
func = -> parser.parse "item: Alpha; multiblock:; input: Bravo"
|
|
||||||
expect(func).to.throw Error, 'at least one "layer"'
|
|
||||||
|
|
||||||
describe 'Recipe', ->
|
|
||||||
|
|
||||||
beforeEach -> baseText = 'item: Charlie; '
|
|
||||||
|
|
||||||
describe 'input', ->
|
|
||||||
|
|
||||||
it 'adds "input" when present', ->
|
|
||||||
modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern: ... 010 ...'
|
|
||||||
charlieSlug = ItemSlug.slugify('test__charlie')
|
|
||||||
slugs = (s.itemSlug.item for s in modVersion.findRecipes(charlieSlug)[0].input)
|
|
||||||
slugs.should.eql ['alpha', 'bravo']
|
|
||||||
|
|
||||||
it 'requires an "input" declaration', ->
|
|
||||||
func = -> parser.parse baseText + 'recipe:; pattern: ... .0. ...'
|
|
||||||
expect(func).to.throw Error, 'the "input" declaration is required'
|
|
||||||
|
|
||||||
it 'does not allow a duplicate "input" declaration', ->
|
|
||||||
func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:....0....; input:Bravo'
|
|
||||||
expect(func).to.throw Error, 'duplicate declaration of "input"'
|
|
||||||
|
|
||||||
it 'does not allow "input" before "recipe"', ->
|
|
||||||
func = -> parser.parse baseText + 'input:Alpha, Bravo; recipe:; pattern:....0....'
|
|
||||||
expect(func).to.throw Error, 'cannot declare "input" before "recipe"'
|
|
||||||
|
|
||||||
it 'registers slugs for each input name', ->
|
|
||||||
modVersion = parser.parse baseText + 'recipe:; input:Delta, Echo, Foxtrot; pattern:...012...'
|
|
||||||
(s.item for s in modVersion._slugs).should.eql ['charlie', 'delta', 'echo', 'foxtrot']
|
|
||||||
|
|
||||||
it 'correctly handles recipes which use the same input multiple times', ->
|
|
||||||
modVersion = parser.parse baseText + 'recipe:; input:Alpha; pattern:.0..0....'
|
|
||||||
recipe = _.values(modVersion._recipes)[0]
|
|
||||||
recipe.getQuantityRequired(ItemSlug.slugify('alpha')).should.equal 2
|
|
||||||
|
|
||||||
it 'allows a quantity for each input', ->
|
|
||||||
modVersion = parser.parse baseText + 'recipe:; input: 12 Delta, 3 Echo; pattern:... 0.1 ...'
|
|
||||||
recipe = _.values(modVersion._recipes)[0]
|
|
||||||
recipe.input[0].quantity.should.equal 12
|
|
||||||
recipe.input[0].itemSlug.qualified.should.equal 'delta'
|
|
||||||
recipe.input[1].quantity.should.equal 3
|
|
||||||
recipe.input[1].itemSlug.qualified.should.equal 'echo'
|
|
||||||
|
|
||||||
describe 'pattern', ->
|
|
||||||
|
|
||||||
it 'adds "pattern" when present', ->
|
|
||||||
modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern:... .0. .1.'
|
|
||||||
modVersion.findRecipes(ItemSlug.slugify('test__charlie'))[0].pattern.should.equal '... .0. .1.'
|
|
||||||
|
|
||||||
it 'requires a "pattern" declaration', ->
|
|
||||||
func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo'
|
|
||||||
expect(func).to.throw Error, 'the "pattern" declaration is required'
|
|
||||||
|
|
||||||
it 'does not allow a duplicate "pattern" declaration', ->
|
|
||||||
func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern:....0..1.; pattern:01.......'
|
|
||||||
expect(func).to.throw Error, 'duplicate declaration of "pattern"'
|
|
||||||
|
|
||||||
it 'requires pattern to be the right length', ->
|
|
||||||
func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:000'
|
|
||||||
expect(func).to.throw Error, 'a pattern must have'
|
|
||||||
|
|
||||||
it 'requires pattern to only use proper characters', ->
|
|
||||||
func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:abc def ghi'
|
|
||||||
expect(func).to.throw Error, 'a pattern must have'
|
|
||||||
|
|
||||||
it 'requires pattern to only refer to existing items', ->
|
|
||||||
func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:... 010 ...'
|
|
||||||
expect(func).to.throw Error, 'there is no item 1 in this recipe'
|
|
||||||
|
|
||||||
it 'requires all items to appear in the pattern', ->
|
|
||||||
func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern: 000 0.0 000'
|
|
||||||
expect(func).to.throw Error, 'Bravo is listed'
|
|
||||||
|
|
||||||
it 'does not allow "pattern" before "recipe"', ->
|
|
||||||
func = -> parser.parse baseText + 'pattern:... .0. ...; recipe:; inputs:Alpha'
|
|
||||||
expect(func).to.throw Error, 'cannot declare "pattern" before "recipe"'
|
|
||||||
|
|
||||||
describe 'quantity', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
baseText = 'item: Charlie; recipe:; input:Alpha; pattern:...0.0...; '
|
|
||||||
|
|
||||||
it 'adds "quantity" when present', ->
|
|
||||||
modVersion = parser.parse baseText + 'quantity: 2'
|
|
||||||
modVersion.findRecipes(ItemSlug.slugify('test__charlie'))[0].output[0].quantity.should.equal 2
|
|
||||||
|
|
||||||
it 'does not allow a duplicate "quantity" declaration', ->
|
|
||||||
func = -> parser.parse baseText + 'quantity:1; quantity:2'
|
|
||||||
expect(func).to.throw Error, 'duplicate declaration of "quantity"'
|
|
||||||
|
|
||||||
it 'requires quantity to be an integer', ->
|
|
||||||
func = -> parser.parse baseText + 'quantity:ten'
|
|
||||||
expect(func).to.throw Error, 'quantity must be an integer'
|
|
||||||
|
|
||||||
it 'assumes a quantity of 1 by default', ->
|
|
||||||
modVersion = parser.parse baseText
|
|
||||||
modVersion.findRecipes(ItemSlug.slugify('test__charlie'))[0].output[0].quantity.should.equal 1
|
|
||||||
|
|
||||||
it 'does not allow "quantity" before recipe', ->
|
|
||||||
func = -> parser.parse 'item:Bravo; quantity:12; recipe:;'
|
|
||||||
expect(func).to.throw Error, 'cannot declare "quantity" before "recipe"'
|
|
||||||
|
|
||||||
describe 'onlyIf', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
baseText = 'item: Charlie; recipe:; input:Alpha; pattern:...0.0...; '
|
|
||||||
|
|
||||||
it 'understands the "item" verb', ->
|
|
||||||
modVersion = parser.parse baseText + 'onlyIf: item Iron Ingot'
|
|
||||||
recipes = []
|
|
||||||
modVersion.eachRecipe (recipe)-> recipes.push recipe
|
|
||||||
recipe = recipes[0]
|
|
||||||
recipe.condition.should.eql verb:'item', noun:'Iron Ingot', inverted:false
|
|
||||||
|
|
||||||
it 'understands the "mod" verb', ->
|
|
||||||
modVersion = parser.parse baseText + 'onlyIf: mod BuildCraft'
|
|
||||||
recipes = []
|
|
||||||
modVersion.eachRecipe (recipe)-> recipes.push recipe
|
|
||||||
recipe = recipes[0]
|
|
||||||
recipe.condition.should.eql verb:'mod', noun:'BuildCraft', inverted:false
|
|
||||||
|
|
||||||
it 'understands inverting verbs', ->
|
|
||||||
modVersion = parser.parse baseText + 'onlyIf: not item Iron Ingot'
|
|
||||||
recipes = []
|
|
||||||
modVersion.eachRecipe (recipe)-> recipes.push recipe
|
|
||||||
recipe = recipes[0]
|
|
||||||
recipe.condition.should.eql verb:'item', noun:'Iron Ingot', inverted:true
|
|
||||||
|
|
||||||
it 'requires at least two words', ->
|
|
||||||
func = -> parser.parse baseText + 'onlyIf: item'
|
|
||||||
expect(func).to.throw Error, 'verb followed by a noun'
|
|
||||||
|
|
||||||
it 'only allows known verbs', ->
|
|
||||||
func = -> parser.parse baseText + 'onlyIf: foo Iron Ingot'
|
|
||||||
expect(func).to.throw Error, 'unknown verb'
|
|
||||||
|
|
||||||
describe 'output', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
baseText = 'item: Delta; item:Bravo; recipe:; input:Charlie; pattern:... .0. ...; '
|
|
||||||
|
|
||||||
it 'adds a single item as the default output', ->
|
|
||||||
modVersion = parser.parse baseText
|
|
||||||
stack = modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].output[0]
|
|
||||||
stack.itemSlug.qualified.should.equal 'test__bravo'
|
|
||||||
stack.quantity.should.equal 1
|
|
||||||
|
|
||||||
it 'can add multiple extras with quantities', ->
|
|
||||||
modVersion = parser.parse baseText + 'extras:2 Delta, 4 Echo'
|
|
||||||
output = modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].output
|
|
||||||
output[0].itemSlug.qualified.should.equal 'test__bravo'
|
|
||||||
output[0].quantity.should.equal 1
|
|
||||||
output[1].itemSlug.qualified.should.equal 'test__delta'
|
|
||||||
output[1].quantity.should.equal 2
|
|
||||||
output[2].itemSlug.qualified.should.equal 'echo'
|
|
||||||
output[2].quantity.should.equal 4
|
|
||||||
|
|
||||||
it 'does not allow "extras" before "recipe"', ->
|
|
||||||
func = -> parser.parse 'item:Bravo; extras:Charlie'
|
|
||||||
expect(func).to.throw Error, 'cannot declare "extras" before "recipe"'
|
|
||||||
|
|
||||||
it 'registers slugs for each output name', ->
|
|
||||||
modVersion = parser.parse baseText + 'extras:Delta, Echo'
|
|
||||||
(s.qualified for s in modVersion._slugs).should.eql [
|
|
||||||
'test__bravo', 'charlie', 'test__delta', 'echo'
|
|
||||||
]
|
|
||||||
|
|
||||||
it 'does not allow a duplicate "extras" declaration', ->
|
|
||||||
func = -> parser.parse baseText + 'extras:Echo; extras:Delta'
|
|
||||||
expect(func).to.throw Error, 'duplicate declaration of "extras"'
|
|
||||||
|
|
||||||
describe 'tools', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
baseText = 'item:Bravo; recipe:; input:Charlie; pattern:... .0. ...; '
|
|
||||||
|
|
||||||
it 'can add a single tool', ->
|
|
||||||
modVersion = parser.parse baseText + 'tools: Furnace'
|
|
||||||
modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].tools[0].itemSlug.item.should.equal 'furnace'
|
|
||||||
|
|
||||||
it 'can add multiple tools', ->
|
|
||||||
modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace'
|
|
||||||
tools = modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].tools
|
|
||||||
tools[0].itemSlug.item.should.equal 'crafting_table'
|
|
||||||
tools[1].itemSlug.item.should.equal 'furnace'
|
|
||||||
|
|
||||||
it 'registers slugs for each tool name', ->
|
|
||||||
modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace'
|
|
||||||
(s.item for s in modVersion._slugs).should.eql ['bravo', 'charlie', 'crafting_table', 'furnace']
|
|
||||||
|
|
||||||
it 'does not allow a duplicate "tools" declaration', ->
|
|
||||||
func = -> parser.parse baseText + 'tools:Crafting Table; tools:Furnace'
|
|
||||||
expect(func).to.throw Error, 'duplicate declaration of "tools"'
|
|
||||||
|
|
||||||
describe "unparsing", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
baseText = """
|
|
||||||
schema: 1
|
|
||||||
|
|
||||||
group: Agriculture
|
|
||||||
|
|
||||||
item: Apple
|
|
||||||
|
|
||||||
item: Baked Potato
|
|
||||||
recipe:
|
|
||||||
input: furnace fuel, Potato
|
|
||||||
pattern: .1. ... .0.
|
|
||||||
tools: Furnace
|
|
||||||
|
|
||||||
item: (filled) Canned Food
|
|
||||||
recipe:
|
|
||||||
input: 4 (Empty) Tin Can, Apple
|
|
||||||
pattern: .1. .0. ...
|
|
||||||
|
|
||||||
item: Pyramid
|
|
||||||
multiblock:
|
|
||||||
input: Cobblestone
|
|
||||||
layer: 000 000 000
|
|
||||||
layer: ... .0. ...
|
|
||||||
|
|
||||||
group: Functional Blocks
|
|
||||||
|
|
||||||
item: Furnace
|
|
||||||
recipe:
|
|
||||||
input: Cobblestone
|
|
||||||
pattern: 000 0.0 000
|
|
||||||
tools: Crafting Table
|
|
||||||
|
|
||||||
update: Iron Ingot
|
|
||||||
recipe:
|
|
||||||
input: furnace fuel, Iron Dust
|
|
||||||
pattern: .1. ... .0.
|
|
||||||
tools: Furnace
|
|
||||||
recipe:
|
|
||||||
onlyIf: item Redstone Furnace
|
|
||||||
input: Iron Ore
|
|
||||||
pattern: ... .0. ...
|
|
||||||
tools: Redstone Furnace
|
|
||||||
"""
|
|
||||||
|
|
||||||
it 'can round-trip a data file', ->
|
|
||||||
text = parser.unparse parser.parse baseText
|
|
||||||
actual = CommandParserVersionBase.simplify text
|
|
||||||
expected = CommandParserVersionBase.simplify baseText
|
|
||||||
|
|
||||||
actual.should.equal expected
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - tutorial_parser.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
VersionedParserBase = require './versioned_parser_base'
|
|
||||||
TutorialParserV1 = require './tutorial_parser_v1'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class TutorialParser extends VersionedParserBase
|
|
||||||
|
|
||||||
# VersionedParserBase Overrides ################################################################
|
|
||||||
|
|
||||||
_createParsers: (options)->
|
|
||||||
return result =
|
|
||||||
'1': new TutorialParserV1 options
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - tutorial_parser_v1.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
CommandParserVersionBase = require './command_parser_version_base'
|
|
||||||
Tutorial = require '../site/tutorial'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class TutorialParserV1 extends CommandParserVersionBase
|
|
||||||
|
|
||||||
# CommandParserVersionBase Overrides ###########################################################
|
|
||||||
|
|
||||||
_buildModel: (rawData, model)->
|
|
||||||
@_buildTutorial rawData, model
|
|
||||||
|
|
||||||
_unparseModel: (builder, model)->
|
|
||||||
@_unparseTutorial builder, model
|
|
||||||
|
|
||||||
# Command Methods ##############################################################################
|
|
||||||
|
|
||||||
_command_content: (contentParts...)->
|
|
||||||
if not @_rawData.currentSection? then throw new Error 'cannot declare "title" before "section"'
|
|
||||||
if @_rawData.currentSection.content? then throw new Error 'duplicate declaration of content'
|
|
||||||
content = contentParts.join(', ').trim()
|
|
||||||
if not content.length > 0 then throw new Error 'content cannot be empty'
|
|
||||||
|
|
||||||
@_rawData.currentSection.content = content
|
|
||||||
|
|
||||||
_command_officialUrl: (officialUrl)->
|
|
||||||
if @_rawData.officialUrl? then throw new Error 'duplicate declaration of "officialUrl"'
|
|
||||||
if officialUrl.length is 0 then throw new Error 'officialUrl cannot be empty'
|
|
||||||
@_rawData.officialUrl = officialUrl
|
|
||||||
|
|
||||||
_command_section: (textParts...)->
|
|
||||||
@_rawData.sections ?= []
|
|
||||||
@_rawData.sections.push @_rawData.currentSection = {}
|
|
||||||
|
|
||||||
_command_title: (titleParts...)->
|
|
||||||
if not @_rawData.currentSection? then throw new Error 'cannot declare "title" before "section"'
|
|
||||||
if @_rawData.currentSection.title? then throw new Error 'duplicate declaration of title'
|
|
||||||
title = titleParts.join(', ').trim()
|
|
||||||
if not title.length > 0 then throw new Error 'title cannot be empty'
|
|
||||||
|
|
||||||
@_rawData.currentSection.title = title
|
|
||||||
|
|
||||||
_command_video: (youTubeId, nameParts...)->
|
|
||||||
if not youTubeId?.length then throw new Error 'video declaration requires a YouTubeID'
|
|
||||||
name = nameParts.join ', '
|
|
||||||
if not name?.length then throw new Error 'video declaration requires a name'
|
|
||||||
|
|
||||||
@_rawData.videos ?= []
|
|
||||||
@_rawData.videos.push youTubeId:youTubeId, name:name
|
|
||||||
|
|
||||||
# Object Building Methods ######################################################################
|
|
||||||
|
|
||||||
_buildTutorial: (rawData, model)->
|
|
||||||
if not rawData.sections? then throw new Error 'the "section" declaration is required'
|
|
||||||
|
|
||||||
model.officialUrl = rawData.officialUrl
|
|
||||||
model.videos = rawData.videos
|
|
||||||
model.sections = rawData.sections
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - versioned_parser_base.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class VersionedParserBase
|
|
||||||
|
|
||||||
constructor: (options={})->
|
|
||||||
@_parsers = @_createParsers options
|
|
||||||
@_currentSchema = _.chain(@_parsers).keys().last().value()
|
|
||||||
@errors = []
|
|
||||||
|
|
||||||
# Class Members ################################################################################
|
|
||||||
|
|
||||||
@SCHEMA = /schema: *([0-9]+)/
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
parse: (text)->
|
|
||||||
return unless text?
|
|
||||||
|
|
||||||
schema = @_identifySchema text
|
|
||||||
parser = @_parsers[schema]
|
|
||||||
if not parser? then throw new Error "schema version #{schema} is not supported"
|
|
||||||
|
|
||||||
parser.parse text
|
|
||||||
@errors = parser.errors
|
|
||||||
|
|
||||||
return @_model
|
|
||||||
|
|
||||||
unparse: (schema=null)->
|
|
||||||
schema ?= @_currentSchema
|
|
||||||
|
|
||||||
parser = @_parsers["#{schema}"]
|
|
||||||
if not parser? then throw new Error "version #{schema} is not supported"
|
|
||||||
|
|
||||||
return parser.unparse()
|
|
||||||
|
|
||||||
# Overridable Methods ##########################################################################
|
|
||||||
|
|
||||||
_createParsers: (options)->
|
|
||||||
throw new Error 'subclasses must override this method'
|
|
||||||
|
|
||||||
_identifySchema: (text)->
|
|
||||||
match = VersionedParserBase.SCHEMA.exec text
|
|
||||||
if not match? then throw new Error 'missing "schema" declaration'
|
|
||||||
return match[1]
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - tutorial.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Tutorial extends BaseModel
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
if not attributes.name?.length > 0 then throw new Error "attributes.name cannot be empty"
|
|
||||||
attributes.modSlug ?= null
|
|
||||||
attributes.officialUrl ?= null
|
|
||||||
attributes.sections ?= []
|
|
||||||
attributes.slug ?= _.slugify attributes.name
|
|
||||||
attributes.videos ?= []
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
# Backbone.Model Overrides #####################################################################
|
|
||||||
|
|
||||||
parse: (text)->
|
|
||||||
TutorialParser = require '../parsing/tutorial_parser' # to avoid require cycles
|
|
||||||
@_parser ?= new TutorialParser model:this
|
|
||||||
@_parser.parse text
|
|
||||||
|
|
||||||
return null # prevent calling `set`
|
|
||||||
|
|
||||||
url: ->
|
|
||||||
return c.url.tutorialData modSlug:@modSlug, tutorialSlug:@slug
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - base_model.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class BaseModel extends Backbone.Model
|
|
||||||
|
|
||||||
@_loadingQueue = []
|
|
||||||
@_isDraining = false
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
options.logEvents ?= true
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
makeGetter = (name)-> return -> @get name
|
|
||||||
makeSetter = (name)-> return (value)-> @set name, value
|
|
||||||
for name, value of attributes
|
|
||||||
continue if name is 'id'
|
|
||||||
Object.defineProperty this, name, get:makeGetter(name), set:makeSetter(name)
|
|
||||||
|
|
||||||
@fileCache = options.fileCache or null
|
|
||||||
@loading = null
|
|
||||||
@logEvents = options.logEvents or false
|
|
||||||
@state = c.modelState.unloaded
|
|
||||||
|
|
||||||
Object.defineProperties this,
|
|
||||||
isUnloaded: { get:-> @state is c.modelState.unloaded }
|
|
||||||
isLoading: { get:-> @state is c.modelState.loading }
|
|
||||||
isLoaded: { get:-> @state is c.modelState.loaded }
|
|
||||||
isError: { get:-> @state is c.modelState.error }
|
|
||||||
|
|
||||||
# Event Methods ################################################################################
|
|
||||||
|
|
||||||
onLoadSucceeded: (text, status, xhr)->
|
|
||||||
try
|
|
||||||
@set @parse text
|
|
||||||
|
|
||||||
@state = c.modelState.loaded
|
|
||||||
@trigger c.event.change, this
|
|
||||||
@trigger c.event.sync, this
|
|
||||||
logger.info => "#{@constructor.name}.#{@cid} loaded successfully"
|
|
||||||
catch e
|
|
||||||
logger.error -> "A parsing error occured: #{e.stack}"
|
|
||||||
@onLoadFailed e.message, 'parsing failed', xhr
|
|
||||||
|
|
||||||
onLoadFailed: (error, status, xhr)->
|
|
||||||
@state = c.modelState.error
|
|
||||||
logger.error => "#{@constructor.name}.#{@cid} failed to load: status:#{status}, message:#{error}"
|
|
||||||
@trigger c.event.error, this, error
|
|
||||||
|
|
||||||
# Backbone.Model Overrides #####################################################################
|
|
||||||
|
|
||||||
fetch: (options={})->
|
|
||||||
options.force ?= false
|
|
||||||
return if (@isLoading or @isLoaded) and not options.force
|
|
||||||
|
|
||||||
url = @url()
|
|
||||||
logger.info => "#{@constructor.name}.#{@cid} reading from url: #{url}"
|
|
||||||
|
|
||||||
@state = c.modelState.loading
|
|
||||||
@trigger c.event.request, this
|
|
||||||
|
|
||||||
loadFromServer = =>
|
|
||||||
w.promise (resolve, reject)=>
|
|
||||||
$.ajax
|
|
||||||
url: url
|
|
||||||
dataType: 'text'
|
|
||||||
success: (text, status, xhr)=> resolve @onLoadSucceeded text, status, xhr
|
|
||||||
error: (xhr, status, error)=> reject @onLoadFailed error, status, xhr
|
|
||||||
|
|
||||||
if @fileCache?
|
|
||||||
@loading = @fileCache.loading.then =>
|
|
||||||
if @fileCache.hasFile url
|
|
||||||
return @_addToLoadingQueue @fileCache.getFile(url), 'success', {url:url}
|
|
||||||
else
|
|
||||||
@loading = loadFromServer()
|
|
||||||
else
|
|
||||||
@loading = loadFromServer()
|
|
||||||
|
|
||||||
@loading.catch (e)-> # do nothing
|
|
||||||
return @loading
|
|
||||||
|
|
||||||
parse: (text)->
|
|
||||||
return JSON.parse text
|
|
||||||
|
|
||||||
sync: (method, model)->
|
|
||||||
throw new Error "#{@constructor.name}.#{@cid} is not permitted to #{method}"
|
|
||||||
|
|
||||||
trigger: (name, model, args...)->
|
|
||||||
if @logEvents
|
|
||||||
argText = ("#{arg}"[0..50] for arg in args).join ", "
|
|
||||||
logger.trace => "#{@constructor.name}.#{@cid} triggered event #{name} with args: #{argText}"
|
|
||||||
super
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "#{@constructor.name}.#{@cid}"
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_addToLoadingQueue: (text, status, xhr)->
|
|
||||||
deferred = w.defer()
|
|
||||||
|
|
||||||
BaseModel._loadingQueue.push resolve:deferred.resolve, func:(=> @onLoadSucceeded text, status, xhr)
|
|
||||||
@_drainLoadingQueue()
|
|
||||||
return deferred.promise
|
|
||||||
|
|
||||||
_drainLoadingQueue: ->
|
|
||||||
return if @_isDraining
|
|
||||||
@_isDraining = true
|
|
||||||
|
|
||||||
drainDelay = 50
|
|
||||||
drain = =>
|
|
||||||
toLoad = BaseModel._loadingQueue.shift()
|
|
||||||
if not toLoad?
|
|
||||||
@_isDraining = false
|
|
||||||
else
|
|
||||||
toLoad.func()
|
|
||||||
toLoad.resolve(true)
|
|
||||||
|
|
||||||
_.delay drain, drainDelay
|
|
||||||
|
|
||||||
_.delay drain, drainDelay
|
|
||||||
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - crafting_plan.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Inventory = require "../game/inventory"
|
|
||||||
{StringBuilder} = require "crafting-guide-common"
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class CraftingPlan
|
|
||||||
|
|
||||||
constructor: (attributes={})->
|
|
||||||
@_id = _.uniqueId "crafting-plan-"
|
|
||||||
@_make = null
|
|
||||||
@_need = null
|
|
||||||
@have = attributes.have
|
|
||||||
@steps = attributes.steps
|
|
||||||
@want = attributes.want
|
|
||||||
|
|
||||||
@_computeResources()
|
|
||||||
@_consolidateSteps()
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
have: # an Inventory specifying what the player already has
|
|
||||||
get: -> return @_have
|
|
||||||
set: (have)->
|
|
||||||
have ?= new Inventory
|
|
||||||
if @_have is have then return
|
|
||||||
if @_have? then throw new Error "have cannot be reassigned"
|
|
||||||
@_have = new Inventory have
|
|
||||||
|
|
||||||
id: # a string uniquely specifying this crafting plan
|
|
||||||
get: -> return @_id
|
|
||||||
set: -> throw new Error "id is not assignabled"
|
|
||||||
|
|
||||||
make: # an Inventory specifying what the results of executing the plan will be
|
|
||||||
get: -> return @_make
|
|
||||||
set: -> throw new Error "make cannot be assigned"
|
|
||||||
|
|
||||||
need: # an Inventory specifying what the player will need to gather before executing the plan
|
|
||||||
get: -> return @_need
|
|
||||||
set: -> throw new Error "need cannot be assigned"
|
|
||||||
|
|
||||||
want: # an Inventory specifying what the player wants to make
|
|
||||||
get: -> return @_want
|
|
||||||
set: (want)->
|
|
||||||
if not want? then throw new Error "want is required"
|
|
||||||
if want.isEmpty then throw new Error "want cannot be empty"
|
|
||||||
if @_want is want then return
|
|
||||||
if @_want? then throw new Error "want cannot be reassigned"
|
|
||||||
@_want = new Inventory want
|
|
||||||
|
|
||||||
steps: # an array of CraftingPlanSteps detailing the steps of the plan
|
|
||||||
get: -> return @_steps
|
|
||||||
set: (steps)->
|
|
||||||
steps ?= []
|
|
||||||
if steps is @_steps then return
|
|
||||||
if @_steps? then throw new Error "steps cannot be reassigned"
|
|
||||||
@_steps = steps
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: (options={})->
|
|
||||||
options.full ?= false
|
|
||||||
|
|
||||||
if options.full
|
|
||||||
b = new StringBuilder
|
|
||||||
b.line "CraftingPlan<", @_id, ">"
|
|
||||||
b.indent()
|
|
||||||
b.line "have: ", @_have.toString full:true
|
|
||||||
b.line "want: ", @_want.toString full:true
|
|
||||||
b.line "need: ", @_need.toString full:true
|
|
||||||
b.line "steps:"
|
|
||||||
b.indent()
|
|
||||||
b.loop @_steps, delimiter:"\n", onEach:(b, step)->
|
|
||||||
b.push step.count, " × ", step.recipe.toString(full:true)
|
|
||||||
b.line()
|
|
||||||
b.outdent()
|
|
||||||
b.line "make: ", @_make.toString full:true
|
|
||||||
b.outdent()
|
|
||||||
|
|
||||||
return b.toString()
|
|
||||||
else
|
|
||||||
return "CraftingPlan:#{@_make.toString(full:true)}<#{@_id}>"
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_computeResources: ->
|
|
||||||
need = new Inventory @_want
|
|
||||||
make = new Inventory @_have
|
|
||||||
steps = @_steps[..].reverse()
|
|
||||||
|
|
||||||
for step in steps
|
|
||||||
step.count = 0
|
|
||||||
|
|
||||||
for productStack in step.recipe.allProducts
|
|
||||||
continue unless need.contains productStack.item
|
|
||||||
productCount = Math.ceil need.getQuantity(productStack.item) / productStack.quantity
|
|
||||||
step.count = Math.max step.count, productCount
|
|
||||||
|
|
||||||
continue unless step.count > 0
|
|
||||||
|
|
||||||
for itemId, item of step.recipe.inputs
|
|
||||||
amountNeeded = step.count * step.recipe.computeQuantityRequired item
|
|
||||||
amountAvailable = make.getQuantity item
|
|
||||||
amountUsed = Math.min amountAvailable, amountNeeded
|
|
||||||
amountMissing = amountNeeded - amountUsed
|
|
||||||
|
|
||||||
make.remove item, amountUsed
|
|
||||||
need.add item, amountMissing
|
|
||||||
|
|
||||||
for productStack in step.recipe.allProducts
|
|
||||||
amountCreated = step.count * productStack.quantity
|
|
||||||
amountNeeded = need.getQuantity productStack.item
|
|
||||||
amountFulfilled = Math.min amountNeeded, amountCreated
|
|
||||||
amountSurplus = amountCreated - amountFulfilled
|
|
||||||
|
|
||||||
need.remove productStack.item, amountFulfilled
|
|
||||||
make.add productStack.item, amountSurplus
|
|
||||||
|
|
||||||
make.merge @_want
|
|
||||||
|
|
||||||
@_make = make
|
|
||||||
@_need = need
|
|
||||||
|
|
||||||
_consolidateSteps: ->
|
|
||||||
steps = []
|
|
||||||
stepsByRecipeId = {}
|
|
||||||
|
|
||||||
for step in @_steps
|
|
||||||
priorStep = stepsByRecipeId[step.recipe.id]
|
|
||||||
if priorStep?
|
|
||||||
priorStep.count += step.count
|
|
||||||
else if step.count > 0
|
|
||||||
steps.push step
|
|
||||||
stepsByRecipeId[step.recipe.id] = step
|
|
||||||
|
|
||||||
@_steps = steps
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - crafting_plan.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
CraftingPlan = require './crafting_plan'
|
|
||||||
CraftingPlanStep = require './crafting_plan_step'
|
|
||||||
Inventory = require '../game/inventory'
|
|
||||||
fixtures = require '../fixtures'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe "CraftingPlan", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@mod = fixtures.createMod()
|
|
||||||
@want = new Inventory
|
|
||||||
@have = new Inventory
|
|
||||||
|
|
||||||
describe "with a single step plan", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@oakPlank = fixtures.configureOakPlank @mod
|
|
||||||
@want.add @oakPlank, 11
|
|
||||||
|
|
||||||
@plan = new CraftingPlan want:@want, steps:[
|
|
||||||
new CraftingPlanStep @oakPlank.firstRecipe
|
|
||||||
]
|
|
||||||
|
|
||||||
it "correctly computes the step counts", ->
|
|
||||||
@plan.steps[0].count.should.equal 3
|
|
||||||
|
|
||||||
it "correctly determines the inputs needed", ->
|
|
||||||
@plan.need.toString(full:true).should.equal "3 Oak Wood"
|
|
||||||
|
|
||||||
it "correctly computes the products created", ->
|
|
||||||
@plan.make.toString(full:true).should.equal "12 Oak Planks"
|
|
||||||
|
|
||||||
describe "with a multi-step plan", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@ironIngot = fixtures.configureIronIngot @mod
|
|
||||||
@ironSword = fixtures.configureIronSword @mod
|
|
||||||
@oakPlank = fixtures.configureOakPlank @mod
|
|
||||||
@stick = fixtures.configureStick @mod
|
|
||||||
|
|
||||||
@want.add @ironSword, 20
|
|
||||||
|
|
||||||
@plan = new CraftingPlan want:@want, steps:[
|
|
||||||
new CraftingPlanStep @oakPlank.firstRecipe
|
|
||||||
new CraftingPlanStep @stick.firstRecipe
|
|
||||||
new CraftingPlanStep @ironIngot.firstRecipe
|
|
||||||
new CraftingPlanStep @ironSword.firstRecipe
|
|
||||||
]
|
|
||||||
|
|
||||||
it "correctly computes the step counts", ->
|
|
||||||
@plan.steps[0].count.should.equal 3
|
|
||||||
@plan.steps[1].count.should.equal 5
|
|
||||||
@plan.steps[2].count.should.equal 5
|
|
||||||
@plan.steps[3].count.should.equal 20
|
|
||||||
|
|
||||||
it "correctly determines the inputs needed", ->
|
|
||||||
@plan.need.toString(full:true).should.equal "5 Coal, 40 Iron Ore, 3 Oak Wood"
|
|
||||||
|
|
||||||
it "correctly computes the products created", ->
|
|
||||||
@plan.make.toString(full:true).should.equal "20 Iron Sword, 2 Oak Planks"
|
|
||||||
|
|
||||||
describe "with a plan which recycles some items", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@bucket = fixtures.configureBucket @mod
|
|
||||||
@cake = fixtures.configureCake @mod
|
|
||||||
@ironIngot = fixtures.configureIronIngot @mod
|
|
||||||
@milkBucket = fixtures.configureMilkBucket @mod
|
|
||||||
@sugar = fixtures.configureSugar @mod
|
|
||||||
|
|
||||||
@want.add @cake, 2
|
|
||||||
|
|
||||||
@plan = new CraftingPlan want:@want, steps:[
|
|
||||||
new CraftingPlanStep @ironIngot.firstRecipe
|
|
||||||
new CraftingPlanStep @bucket.firstRecipe
|
|
||||||
new CraftingPlanStep @milkBucket.firstRecipe
|
|
||||||
new CraftingPlanStep @sugar.firstRecipe
|
|
||||||
new CraftingPlanStep @cake.firstRecipe
|
|
||||||
]
|
|
||||||
|
|
||||||
it "correctly computes the step counts", ->
|
|
||||||
@plan.steps.length.should.equal 3
|
|
||||||
@plan.steps[0].count.should.equal 6
|
|
||||||
@plan.steps[1].count.should.equal 4
|
|
||||||
@plan.steps[2].count.should.equal 2
|
|
||||||
|
|
||||||
it "correctly determines the inputs needed", ->
|
|
||||||
@plan.need.toString(full:true).should.equal "2 Egg, 6 Milk, 4 Sugar Cane, 6 Wheat"
|
|
||||||
|
|
||||||
it "correctly computes the products created", ->
|
|
||||||
@plan.make.toString(full:true).should.equal "2 Cake"
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - crafting_plan_step.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class CraftingPlanStep
|
|
||||||
|
|
||||||
constructor: (recipe, count=1)->
|
|
||||||
@_id = _.uniqueId "crafting-plan-step-"
|
|
||||||
@recipe = recipe
|
|
||||||
@count = count
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
recipe:
|
|
||||||
get: -> return @_recipe
|
|
||||||
set: (recipe)->
|
|
||||||
if not recipe? then throw new Error 'recipe is required'
|
|
||||||
if @_recipe is recipe then return
|
|
||||||
if @_recipe? then throw new Error 'recipe cannot be reassigned'
|
|
||||||
@_recipe = recipe
|
|
||||||
|
|
||||||
count:
|
|
||||||
get: -> return @_count
|
|
||||||
set: (count)->
|
|
||||||
count = parseInt "#{count}"
|
|
||||||
count = if Number.isNaN(count) then 0 else Math.max 0, count
|
|
||||||
@_count = count
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "CraftingPlanStep:#{@_recipe}×#{@_count}<#{@_id}>"
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - evaluation.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Evaluation
|
|
||||||
|
|
||||||
constructor: (attributes={})->
|
|
||||||
@evaluator = attributes.evaluator
|
|
||||||
@item = attributes.item if attributes.item?
|
|
||||||
@recipe = attributes.recipe if attributes.recipe?
|
|
||||||
@baseScore = attributes.baseScore
|
|
||||||
|
|
||||||
@_id = _.uniqueId "evaluation-"
|
|
||||||
@_includedTools = {}
|
|
||||||
@_toolScore = null
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
baseScore:
|
|
||||||
get: -> return @_baseScore
|
|
||||||
set: (baseScore)->
|
|
||||||
baseScore = parseFloat "#{baseScore}"
|
|
||||||
baseScore = if Number.isNaN(baseScore) then null else baseScore
|
|
||||||
@_baseScore = baseScore
|
|
||||||
|
|
||||||
evaluator:
|
|
||||||
get: -> return @_evaluator
|
|
||||||
set: (evaluator)->
|
|
||||||
if not evaluator? then throw new Error "evaluator is required"
|
|
||||||
if @_evaluator is evaluator then return
|
|
||||||
if @_evaluator? then throw new Error "evaluator cannot be reassigned"
|
|
||||||
@_evaluator = evaluator
|
|
||||||
|
|
||||||
includedTools:
|
|
||||||
get: -> return @_includedTools
|
|
||||||
set: -> throw new Error "includedTools cannot be assigned"
|
|
||||||
|
|
||||||
item:
|
|
||||||
get: -> return @_item
|
|
||||||
set: (item)->
|
|
||||||
if @_recipe? then throw new Error "this evaluation is for a recipe: cannot set an item"
|
|
||||||
if @_item is item then return
|
|
||||||
if not item? then throw new Error "item cannot be assigned null"
|
|
||||||
if @_item? then throw new Error "item cannot be reassigned"
|
|
||||||
@_item = item
|
|
||||||
|
|
||||||
recipe:
|
|
||||||
get: -> return @_recipe
|
|
||||||
set: (recipe)->
|
|
||||||
if @_item? then throw new Error "this evaluation is for an item: cannot set an recipe"
|
|
||||||
if @_recipe is recipe then return
|
|
||||||
if not recipe? then throw new Error "recipe cannot be assigned null"
|
|
||||||
if @_recipe? then throw new Error "recipe cannot be reassigned"
|
|
||||||
@_recipe = recipe
|
|
||||||
|
|
||||||
toolScore:
|
|
||||||
get: -> @_computeToolScore()
|
|
||||||
set: -> throw new Error "toolScore cannot be assigned"
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
addIncludedTool: (item)->
|
|
||||||
return if @_includedTools[item.id]?
|
|
||||||
@_includedTools[item.id] = item
|
|
||||||
@_toolScore = null
|
|
||||||
|
|
||||||
addIncludedToolsFrom: (evaluation)->
|
|
||||||
for id, toolItem of evaluation.includedTools
|
|
||||||
@addIncludedTool toolItem
|
|
||||||
|
|
||||||
computeTotalScore: (quantity=1)->
|
|
||||||
if @item?
|
|
||||||
multiplier = quantity
|
|
||||||
else if @recipe?
|
|
||||||
multiplier = Math.ceil 1.0 * quantity / @recipe.output.quantity
|
|
||||||
|
|
||||||
return @baseScore * multiplier + @toolScore
|
|
||||||
|
|
||||||
isToolIncluded: (item)->
|
|
||||||
return @_includedTools[item.id]?
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
obj = if @item? then @item else @recipe
|
|
||||||
return "#{@evaluator.constructor.name}:#{obj}@#{@baseScore}<#{@_id}>"
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_computeToolScore: ->
|
|
||||||
if not @_toolScore?
|
|
||||||
@_toolScore = 0
|
|
||||||
for id, toolItem of @_includedTools
|
|
||||||
evaluation = @evaluator.evaluateItem toolItem
|
|
||||||
continue unless evaluation?.baseScore?
|
|
||||||
@_toolScore += evaluation.baseScore
|
|
||||||
|
|
||||||
return @_toolScore
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - evaluator.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Evaluation = require './evaluation'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Evaluator
|
|
||||||
|
|
||||||
constructor: ->
|
|
||||||
@_id = _.uniqueId "evaluator-"
|
|
||||||
@_evaluations = {}
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
id:
|
|
||||||
get: -> return @_id
|
|
||||||
set: -> throw new Error "id cannot be assigned"
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
evaluateItem: (item)->
|
|
||||||
return null unless item?
|
|
||||||
|
|
||||||
evaluation = @_evaluations[item.id]
|
|
||||||
if not evaluation?
|
|
||||||
evaluation = @_evaluations[item.id] = new Evaluation evaluator:this, item:item
|
|
||||||
|
|
||||||
recipeEvaluation = @_findBestRecipeEvaluationFor item
|
|
||||||
if recipeEvaluation?
|
|
||||||
evaluation.baseScore = recipeEvaluation.baseScore
|
|
||||||
evaluation.addIncludedToolsFrom recipeEvaluation
|
|
||||||
else
|
|
||||||
@_computeGatherableItemScore item, evaluation
|
|
||||||
|
|
||||||
return evaluation
|
|
||||||
|
|
||||||
evaluateRecipe: (recipe)->
|
|
||||||
return null unless recipe?
|
|
||||||
|
|
||||||
evaluation = @_evaluations[recipe.id]
|
|
||||||
if not evaluation?
|
|
||||||
evaluation = @_evaluations[recipe.id] = new Evaluation evaluator:this, recipe:recipe
|
|
||||||
@_computeRecipeScore recipe, evaluation
|
|
||||||
|
|
||||||
for id, item of recipe.inputs
|
|
||||||
inputEvaluation = @evaluateItem item
|
|
||||||
evaluation.addIncludedToolsFrom inputEvaluation
|
|
||||||
|
|
||||||
for id, toolItem of recipe.tools
|
|
||||||
evaluation.addIncludedTool toolItem
|
|
||||||
evaluation.addIncludedToolsFrom @evaluateItem toolItem
|
|
||||||
|
|
||||||
return evaluation
|
|
||||||
|
|
||||||
getOrderedRecipes: (item, quantity=1)->
|
|
||||||
recipes = (recipe for recipeId, recipe of item.recipes)
|
|
||||||
|
|
||||||
recipes.sort (a, b)=>
|
|
||||||
scoreA = @evaluateRecipe(a).computeTotalScore quantity
|
|
||||||
scoreB = @evaluateRecipe(b).computeTotalScore quantity
|
|
||||||
|
|
||||||
if scoreA isnt scoreB
|
|
||||||
return if scoreA < scoreB then -1 else +1
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
return recipes
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "#{@constructor.name}<#{@id}>"
|
|
||||||
|
|
||||||
# Overrideable Methods #########################################################################
|
|
||||||
|
|
||||||
_computeGatherableItemScore: (item, evaluation)->
|
|
||||||
throw new Error "#{@constructor.name} must override _computeGatherableItemScore"
|
|
||||||
|
|
||||||
_computeRecipeScore: (recipe, evaluation)->
|
|
||||||
throw new Error "#{@constructor.name} must override _computeRecipeScore"
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_findBestRecipeEvaluationFor: (item)->
|
|
||||||
return null if item.isGatherable
|
|
||||||
result = null
|
|
||||||
|
|
||||||
for id, recipe of item.recipes
|
|
||||||
evaluation = @evaluateRecipe recipe
|
|
||||||
continue unless evaluation?.baseScore?
|
|
||||||
|
|
||||||
if not result? then result = evaluation
|
|
||||||
if evaluation.baseScore < result.baseScore then result = evaluation
|
|
||||||
|
|
||||||
return result
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - plan_builder.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
CraftingPlan = require "./crafting_plan"
|
|
||||||
CraftingPlanStep = require "./crafting_plan_step"
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class PlanBuilder
|
|
||||||
|
|
||||||
constructor: (evaluator)->
|
|
||||||
@evaluator = evaluator
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
evaluator:
|
|
||||||
get: -> return @_evaluator
|
|
||||||
set: (evaluator)->
|
|
||||||
if not evaluator? then throw new Error "evaluator is required"
|
|
||||||
if @_evaluator is evaluator then return
|
|
||||||
if @_evaluator? then throw new Error "evaluator cannot be reassigned"
|
|
||||||
@_evaluator = evaluator
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
createPlan: (want, have)->
|
|
||||||
@_alreadyMaking = {}
|
|
||||||
@_recipesInUse = {}
|
|
||||||
|
|
||||||
stepList = []
|
|
||||||
for itemId, stack of want.stacks
|
|
||||||
steps = @_findStepsForItem stack.item, stack.quantity
|
|
||||||
return null unless steps?
|
|
||||||
|
|
||||||
stepList.push steps
|
|
||||||
|
|
||||||
plan = new CraftingPlan want:want, have:have, steps:_.flatten(stepList)
|
|
||||||
return plan
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_findStepsForItem: (item, quantity=1)->
|
|
||||||
steps = null
|
|
||||||
|
|
||||||
if not @_alreadyMaking[item.id]
|
|
||||||
@_alreadyMaking[item.id] = true
|
|
||||||
|
|
||||||
recipes = @_evaluator.getOrderedRecipes item, quantity
|
|
||||||
if recipes.length is 0
|
|
||||||
steps = []
|
|
||||||
else
|
|
||||||
for recipe in recipes
|
|
||||||
continue if @_recipesInUse[recipe.id]?
|
|
||||||
|
|
||||||
steps = @_findStepsForRecipe recipe, quantity
|
|
||||||
break if steps?
|
|
||||||
|
|
||||||
delete @_alreadyMaking[item.id]
|
|
||||||
|
|
||||||
return steps
|
|
||||||
|
|
||||||
_findStepsForRecipe: (recipe, quantity=1)->
|
|
||||||
steps = null
|
|
||||||
|
|
||||||
if not @_recipesInUse[recipe.id]
|
|
||||||
@_recipesInUse[recipe.id] = true
|
|
||||||
|
|
||||||
invalidRecipe = false
|
|
||||||
for itemId, item of recipe.inputs
|
|
||||||
inputSteps = @_findStepsForItem item, quantity * recipe.computeQuantityRequired(item)
|
|
||||||
if not inputSteps?
|
|
||||||
invalidRecipe = true
|
|
||||||
break
|
|
||||||
|
|
||||||
steps ?= []
|
|
||||||
for step in inputSteps
|
|
||||||
steps.push step
|
|
||||||
|
|
||||||
if invalidRecipe
|
|
||||||
steps = null
|
|
||||||
else
|
|
||||||
steps.push new CraftingPlanStep recipe
|
|
||||||
|
|
||||||
delete @_recipesInUse[recipe.id]
|
|
||||||
|
|
||||||
return steps
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - plan_builder.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
fixtures = require '../fixtures'
|
|
||||||
Inventory = require '../game/inventory'
|
|
||||||
PlanBuilder = require './plan_builder'
|
|
||||||
ResourcesEvaluator = require './resources_evaluator'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe "PlanBuilder", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@planner = new PlanBuilder new ResourcesEvaluator
|
|
||||||
@mod = fixtures.createMod()
|
|
||||||
@want = new Inventory
|
|
||||||
@have = new Inventory
|
|
||||||
|
|
||||||
describe "for a gatherable item, creates a plan that", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@oakWood = fixtures.configureOakWood @mod
|
|
||||||
@want.add @oakWood, 2
|
|
||||||
@plan = @planner.createPlan @want
|
|
||||||
|
|
||||||
it "has no steps at all", ->
|
|
||||||
@plan.steps.length.should.equal 0
|
|
||||||
|
|
||||||
it "demands the item itself as the input", ->
|
|
||||||
@plan.need.toString(full:true).should.equal "2 Oak Wood"
|
|
||||||
|
|
||||||
it "produces the item as the result", ->
|
|
||||||
@plan.make.toString(full:true).should.equal "2 Oak Wood"
|
|
||||||
|
|
||||||
describe "for a single item with a one-step recipe, it creates a plan that", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@oakPlank = fixtures.configureOakPlank @mod
|
|
||||||
@want.add @oakPlank, 63
|
|
||||||
@plan = @planner.createPlan @want
|
|
||||||
|
|
||||||
it "has the recipe as the only step", ->
|
|
||||||
@plan.steps.length.should.equal 1
|
|
||||||
@plan.steps[0].recipe.output.item.displayName.should.equal "Oak Planks"
|
|
||||||
|
|
||||||
it "demands the right inputs", ->
|
|
||||||
@plan.need.toString(full:true).should.equal "16 Oak Wood"
|
|
||||||
|
|
||||||
it "produces the right results", ->
|
|
||||||
@plan.make.toString(full:true).should.equal "64 Oak Planks"
|
|
||||||
|
|
||||||
describe "for a single item with a complex recipe, it creates a plan that", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@ironSword = fixtures.configureIronSword @mod
|
|
||||||
@want.add @ironSword, 2
|
|
||||||
@plan = @planner.createPlan @want
|
|
||||||
|
|
||||||
it "has the right steps", ->
|
|
||||||
@plan.steps.length.should.equal 4
|
|
||||||
@plan.steps[0].recipe.output.item.displayName.should.equal "Iron Ingot"
|
|
||||||
@plan.steps[1].recipe.output.item.displayName.should.equal "Oak Planks"
|
|
||||||
@plan.steps[2].recipe.output.item.displayName.should.equal "Stick"
|
|
||||||
@plan.steps[3].recipe.output.item.displayName.should.equal "Iron Sword"
|
|
||||||
|
|
||||||
it "demands the right inputs", ->
|
|
||||||
@plan.need.toString(full:true).should.equal "1 Coal, 8 Iron Ore, 1 Oak Wood"
|
|
||||||
|
|
||||||
it "produces the right results", ->
|
|
||||||
@plan.make.toString(full:true).should.equal "4 Iron Ingot, 2 Iron Sword, 2 Oak Planks, 2 Stick"
|
|
||||||
|
|
||||||
describe "for multiple items with complex recipes, it creates a plan that", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@ironSword = fixtures.configureIronSword @mod
|
|
||||||
@ironShovel = fixtures.configureIronShovel @mod
|
|
||||||
@want.add @ironSword, 3
|
|
||||||
@want.add @ironShovel, 3
|
|
||||||
@plan = @planner.createPlan @want
|
|
||||||
|
|
||||||
it "has consolidated the common steps", ->
|
|
||||||
(step.recipe.output.item.displayName for step in @plan.steps).should.eql [
|
|
||||||
"Iron Ingot", "Oak Planks", "Stick", "Iron Sword", "Iron Shovel"
|
|
||||||
]
|
|
||||||
|
|
||||||
it "requires the correct repetitions for each step", ->
|
|
||||||
(step.count for step in @plan.steps).should.eql [ 2, 2, 3, 3, 3 ]
|
|
||||||
|
|
||||||
it "demands the right inputs", ->
|
|
||||||
@plan.need.toString(full:true).should.equal "2 Coal, 16 Iron Ore, 2 Oak Wood"
|
|
||||||
|
|
||||||
it "produces the right results", ->
|
|
||||||
@plan.make.toString(full:true).should.equal(
|
|
||||||
"7 Iron Ingot, 3 Iron Shovel, 3 Iron Sword, 2 Oak Planks, 3 Stick"
|
|
||||||
)
|
|
||||||
|
|
||||||
describe "for an item which has a recursive recipe, it creates a plan that", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@ironBlock = fixtures.configureIronBlock @mod
|
|
||||||
@want.add @ironBlock, 4
|
|
||||||
@plan = @planner.createPlan @want
|
|
||||||
|
|
||||||
it "avoids the recursive options", ->
|
|
||||||
(step.recipe.output.item.displayName for step in @plan.steps).should.eql [
|
|
||||||
"Iron Ingot", "Iron Block"
|
|
||||||
]
|
|
||||||
|
|
||||||
it "requires the correct repetitions for each step", ->
|
|
||||||
(step.count for step in @plan.steps).should.eql [ 5, 4 ]
|
|
||||||
|
|
||||||
it "demands the right inputs", ->
|
|
||||||
@plan.need.toString(full:true).should.equal "5 Coal, 40 Iron Ore"
|
|
||||||
|
|
||||||
it "produces the right results", ->
|
|
||||||
@plan.make.toString(full:true).should.equal(
|
|
||||||
"4 Iron Block, 4 Iron Ingot"
|
|
||||||
)
|
|
||||||
|
|
||||||
describe "when making enormous quantities of something simple", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@oakPlank = fixtures.configureOakPlank @mod
|
|
||||||
@want.add @oakPlank, Number.MAX_VALUE / 2
|
|
||||||
@start = Date.now()
|
|
||||||
@plan = @planner.createPlan @want
|
|
||||||
@duration = Date.now() - @start
|
|
||||||
|
|
||||||
it "doesn't take forever to create the plan", ->
|
|
||||||
@duration.should.be.lessThan 10
|
|
||||||
|
|
||||||
describe "when making an item which can be made with and without a tool, for a", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@oakPlank = fixtures.configureOakPlank @mod
|
|
||||||
@saw = fixtures.configureSaw @mod
|
|
||||||
|
|
||||||
describe "small batch, the plan", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@want.add @oakPlank, 2
|
|
||||||
@plan = @planner.createPlan @want
|
|
||||||
|
|
||||||
it "avoids the expensive tool", ->
|
|
||||||
(step.recipe.needsTools for step in @plan.steps).should.eql [ false ]
|
|
||||||
|
|
||||||
describe "large batch, the plan", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@want.add @oakPlank, 2048
|
|
||||||
@plan = @planner.createPlan @want
|
|
||||||
|
|
||||||
it "uses the tool for efficiency", ->
|
|
||||||
(step.recipe.needsTools for step in @plan.steps).should.eql [ true ]
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - resources_evaluator.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Evaluator = require './evaluator'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ResourcesEvaluator extends Evaluator
|
|
||||||
|
|
||||||
# Evaluator Overrides ##########################################################################
|
|
||||||
|
|
||||||
_computeRecipeScore: (recipe, evaluation)->
|
|
||||||
evaluation.baseScore = 0
|
|
||||||
|
|
||||||
for x in [0...recipe.width]
|
|
||||||
for y in [0...recipe.height]
|
|
||||||
for z in [0...recipe.depth]
|
|
||||||
stack = recipe.getInputAt x, y, z
|
|
||||||
continue unless stack?
|
|
||||||
|
|
||||||
inputEvaluation = @evaluateItem stack.item
|
|
||||||
if inputEvaluation?.baseScore?
|
|
||||||
evaluation.baseScore += inputEvaluation.baseScore * stack.quantity
|
|
||||||
else
|
|
||||||
evaluation.baseScore = null
|
|
||||||
return
|
|
||||||
|
|
||||||
for id, extraStack of recipe.extras
|
|
||||||
extraEvaluation = @evaluateItem extraStack.item
|
|
||||||
continue unless extraEvaluation?.baseScore?
|
|
||||||
evaluation.baseScore -= extraStack.quantity * extraEvaluation.baseScore
|
|
||||||
|
|
||||||
evaluation.baseScore = evaluation.baseScore / recipe.output.quantity
|
|
||||||
|
|
||||||
_computeGatherableItemScore: (item, evaluation)->
|
|
||||||
evaluation.baseScore = 1
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - resources_evaluator.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
ResourcesEvaluator = require './resources_evaluator'
|
|
||||||
fixtures = require '../fixtures'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe "ResourcesEvaluator", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@evaluator = new ResourcesEvaluator
|
|
||||||
@mod = fixtures.createMod()
|
|
||||||
|
|
||||||
describe 'evaluating a gatherable item', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@cobblestone = fixtures.configureCobblestone @mod
|
|
||||||
@evaluation = @evaluator.evaluateItem @cobblestone
|
|
||||||
|
|
||||||
it 'should return a score of 1 for each item', ->
|
|
||||||
# 1 cobblestone
|
|
||||||
@evaluation.computeTotalScore().should.equal 1
|
|
||||||
|
|
||||||
describe 'evaluating a first-level recipe', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@craftingTable = fixtures.configureCraftingTable @mod
|
|
||||||
@evaluation = @evaluator.evaluateItem @craftingTable
|
|
||||||
|
|
||||||
it 'should return a score which is the sum of all resources', ->
|
|
||||||
# 1 oak wood => 4 planks (1/4)
|
|
||||||
# 4 oak planks => 1 crafting table (4 * 1/4 = 1)
|
|
||||||
@evaluation.computeTotalScore().should.equal 1
|
|
||||||
|
|
||||||
describe 'evaluating a recipe which requires a tool', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@furnace = fixtures.configureFurnace @mod
|
|
||||||
@evaluation = @evaluator.evaluateItem @furnace
|
|
||||||
|
|
||||||
it 'includes the cost of the tool', ->
|
|
||||||
# 1 oak wood => 4 planks (1/4)
|
|
||||||
# 4 oak planks => 1 crafting table (4 * 1/4 = 1)
|
|
||||||
# 8 cobblestone =(crafting table)=> 1 furnace (8 + 1 = 9)
|
|
||||||
@evaluation.computeTotalScore().should.equal 9
|
|
||||||
|
|
||||||
describe 'evaluating a recipe where a tool requires a tool', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@ironIngot = fixtures.configureIronIngot @mod
|
|
||||||
@evaluation = @evaluator.evaluateItem @ironIngot
|
|
||||||
|
|
||||||
it 'should include both tools in the score', ->
|
|
||||||
# raw ingredients:
|
|
||||||
# 8 iron ore + 1 coal =(furnace)=> 8 iron ingots ((8 + 1) / 8 = 1.125)
|
|
||||||
# tools:
|
|
||||||
# 8 cobblestone =(crafting table)=> 1 furnace (8)
|
|
||||||
# 4 oak planks => 1 crafting table (4 * 1/4 = 1)
|
|
||||||
# 1 oak wood => 4 planks (1/4)
|
|
||||||
@evaluation.computeTotalScore().should.equal 10.125
|
|
||||||
|
|
||||||
describe 'evaluating a recipe which requires a tool multiple times', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@ironSword = fixtures.configureIronSword @mod
|
|
||||||
@evaluation = @evaluator.evaluateItem @ironSword
|
|
||||||
|
|
||||||
it 'should include the tool only once', ->
|
|
||||||
# raw ingredients:
|
|
||||||
# 1 oak wood ==> 4 planks (0.25)
|
|
||||||
# 2 oak planks ==> 4 sticks (0.125)
|
|
||||||
# 8 iron ore + 1 coal =(furnace)=> 8 iron ingots (1.125)
|
|
||||||
# 2 iron ingot + 1 stick =(crafting table)=> 1 iron sword (2.375)
|
|
||||||
#
|
|
||||||
# tools:
|
|
||||||
# 8 cobblestone =(crafting table)=> 1 furnace (8)
|
|
||||||
#
|
|
||||||
# 1 oak wood => 4 planks (0.25)
|
|
||||||
# 4 oak planks => 1 crafting table (1)
|
|
||||||
#
|
|
||||||
@evaluation.computeTotalScore().should.equal 11.375
|
|
||||||
|
|
||||||
describe 'evaluating a recipe which has a recursive recipe', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@ironBlock = fixtures.configureIronBlock @mod
|
|
||||||
@evaluation = @evaluator.evaluateItem @ironBlock
|
|
||||||
|
|
||||||
it 'should avoid the recursive recipes', ->
|
|
||||||
# raw ingredients:
|
|
||||||
# 8 iron ore + 1 coal =(furnace)=> 8 iron ingots ((8 + 1) / 8 = 1.125)
|
|
||||||
# 9 iron ingot =(crafting table)=> 1 iron block (10.125)
|
|
||||||
#
|
|
||||||
# tools:
|
|
||||||
# 8 cobblestone =(crafting table)=> 1 furnace (8)
|
|
||||||
# 4 oak planks => 1 crafting table (4 * 1/4 = 1)
|
|
||||||
# 1 oak wood => 4 planks (1/4)
|
|
||||||
@evaluation.computeTotalScore().should.equal 19.125
|
|
||||||
|
|
||||||
describe 'evaluating a recipe with multiple ouputs', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@cake = fixtures.configureCake @mod
|
|
||||||
@evaluation = @evaluator.evaluateItem @cake
|
|
||||||
|
|
||||||
it 'should discount the extra outputs', ->
|
|
||||||
# raw ingredients:
|
|
||||||
# 8 iron ore + 1 coal =(furnace)=> 8 iron ingot (1.125)
|
|
||||||
# 3 iron ingot =(crafting table)=> 1 bucket (3.375)
|
|
||||||
# 1 milk + 1 bucket ==> 1 milk bucket (4.375)
|
|
||||||
# 1 sugar cane ==> 1 sugar (1)
|
|
||||||
# 3 wheat + 1 egg + 2 sugar + 3 milk bucket =(crafting table)=> 1 cake, 3 buckets (9)
|
|
||||||
#
|
|
||||||
# furnace:
|
|
||||||
# 8 cobblestone =(crafting table)=> 1 furnace (8)
|
|
||||||
#
|
|
||||||
# crafting table
|
|
||||||
# 1 oak wood ==> 4 oak planks (0.25)
|
|
||||||
# 4 planks ==> 1 crafting table (1)
|
|
||||||
@evaluation.computeTotalScore().should.equal 18
|
|
||||||
|
|
||||||
describe 'evaluating a recipe which is only ever made as an extra', ->
|
|
||||||
|
|
||||||
it 'should still be able to come up with an evaluation'
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - steps_evaluator.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Evaluator = require './evaluator'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class StepsEvaluator extends Evaluator
|
|
||||||
|
|
||||||
# Evaluator Overrides ##########################################################################
|
|
||||||
|
|
||||||
_computeRecipeScore: (recipe, evaluation)->
|
|
||||||
evaluation.score = 0
|
|
||||||
|
|
||||||
for id, stack of recipe.inputs
|
|
||||||
inputEvaluation = @evaluateItem stack.item
|
|
||||||
if not inputEvaluation?.score?
|
|
||||||
evaluation.score = null
|
|
||||||
return
|
|
||||||
|
|
||||||
evaluation.score = Math.min evaluation.score, inputEvaluation.score + 1
|
|
||||||
|
|
||||||
for id, item of recipe.tools
|
|
||||||
continue if evaluation.isToolIncluded item
|
|
||||||
|
|
||||||
toolEvaluation = @evaluateItem stack.item
|
|
||||||
if not toolEvaluation?.score?
|
|
||||||
evaluation.score = null
|
|
||||||
return
|
|
||||||
|
|
||||||
evaluation.addIncludedTool item
|
|
||||||
evaluation.score += toolEvaluation.score
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
_computeGatherableItemScore: (item, evaluation)->
|
|
||||||
evaluation.score = 0
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - event_recorder.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
util = require 'util'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class EventRecorder
|
|
||||||
|
|
||||||
constructor: (model)->
|
|
||||||
if not model? then throw new Error 'model is required'
|
|
||||||
|
|
||||||
@model = model
|
|
||||||
@events = []
|
|
||||||
|
|
||||||
@model.on 'all', (event, model, args...)=>
|
|
||||||
logger.verbose -> "#{model?.constructor?.name}(#{model?.cid}) emitted #{event}
|
|
||||||
with args: #{util.inspect(args)}"
|
|
||||||
@events.push id:model?.cid, event:event, args:args
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
reset: ->
|
|
||||||
@events = []
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
getNames: ->
|
|
||||||
return (e.event for e in @events)
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
names: {get:@prototype.getNames}
|
|
||||||
@@ -1,319 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - fixtures.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Item = require "./game/item"
|
|
||||||
Mod = require "./game/mod"
|
|
||||||
ModPack = require "./game/mod_pack"
|
|
||||||
Recipe = require "./game/recipe"
|
|
||||||
Stack = require "./game/stack"
|
|
||||||
|
|
||||||
# Instance Creation Fixtures ###########################################################################################
|
|
||||||
|
|
||||||
exports.createModPack = createModPack = (attributes={})->
|
|
||||||
attributes.id ?= _.uniqueId "mod-pack-"
|
|
||||||
attributes.displayName ?= "Test ModPack"
|
|
||||||
return new ModPack attributes
|
|
||||||
|
|
||||||
exports.createMod = createMod = (attributes={})->
|
|
||||||
attributes.modPack ?= createModPack()
|
|
||||||
attributes.id ?= _.uniqueId "mod-"
|
|
||||||
attributes.displayName ?= "Test Mod"
|
|
||||||
return new Mod attributes
|
|
||||||
|
|
||||||
exports.createItem = createItem = (attributes={})->
|
|
||||||
attributes.mod ?= createMod()
|
|
||||||
attributes.id ?= _.uniqueId "item-"
|
|
||||||
attributes.displayName ?= _.uniqueId "Test Item "
|
|
||||||
return new Item attributes
|
|
||||||
|
|
||||||
exports.createRecipe = createRecipe = (attributes={})->
|
|
||||||
attributes.id ?= _.uniqueId "recipe-"
|
|
||||||
attributes.output ?= createItem()
|
|
||||||
return new Recipe attributes
|
|
||||||
|
|
||||||
exports.createStack = createStack = (attributes={})->
|
|
||||||
attributes.item ?= createItem()
|
|
||||||
attributes.quantity ?= 1
|
|
||||||
return new Stack attributes
|
|
||||||
|
|
||||||
# Item Configuration Fixtures ##########################################################################################
|
|
||||||
|
|
||||||
exports.configureBucket = configureBucket = (mod)->
|
|
||||||
bucket = mod.modPack.findItem "bucket"
|
|
||||||
if not bucket?
|
|
||||||
ironIngot = configureIronIngot mod
|
|
||||||
bucket = createItem mod:mod, id:"bucket", displayName:"Bucket"
|
|
||||||
craftingTable = configureCraftingTable mod
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:bucket
|
|
||||||
recipe.setInputAt 0, 0, createStack item:ironIngot
|
|
||||||
recipe.setInputAt 1, 1, createStack item:ironIngot
|
|
||||||
recipe.setInputAt 2, 0, createStack item:ironIngot
|
|
||||||
recipe.addTool craftingTable
|
|
||||||
|
|
||||||
return bucket
|
|
||||||
|
|
||||||
exports.configureCake = configureCake = (mod)->
|
|
||||||
cake = mod.modPack.findItem "cake"
|
|
||||||
if not cake?
|
|
||||||
bucket = configureBucket mod
|
|
||||||
cake = createItem mod:mod, id:"cake", displayName:"Cake"
|
|
||||||
craftingTable = configureCraftingTable mod
|
|
||||||
egg = configureEgg mod
|
|
||||||
milkBucket = configureMilkBucket mod
|
|
||||||
sugar = configureSugar mod
|
|
||||||
wheat = configureWheat mod
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:cake
|
|
||||||
recipe.setInputAt 0, 0, createStack item:milkBucket
|
|
||||||
recipe.setInputAt 1, 0, createStack item:milkBucket
|
|
||||||
recipe.setInputAt 2, 0, createStack item:milkBucket
|
|
||||||
recipe.setInputAt 0, 1, createStack item:sugar
|
|
||||||
recipe.setInputAt 1, 1, createStack item:egg
|
|
||||||
recipe.setInputAt 2, 1, createStack item:sugar
|
|
||||||
recipe.setInputAt 0, 2, createStack item:wheat
|
|
||||||
recipe.setInputAt 1, 2, createStack item:wheat
|
|
||||||
recipe.setInputAt 2, 2, createStack item:wheat
|
|
||||||
recipe.addTool craftingTable
|
|
||||||
recipe.addExtra createStack item:bucket, quantity:3
|
|
||||||
|
|
||||||
return cake
|
|
||||||
|
|
||||||
exports.configureCoal = configureCoal = (mod)->
|
|
||||||
coal = mod.modPack.findItem "coal"
|
|
||||||
if not coal?
|
|
||||||
coal = createItem mod:mod, id:"coal", displayName:"Coal"
|
|
||||||
return coal
|
|
||||||
|
|
||||||
exports.configureCobblestone = configureCobblestone = (mod)->
|
|
||||||
cobblestone = mod.modPack.findItem "cobblestone"
|
|
||||||
if not cobblestone?
|
|
||||||
cobblestone = createItem mod:mod, displayName:"Cobblestone"
|
|
||||||
return cobblestone
|
|
||||||
|
|
||||||
exports.configureCraftingTable = configureCraftingTable = (mod)->
|
|
||||||
craftingTable = mod.modPack.findItem "crafting_table"
|
|
||||||
if not craftingTable?
|
|
||||||
craftingTable = createItem mod:mod, id:"crafting_table", displayName:"Crafting Table"
|
|
||||||
oakPlanks = configureOakPlank mod
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:craftingTable
|
|
||||||
recipe.setInputAt 0, 0, createStack item:oakPlanks
|
|
||||||
recipe.setInputAt 1, 0, createStack item:oakPlanks
|
|
||||||
recipe.setInputAt 0, 1, createStack item:oakPlanks
|
|
||||||
recipe.setInputAt 1, 1, createStack item:oakPlanks
|
|
||||||
|
|
||||||
return craftingTable
|
|
||||||
|
|
||||||
exports.configureEgg = configureEgg = (mod)->
|
|
||||||
egg = mod.modPack.findItem "egg"
|
|
||||||
if not egg?
|
|
||||||
egg = createItem mod:mod, id:"egg", displayName:"Egg"
|
|
||||||
return egg
|
|
||||||
|
|
||||||
exports.configureFurnace = configureFurnace = (mod)->
|
|
||||||
furnace = mod.modPack.findItem "furnace"
|
|
||||||
if not furnace?
|
|
||||||
cobblestone = configureCobblestone mod
|
|
||||||
craftingTable = configureCraftingTable mod
|
|
||||||
furnace = createItem mod:mod, displayName:"Furnace"
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:furnace
|
|
||||||
recipe.setInputAt 0, 0, createStack item:cobblestone
|
|
||||||
recipe.setInputAt 1, 0, createStack item:cobblestone
|
|
||||||
recipe.setInputAt 2, 0, createStack item:cobblestone
|
|
||||||
recipe.setInputAt 0, 1, createStack item:cobblestone
|
|
||||||
recipe.setInputAt 2, 1, createStack item:cobblestone
|
|
||||||
recipe.setInputAt 0, 2, createStack item:cobblestone
|
|
||||||
recipe.setInputAt 1, 2, createStack item:cobblestone
|
|
||||||
recipe.setInputAt 2, 2, createStack item:cobblestone
|
|
||||||
recipe.addTool craftingTable
|
|
||||||
|
|
||||||
return furnace
|
|
||||||
|
|
||||||
exports.configureIronIngot = configureIronIngot = (mod)->
|
|
||||||
ironIngot = mod.modPack.findItem "iron_ingot"
|
|
||||||
if not ironIngot?
|
|
||||||
coal = configureCoal mod
|
|
||||||
furnace = configureFurnace mod
|
|
||||||
ironIngot = createItem mod:mod, id:"iron_ingot", displayName:"Iron Ingot"
|
|
||||||
ironOre = configureIronOre mod
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:ironIngot, quantity:8
|
|
||||||
recipe.setInputAt 1, 0, createStack item:ironOre, quantity:8
|
|
||||||
recipe.setInputAt 1, 2, createStack item:coal
|
|
||||||
recipe.addTool furnace
|
|
||||||
|
|
||||||
return ironIngot
|
|
||||||
|
|
||||||
exports.configureIronBlock = configureIronBlock = (mod)->
|
|
||||||
ironBlock = mod.modPack.findItem "iron_block"
|
|
||||||
if not ironBlock?
|
|
||||||
craftingTable = configureCraftingTable mod
|
|
||||||
ironBlock = createItem mod:mod, id:"iron_block", displayName:"Iron Block"
|
|
||||||
ironIngot = configureIronIngot mod
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:ironBlock
|
|
||||||
for x in [0..2]
|
|
||||||
for y in [0..2]
|
|
||||||
recipe.setInputAt x, y, createStack item:ironIngot
|
|
||||||
recipe.addTool craftingTable
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:ironIngot, quantity:9
|
|
||||||
recipe.setInputAt 1, 1, createStack item:ironBlock
|
|
||||||
|
|
||||||
return ironBlock
|
|
||||||
|
|
||||||
exports.configureIronSword = configureIronSword = (mod)->
|
|
||||||
ironSword = mod.modPack.findItem "iron_sword"
|
|
||||||
if not ironSword?
|
|
||||||
craftingTable = configureCraftingTable mod
|
|
||||||
ironIngot = configureIronIngot mod
|
|
||||||
ironSword = createItem mod:mod, id:"iron_sword", displayName:"Iron Sword"
|
|
||||||
stick = configureStick mod
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:ironSword
|
|
||||||
recipe.setInputAt 1, 0, createStack item:ironIngot
|
|
||||||
recipe.setInputAt 1, 1, createStack item:ironIngot
|
|
||||||
recipe.setInputAt 1, 2, createStack item:stick
|
|
||||||
recipe.addTool craftingTable
|
|
||||||
|
|
||||||
return ironSword
|
|
||||||
|
|
||||||
exports.configureIronShovel = configureIronShovel = (mod)->
|
|
||||||
ironShovel = mod.modPack.findItem "iron_shovel"
|
|
||||||
if not ironShovel?
|
|
||||||
craftingTable = configureCraftingTable mod
|
|
||||||
ironIngot = configureIronIngot mod
|
|
||||||
ironShovel = createItem mod:mod, id:"iron_shovel", displayName:"Iron Shovel"
|
|
||||||
stick = configureStick mod
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:ironShovel
|
|
||||||
recipe.setInputAt 1, 0, createStack item:ironIngot
|
|
||||||
recipe.setInputAt 1, 1, createStack item:stick
|
|
||||||
recipe.setInputAt 1, 2, createStack item:stick
|
|
||||||
recipe.addTool craftingTable
|
|
||||||
|
|
||||||
return ironShovel
|
|
||||||
|
|
||||||
exports.configureIronOre = configureIronOre = (mod)->
|
|
||||||
ironOre = mod.modPack.findItem "iron_ore"
|
|
||||||
if not ironOre?
|
|
||||||
ironOre = createItem mod:mod, id:"iron_ore", displayName:"Iron Ore"
|
|
||||||
return ironOre
|
|
||||||
|
|
||||||
exports.configureMilk = configureMilk = (mod)->
|
|
||||||
milk = mod.modPack.findItem "milk"
|
|
||||||
if not milk?
|
|
||||||
milk = createItem mod:mod, id:"milk", displayName:"Milk"
|
|
||||||
return milk
|
|
||||||
|
|
||||||
exports.configureMilkBucket = configureMilkBucket = (mod)->
|
|
||||||
milkBucket = mod.modPack.findItem "milk_bucket"
|
|
||||||
if not milkBucket?
|
|
||||||
bucket = configureBucket mod
|
|
||||||
milk = configureMilk mod
|
|
||||||
milkBucket = createItem mod:mod, id:"milkBucket", displayName:"Milk Bucket"
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:milkBucket
|
|
||||||
recipe.setInputAt 1, 0, createStack item:milk
|
|
||||||
recipe.setInputAt 1, 1, createStack item:bucket
|
|
||||||
|
|
||||||
return milkBucket
|
|
||||||
|
|
||||||
exports.configureOakPlank = configureOakPlank = (mod)->
|
|
||||||
oakPlanks = mod.modPack.findItem "oak_planks"
|
|
||||||
if not oakPlanks?
|
|
||||||
oakPlanks = createItem mod:mod, id:"oak_planks", displayName:"Oak Planks"
|
|
||||||
oakWood = configureOakWood mod
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:oakPlanks, quantity:4
|
|
||||||
recipe.setInputAt 1, 1, createStack item:oakWood
|
|
||||||
|
|
||||||
return oakPlanks
|
|
||||||
|
|
||||||
exports.configureOakWood = configureOakWood = (mod)->
|
|
||||||
oakWood = mod.modPack.findItem "oak_wood"
|
|
||||||
if not oakWood?
|
|
||||||
oakWood = createItem mod:mod, id:"oak_wood", displayName:"Oak Wood"
|
|
||||||
return oakWood
|
|
||||||
|
|
||||||
exports.configureObsidian = configureObsidian = (mod)->
|
|
||||||
obsidian = mod.modPack.findItem "obsidian"
|
|
||||||
if not obsidian?
|
|
||||||
obsidian = createItem mod:mod, id:"obsidian", displayName:"Obsidian"
|
|
||||||
return obsidian
|
|
||||||
|
|
||||||
exports.configureRedstoneDust = configureRedstoneDust = (mod)->
|
|
||||||
redstoneDust = mod.modPack.findItem "redstone_dust"
|
|
||||||
if not redstoneDust?
|
|
||||||
redstoneDust = createItem mod:mod, id:"redstone_dust", displayName:"Redstone Dust"
|
|
||||||
return redstoneDust
|
|
||||||
|
|
||||||
exports.configureStick = configureStick = (mod)->
|
|
||||||
stick = mod.modPack.findItem "stick"
|
|
||||||
if not stick?
|
|
||||||
oakPlanks = configureOakPlank mod
|
|
||||||
stick = createItem mod:mod, id:"stick", displayName:"Stick"
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:stick, quantity:4
|
|
||||||
recipe.setInputAt 0, 0, createStack item:oakPlanks
|
|
||||||
recipe.setInputAt 0, 1, createStack item:oakPlanks
|
|
||||||
|
|
||||||
return stick
|
|
||||||
|
|
||||||
exports.configureSugar = configureSugar = (mod)->
|
|
||||||
sugar = mod.modPack.findItem "sugar"
|
|
||||||
if not sugar?
|
|
||||||
sugar = createItem mod:mod, id:"sugar", displayName:"Sugar"
|
|
||||||
sugarCane = configureSugarCane mod
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:sugar
|
|
||||||
recipe.setInputAt 1, 1, createStack item:sugarCane
|
|
||||||
|
|
||||||
return sugar
|
|
||||||
|
|
||||||
exports.configureSaw = configureSaw = (mod)->
|
|
||||||
saw = mod.modPack.findItem "saw"
|
|
||||||
if not saw?
|
|
||||||
craftingTable = configureCraftingTable mod
|
|
||||||
ironBlock = configureIronBlock mod
|
|
||||||
ironIngot = configureIronIngot mod
|
|
||||||
oakPlank = configureOakPlank mod
|
|
||||||
oakWood = configureOakWood mod
|
|
||||||
redstoneDust = configureRedstoneDust mod
|
|
||||||
saw = createItem mod:mod, id:"saw", displayName:"Saw"
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:saw
|
|
||||||
recipe.setInputAt 0, 0, createStack item:oakPlank
|
|
||||||
recipe.setInputAt 1, 0, createStack item:ironIngot
|
|
||||||
recipe.setInputAt 2, 0, createStack item:oakPlank
|
|
||||||
recipe.setInputAt 0, 1, createStack item:oakPlank
|
|
||||||
recipe.setInputAt 1, 1, createStack item:ironBlock
|
|
||||||
recipe.setInputAt 2, 1, createStack item:oakPlank
|
|
||||||
recipe.setInputAt 0, 2, createStack item:oakPlank
|
|
||||||
recipe.setInputAt 1, 2, createStack item:redstoneDust
|
|
||||||
recipe.setInputAt 2, 2, createStack item:oakPlank
|
|
||||||
recipe.addTool craftingTable
|
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:oakPlank, quantity:8
|
|
||||||
recipe.setInputAt 1, 1, createStack item:oakWood
|
|
||||||
recipe.addTool saw
|
|
||||||
|
|
||||||
return saw
|
|
||||||
|
|
||||||
exports.configureSugarCane = configureSugarCane = (mod)->
|
|
||||||
sugarCane = mod.modPack.findItem "sugar_cane"
|
|
||||||
if not sugarCane?
|
|
||||||
sugarCane = createItem mod:mod, id:"sugar_cane", displayName:"Sugar Cane"
|
|
||||||
return sugarCane
|
|
||||||
|
|
||||||
exports.configureWheat = configureWheat = (mod)->
|
|
||||||
wheat = mod.modPack.findItem "wheat"
|
|
||||||
if not wheat?
|
|
||||||
wheat = createItem mod:mod, id:"wheat", displayName:"Wheat"
|
|
||||||
return wheat
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - inventory.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Stack = require './stack'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Inventory
|
|
||||||
|
|
||||||
constructor: (inventory=null)->
|
|
||||||
@_id = _.uniqueId "inventory-"
|
|
||||||
@_stacks = {}
|
|
||||||
|
|
||||||
if inventory? then @merge inventory
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
isEmpty:
|
|
||||||
get: -> (id for id, stack of @_stacks).length is 0
|
|
||||||
|
|
||||||
stacks:
|
|
||||||
get: -> return @_stacks
|
|
||||||
set: -> throw new Error "stacks cannot be replaced"
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
add: (item, quantity)->
|
|
||||||
return unless item?
|
|
||||||
return if quantity is 0
|
|
||||||
|
|
||||||
existingStack = @_stacks[item.id]
|
|
||||||
if existingStack?
|
|
||||||
if existingStack.quantity + quantity < 0 then throw new Error "cannot have a negative quantity"
|
|
||||||
existingStack.quantity += quantity
|
|
||||||
else
|
|
||||||
if quantity < 0 then throw new Error "cannot have a negative quantity"
|
|
||||||
@_stacks[item.id] = new Stack item:item, quantity:quantity
|
|
||||||
|
|
||||||
if @_stacks[item.id].quantity is 0
|
|
||||||
delete @_stacks[item.id]
|
|
||||||
|
|
||||||
clear: ->
|
|
||||||
@_stacks = {}
|
|
||||||
|
|
||||||
contains: (item)->
|
|
||||||
return @_stacks[item.id]?
|
|
||||||
|
|
||||||
getQuantity: (item)->
|
|
||||||
existingStack = @_stacks[item.id]
|
|
||||||
return 0 unless existingStack?
|
|
||||||
return existingStack.quantity
|
|
||||||
|
|
||||||
merge: (inventory)->
|
|
||||||
for id, stack of inventory.stacks
|
|
||||||
@add stack.item, stack.quantity
|
|
||||||
|
|
||||||
remove: (item, quantity)->
|
|
||||||
@add item, -1 * quantity
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: (options={})->
|
|
||||||
options.full ?= false
|
|
||||||
|
|
||||||
if options.full
|
|
||||||
result = []
|
|
||||||
needsDelimiter = false
|
|
||||||
|
|
||||||
stackList = (stack for itemId, stack of @_stacks)
|
|
||||||
stackList.sort (a, b)->
|
|
||||||
if a.item.displayName isnt b.item.displayName
|
|
||||||
return if a.item.displayName < b.item.displayName then -1 else +1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
for stack in stackList
|
|
||||||
if needsDelimiter then result.push ", "
|
|
||||||
needsDelimiter = true
|
|
||||||
|
|
||||||
result.push stack.quantity
|
|
||||||
result.push " "
|
|
||||||
result.push stack.item.displayName
|
|
||||||
return result.join ""
|
|
||||||
else
|
|
||||||
return "Inventory<#{@_id}>@#{(id for id, item of @_stacks).length}"
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - item.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Item
|
|
||||||
|
|
||||||
constructor: (attributes={})->
|
|
||||||
@id = attributes.id
|
|
||||||
@displayName = attributes.displayName
|
|
||||||
@isGatherable = attributes.isGatherable
|
|
||||||
@mod = attributes.mod
|
|
||||||
|
|
||||||
@_hasPrimaryRecipe = false
|
|
||||||
@_recipesAsPrimary = {}
|
|
||||||
@_recipesAsExtra = {}
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
displayName: # a string containing the user-facing name of this item
|
|
||||||
get: -> return @_displayName
|
|
||||||
set: (displayName)->
|
|
||||||
if not displayName? then throw new Error "displayName is required"
|
|
||||||
@_displayName = displayName
|
|
||||||
|
|
||||||
firstRecipe: # the first Recipe returned by iterating the `recipes` property
|
|
||||||
get: ->
|
|
||||||
recipeList = (recipe for id, recipe of @recipes)
|
|
||||||
return null unless recipeList.length > 0
|
|
||||||
return recipeList[0]
|
|
||||||
set: -> throw new Error "firstRecipe cannot be assigned"
|
|
||||||
|
|
||||||
id: # a string containing a unique identifier for this item
|
|
||||||
get: -> return @_id
|
|
||||||
set: (id)->
|
|
||||||
if not id? then throw new Error "id is required"
|
|
||||||
return if @_id is id
|
|
||||||
if @_id? then throw new Error "id cannot be reassigned"
|
|
||||||
@_id = id
|
|
||||||
|
|
||||||
isGatherable: # whether this item can be gathered directly without needing to be crafted
|
|
||||||
get: ->
|
|
||||||
return true if @_isGatherable is true
|
|
||||||
return false if (id for id, recipe of @_recipesAsPrimary).length > 0
|
|
||||||
return false if (id for id, recipe of @_recipesAsExtra).length > 0
|
|
||||||
return true
|
|
||||||
set: (isGatherable)->
|
|
||||||
@_isGatherable = null unless isGatherable?
|
|
||||||
@_isGatherable = !!isGatherable
|
|
||||||
|
|
||||||
mod: # the Mod which adds this item to the game
|
|
||||||
get: -> return @_mod
|
|
||||||
set: (mod)->
|
|
||||||
if not mod? then throw new Error "mod is required"
|
|
||||||
if @_mod is mod then return
|
|
||||||
if @_mod? then throw new Error "mod cannot be reassigned"
|
|
||||||
@_mod = mod
|
|
||||||
@_mod.addItem this
|
|
||||||
|
|
||||||
modPack: # the ModPack containing this item
|
|
||||||
get: -> return @_mod.modPack
|
|
||||||
set: -> throw new Error "modPack cannot be assigned"
|
|
||||||
|
|
||||||
recipes: # a hash of recipeId to Recipe containing `recipesAsPrimary` if not empty or else `recipesAsExtra`
|
|
||||||
get: -> return if @_hasPrimaryRecipe then @_recipesAsPrimary else @_recipesAsExtra
|
|
||||||
set: -> throw new Error "recipes cannot be assigned"
|
|
||||||
|
|
||||||
recipesAsPrimary: # a hash of recipeId to Recipe where this item is the primary output
|
|
||||||
get: -> return @_recipesAsPrimary
|
|
||||||
set: -> throw new Error "recipes cannot be assigned"
|
|
||||||
|
|
||||||
recipesAsExtra: # a hash of recipeId to Recipe where this item is an extra output
|
|
||||||
get: -> return @_recipesAsExtra
|
|
||||||
set: -> throw new Error "recipesAsExtra cannot be assigned"
|
|
||||||
|
|
||||||
# Public Recipes ###############################################################################
|
|
||||||
|
|
||||||
addRecipe: (recipe)->
|
|
||||||
if recipe.output.item is this
|
|
||||||
@_recipesAsPrimary[recipe.id] = recipe
|
|
||||||
@_hasPrimaryRecipe = true
|
|
||||||
else if recipe.extras[this.id]?.item is this
|
|
||||||
@_recipesAsExtra[recipe.id] = recipe
|
|
||||||
else
|
|
||||||
throw new Error "recipe<#{recipe.id}> does not produce this item<#{@id}>"
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "Item:#{@displayName}<#{@id}>"
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Mod
|
|
||||||
|
|
||||||
constructor: (attributes={})->
|
|
||||||
@id = attributes.id
|
|
||||||
@displayName = attributes.displayName
|
|
||||||
@modPack = attributes.modPack
|
|
||||||
|
|
||||||
@_items = {}
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
displayName:
|
|
||||||
get: -> return @_displayName
|
|
||||||
set: (displayName)->
|
|
||||||
if not displayName? then throw new Error "displayName is required"
|
|
||||||
return if @_displayName is displayName
|
|
||||||
@_displayName = displayName
|
|
||||||
|
|
||||||
id:
|
|
||||||
get: -> return @_id
|
|
||||||
set: (id)->
|
|
||||||
if not id? then throw new Error "id is required"
|
|
||||||
return if @_id is id
|
|
||||||
if @_id? then throw new Error "id cannot be reassigned"
|
|
||||||
@_id = id
|
|
||||||
|
|
||||||
items:
|
|
||||||
get: -> return @_items
|
|
||||||
set: -> throw new Error "items cannot be replaced"
|
|
||||||
|
|
||||||
modPack:
|
|
||||||
get: -> return @_modPack
|
|
||||||
set: (modPack)->
|
|
||||||
if not modPack? then throw new Error "modPack is required"
|
|
||||||
if @_modPack is modPack then return
|
|
||||||
if @_modPack? then throw new Error "modPack cannot be reassigned"
|
|
||||||
@_modPack = modPack
|
|
||||||
@_modPack.addMod this
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
addItem: (item)->
|
|
||||||
if not item? then return
|
|
||||||
if @_items[item.id] is item then return
|
|
||||||
@_items[item.id] = item
|
|
||||||
item.mod = this
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "Mod:#{@displayName}<#{@id}>"
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_pack.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ModPack
|
|
||||||
|
|
||||||
constructor: (attributes={})->
|
|
||||||
@id = attributes.id
|
|
||||||
@displayName = attributes.displayName
|
|
||||||
|
|
||||||
@_mods = {}
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
displayName: # a string containing the user-displayable name of this ModPack
|
|
||||||
get: -> return @_displayName
|
|
||||||
set: (displayName)->
|
|
||||||
if not displayName? then throw new Error "displayName is required"
|
|
||||||
if @_displayName is displayName then return
|
|
||||||
@_displayName = displayName
|
|
||||||
|
|
||||||
id: # a string which uniquely identifies this ModPack
|
|
||||||
get: -> return @_id
|
|
||||||
set: (id)->
|
|
||||||
if not id? then throw new Error "id is required"
|
|
||||||
if @_id is id then return
|
|
||||||
if @_id? then throw new Error "id cannot be reassigned"
|
|
||||||
@_id = id
|
|
||||||
|
|
||||||
mods: # a hash of mod id to Mod containing all the mods which are part of this ModPack
|
|
||||||
get: -> return @_mods
|
|
||||||
set: -> throw new Error "mods cannot be replaced"
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
addMod: (mod)->
|
|
||||||
if not mod? then return
|
|
||||||
if @_mods[mod.id] is mod then return
|
|
||||||
@_mods[mod.id] = mod
|
|
||||||
mod.modPack = this
|
|
||||||
|
|
||||||
findItem: (itemId)->
|
|
||||||
for modId, mod of @mods
|
|
||||||
item = mod.items[itemId]
|
|
||||||
return item if item?
|
|
||||||
|
|
||||||
return null
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "ModPack:#{@displayName}<#{@id}>"
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - recipe.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
{StringBuilder} = require 'crafting-guide-common'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Recipe
|
|
||||||
|
|
||||||
constructor: (attributes={})->
|
|
||||||
@depth = attributes.depth
|
|
||||||
@height = attributes.height
|
|
||||||
@id = attributes.id
|
|
||||||
@output = attributes.output
|
|
||||||
@width = attributes.width
|
|
||||||
|
|
||||||
@_extras = {}
|
|
||||||
@_inputs = {}
|
|
||||||
@_inputGrid = []
|
|
||||||
@_tools = {}
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
allProducts: # an array of Stacks starting the the primary output of this recipe
|
|
||||||
get: -> return [].concat @output, (stack for id, stack of @extras)
|
|
||||||
set: -> throw new Error "allProducts cannot be assigned"
|
|
||||||
|
|
||||||
depth: # an integer specifying the number of layers to this recipe
|
|
||||||
get: -> return @_depth
|
|
||||||
set: (depth)->
|
|
||||||
depth = parseInt "#{depth}"
|
|
||||||
depth = if Number.isNaN(depth) then 0 else Math.max(0, depth)
|
|
||||||
@_depth = depth
|
|
||||||
|
|
||||||
extras: # a hash of item id to Stack of all the non-primary outputs of this recipe
|
|
||||||
get: -> return @_extras
|
|
||||||
set: -> throw new Error "extras cannot be replaced"
|
|
||||||
|
|
||||||
id: # a string which uniquely identifies this recipe
|
|
||||||
get: -> return @_id
|
|
||||||
set: (id)->
|
|
||||||
if not id? then throw new Error "id is required"
|
|
||||||
if @_id is id then return
|
|
||||||
if @_id? then throw new Error "id cannot be reassigned"
|
|
||||||
@_id = id
|
|
||||||
|
|
||||||
height: # an integer specifying the number of rows needed by this recipe
|
|
||||||
get: -> return @_height
|
|
||||||
set: (height)->
|
|
||||||
height = parseInt "#{height}"
|
|
||||||
height = if Number.isNaN(height) then 0 else Math.max(0, height)
|
|
||||||
@_height = height
|
|
||||||
|
|
||||||
inputs: # a hash of item id to Item containing all the inputs to this recipe
|
|
||||||
get: -> return @_inputs
|
|
||||||
set: -> throw new Error "inputs cannot be replaced"
|
|
||||||
|
|
||||||
needsTools: # a boolean indicating whether this recipe requires a tool
|
|
||||||
get: -> return (id for id, toolItem of @_tools).length > 0
|
|
||||||
|
|
||||||
output: # a Stack specifying the primary output of this recipe
|
|
||||||
get: -> return @_output
|
|
||||||
set: (output)->
|
|
||||||
if not output? then throw new Error "output is required"
|
|
||||||
if @_output is output then return
|
|
||||||
if @_output? then throw new Error "output cannot be reassigned"
|
|
||||||
@_output = output
|
|
||||||
@_output.item.addRecipe this
|
|
||||||
|
|
||||||
modPack: # the ModPack to which this recipe belongs
|
|
||||||
get: -> return @_output.modPack
|
|
||||||
set: -> throw new Error "modPack cannot be replaced"
|
|
||||||
|
|
||||||
tools: # a hash of item id to Item of all the tools required for this recipe
|
|
||||||
get: -> return @_tools
|
|
||||||
set: -> throw new Error "tools cannot be assigned"
|
|
||||||
|
|
||||||
width: # an integer specifying the number of columns needed by this recipe
|
|
||||||
get: -> return @_width
|
|
||||||
set: (width)->
|
|
||||||
width = parseInt "#{width}"
|
|
||||||
width = if Number.isNaN(width) then 0 else Math.max(0, width)
|
|
||||||
@_width = width
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
addExtra: (stack)->
|
|
||||||
return unless stack
|
|
||||||
return if @_extras[stack.item.id] is stack
|
|
||||||
@_extras[stack.item.id] = stack
|
|
||||||
stack.item.addRecipe this
|
|
||||||
|
|
||||||
addTool: (item)->
|
|
||||||
return unless item
|
|
||||||
@_tools[item.id] = item
|
|
||||||
|
|
||||||
computeQuantityRequired: (item)->
|
|
||||||
result = 0
|
|
||||||
|
|
||||||
for x in [0...@width]
|
|
||||||
for y in [0...@height]
|
|
||||||
for z in [0...@depth]
|
|
||||||
stack = @getInputAt x, y, z
|
|
||||||
continue unless stack?
|
|
||||||
continue unless stack.item.id is item.id
|
|
||||||
result += stack.quantity
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
computeQuantityProduced: (item)->
|
|
||||||
result = 0
|
|
||||||
|
|
||||||
if @_output.item.id is item.id
|
|
||||||
result += @_output.quantity
|
|
||||||
|
|
||||||
for itemId, stack of @_extras
|
|
||||||
continue unless itemId is item.id
|
|
||||||
result += stack.quantity
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
getInputAt: ->
|
|
||||||
[x, y, z] = [0, 0, 0]
|
|
||||||
if arguments.length is 3
|
|
||||||
[x, y, z] = arguments
|
|
||||||
else
|
|
||||||
[x, y] = arguments
|
|
||||||
|
|
||||||
return @_inputGrid[x]?[y]?[z] or null
|
|
||||||
|
|
||||||
setInputAt: ->
|
|
||||||
[x, y, z, stack] = [0, 0, 0, null]
|
|
||||||
if arguments.length is 4
|
|
||||||
[x, y, z, stack] = arguments
|
|
||||||
else
|
|
||||||
[x, y, stack] = arguments
|
|
||||||
|
|
||||||
@_depth = Math.max @_depth, z + 1
|
|
||||||
@_height = Math.max @_height, y + 1
|
|
||||||
@_width = Math.max @_width, x + 1
|
|
||||||
|
|
||||||
@_inputGrid[x] ?= []
|
|
||||||
@_inputGrid[x][y] ?= []
|
|
||||||
@_inputGrid[x][y][z] = stack
|
|
||||||
|
|
||||||
if stack?.item? then @_inputs[stack.item.id] = stack.item
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: (options={})->
|
|
||||||
options.full ?= false
|
|
||||||
|
|
||||||
if options.full
|
|
||||||
b = new StringBuilder
|
|
||||||
b.loop (item for id, item of @inputs), delimiter:" + ", onEach:(b, item)=>
|
|
||||||
b.push @computeQuantityRequired(item), " ", item.displayName
|
|
||||||
b.push " ="
|
|
||||||
b.onlyIf @needsTools, (b)=>
|
|
||||||
b.push "("
|
|
||||||
b.loop (toolItem for id, toolItem of @tools), onEach:(b, toolItem)-> b.push toolItem.displayName
|
|
||||||
b.push ")"
|
|
||||||
b.push "=> "
|
|
||||||
b.loop @allProducts, delimiter:" + ", onEach:(b, stack)=>
|
|
||||||
b.push stack.quantity, " ", stack.item.displayName
|
|
||||||
|
|
||||||
return b.toString()
|
|
||||||
else
|
|
||||||
return "Recipe:#{@output}<#{@id}>"
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - recipe.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
fixtures = require "../fixtures"
|
|
||||||
Item = require "./item"
|
|
||||||
Recipe = require "./recipe"
|
|
||||||
Stack = require "./stack"
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe "Recipe", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@mod = fixtures.createMod()
|
|
||||||
@stick = fixtures.configureStick @mod
|
|
||||||
@ironIngot = fixtures.configureIronIngot @mod
|
|
||||||
@obsidian = fixtures.configureObsidian @mod
|
|
||||||
|
|
||||||
@ironSword = new Item id:"iron_sword", displayName:"Iron Sword", mod:@mod
|
|
||||||
@obsidianBox = new Item id:"obsidian_box", displayName:"Obsidian Box", mod:@mod
|
|
||||||
|
|
||||||
describe "getting & setting inputs", ->
|
|
||||||
|
|
||||||
describe "for 2D recipes", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@recipe = new Recipe id:"test1", output:new Stack item:@ironSword
|
|
||||||
@recipe.setInputAt 1, 0, new Stack item:@ironIngot
|
|
||||||
@recipe.setInputAt 1, 1, new Stack item:@ironIngot
|
|
||||||
@recipe.setInputAt 1, 2, new Stack item:@stick
|
|
||||||
|
|
||||||
it "returns assigned values as expected", ->
|
|
||||||
expect(@recipe.getInputAt(0, 0)).to.equal null
|
|
||||||
@recipe.getInputAt(1, 0).item.displayName.should.equal "Iron Ingot"
|
|
||||||
@recipe.getInputAt(1, 1).item.displayName.should.equal "Iron Ingot"
|
|
||||||
@recipe.getInputAt(1, 2).item.displayName.should.equal "Stick"
|
|
||||||
expect(@recipe.getInputAt(2, 2)).to.equal null
|
|
||||||
|
|
||||||
it "determines the correct dimentions", ->
|
|
||||||
@recipe.depth.should.equal 1
|
|
||||||
@recipe.height.should.equal 3
|
|
||||||
@recipe.width.should.equal 2
|
|
||||||
|
|
||||||
describe "for 3D recipes", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@recipe = new Recipe id:"test2", output:new Stack item:@obsidianBox
|
|
||||||
for x in [0..2]
|
|
||||||
for y in [0..2]
|
|
||||||
for z in [0..2]
|
|
||||||
continue if x is 1 and y is 1
|
|
||||||
continue if x is 1 and z is 1
|
|
||||||
continue if y is 1 and z is 1
|
|
||||||
|
|
||||||
@recipe.setInputAt x, y, z, new Stack item:@obsidian
|
|
||||||
|
|
||||||
it "returns assigned values as expected", ->
|
|
||||||
@recipe.getInputAt(0, 0, 0).item.displayName.should.equal "Obsidian"
|
|
||||||
@recipe.getInputAt(2, 0, 0).item.displayName.should.equal "Obsidian"
|
|
||||||
@recipe.getInputAt(0, 2, 0).item.displayName.should.equal "Obsidian"
|
|
||||||
@recipe.getInputAt(2, 2, 2).item.displayName.should.equal "Obsidian"
|
|
||||||
expect(@recipe.getInputAt(1, 1, 1)).to.equal null
|
|
||||||
expect(@recipe.getInputAt(1, 1, 0)).to.equal null
|
|
||||||
|
|
||||||
it "determines the correct dimentions", ->
|
|
||||||
@recipe.depth.should.equal 3
|
|
||||||
@recipe.height.should.equal 3
|
|
||||||
@recipe.width.should.equal 3
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - stack.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Stack
|
|
||||||
|
|
||||||
constructor: (attributes={})->
|
|
||||||
@item = attributes.item
|
|
||||||
@quantity = attributes.quantity
|
|
||||||
|
|
||||||
# Properties ###################################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
item:
|
|
||||||
get: -> return @_item
|
|
||||||
set: (item)->
|
|
||||||
if not item? then throw new Error "item is required"
|
|
||||||
if @_item is item then return
|
|
||||||
if @_item? then throw new Error "item cannot be reassigned"
|
|
||||||
@_item = item
|
|
||||||
|
|
||||||
modPack:
|
|
||||||
get: -> return @_item.modPack
|
|
||||||
set: -> throw new Error "modPack cannot be replaced"
|
|
||||||
|
|
||||||
quantity:
|
|
||||||
get: -> return @_quantity
|
|
||||||
set: (quantity)->
|
|
||||||
quantity = parseInt "#{quantity}"
|
|
||||||
quantity = if Number.isNaN(quantity) then 0 else Math.max(0, quantity)
|
|
||||||
@_quantity = quantity
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "Stack:#{@item}×#{@quantity}"
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_pack_json.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
fixtures = require "../fixtures"
|
|
||||||
ModPackJsonFormatter = require "./mod_pack_json_formatter"
|
|
||||||
ModPackJsonParser = require "./mod_pack_json_parser"
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe "ModPackJsonParser & ModPackJsonFormatter", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@formatter = new ModPackJsonFormatter
|
|
||||||
@parser = new ModPackJsonParser
|
|
||||||
|
|
||||||
@modPack = fixtures.createModPack id:"alpha", displayName:"ALPHA"
|
|
||||||
|
|
||||||
@runTest = =>
|
|
||||||
@string1 = @formatter.format @modPack
|
|
||||||
@string2 = @formatter.format @parser.parse @string1
|
|
||||||
@result = @parser.parse @string2
|
|
||||||
|
|
||||||
describe "an empty modpack", ->
|
|
||||||
|
|
||||||
beforeEach -> @runTest()
|
|
||||||
|
|
||||||
it "can survive a round trip", ->
|
|
||||||
@string1.should.equal @string2
|
|
||||||
|
|
||||||
it "contains the modpack's own properties", ->
|
|
||||||
@result.id.should.equal @modPack.id
|
|
||||||
@result.displayName.should.equal @modPack.displayName
|
|
||||||
|
|
||||||
it "doesn't contain a mods list", ->
|
|
||||||
expect(@result.mods).to.beUndefined
|
|
||||||
|
|
||||||
describe "a modpack with a single mod", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@mod = fixtures.createMod modPack:@modPack, id:"bravo", displayName:"BRAVO"
|
|
||||||
|
|
||||||
describe "containing only a gatherable item", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@oakWood = fixtures.configureOakWood @mod
|
|
||||||
@runTest()
|
|
||||||
|
|
||||||
it "can survive a round trip", ->
|
|
||||||
@string1.should.equal @string2
|
|
||||||
|
|
||||||
it "contains the correct mod", ->
|
|
||||||
mod = @result.mods[@mod.id]
|
|
||||||
mod.displayName.should.equal @mod.displayName
|
|
||||||
|
|
||||||
it "contains the item", ->
|
|
||||||
item = @result.mods[@mod.id].items[@oakWood.id]
|
|
||||||
item.displayName.should.equal @oakWood.displayName
|
|
||||||
|
|
||||||
describe "containing a multi-step item & it's requirements", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@craftingTable = fixtures.configureCraftingTable @mod
|
|
||||||
@oakPlank = fixtures.configureOakPlank @mod
|
|
||||||
@runTest()
|
|
||||||
|
|
||||||
it "can survive a round trip", ->
|
|
||||||
@string1.should.equal @string2
|
|
||||||
|
|
||||||
it "contains oak planks", ->
|
|
||||||
item = @result.mods[@mod.id].items[@oakPlank.id]
|
|
||||||
item.displayName.should.equal @oakPlank.displayName
|
|
||||||
|
|
||||||
it "has the recipe for a crafting table", ->
|
|
||||||
recipe = @result.mods[@mod.id].items[@craftingTable.id].firstRecipe
|
|
||||||
recipe.getInputAt(0, 0).item.id.should.equal @oakPlank.id
|
|
||||||
recipe.getInputAt(0, 1).item.id.should.equal @oakPlank.id
|
|
||||||
recipe.getInputAt(1, 0).item.id.should.equal @oakPlank.id
|
|
||||||
recipe.getInputAt(1, 1).item.id.should.equal @oakPlank.id
|
|
||||||
recipe.output.quantity.should.equal 1
|
|
||||||
|
|
||||||
describe "containing a complex item which needs tools & it's requirements", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@cake = fixtures.configureCake @mod
|
|
||||||
@runTest()
|
|
||||||
|
|
||||||
it "can survive a round trip", ->
|
|
||||||
@string1.should.equal @string2
|
|
||||||
|
|
||||||
it "has the recipe for a cake", ->
|
|
||||||
recipe = @result.mods[@mod.id].items[@cake.id].firstRecipe
|
|
||||||
|
|
||||||
describe "a modpack with multiple mods", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@modA = fixtures.createMod modPack:@modPack, id:"bravo", displayName:"BRAVO"
|
|
||||||
@modB = fixtures.createMod modPack:@modPack, id:"charlie", displayName:"CHARLIE"
|
|
||||||
|
|
||||||
describe "where items are used in recipes crossing mods", ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@stick = fixtures.configureStick @modA
|
|
||||||
@ironIngot = fixtures.configureIronIngot @modA
|
|
||||||
@ironSword = fixtures.configureIronSword @modB
|
|
||||||
@runTest()
|
|
||||||
|
|
||||||
it "can survive a round trip", ->
|
|
||||||
@string1.should.equal @string2
|
|
||||||
|
|
||||||
it "has each item in the correct mod", ->
|
|
||||||
@result.mods[@modA.id].items[@ironIngot.id].displayName.should.equal @ironIngot.displayName
|
|
||||||
@result.mods[@modB.id].items[@ironSword.id].displayName.should.equal @ironSword.displayName
|
|
||||||
|
|
||||||
it "has the correct recipe for an iron sword", ->
|
|
||||||
recipe = @result.mods[@modB.id].items[@ironSword.id].firstRecipe
|
|
||||||
recipe.output.item.id.should.equal @ironSword.id
|
|
||||||
recipe.output.quantity.should.equal 1
|
|
||||||
recipe.getInputAt(1, 0).item.id.should.equal @ironIngot.id
|
|
||||||
recipe.getInputAt(1, 1).item.id.should.equal @ironIngot.id
|
|
||||||
recipe.getInputAt(1, 2).item.id.should.equal @stick.id
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_pack_json_formatter.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ModPackJsonParser
|
|
||||||
|
|
||||||
constructor: ->
|
|
||||||
@_reset()
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
format: (modPack)->
|
|
||||||
@_reset()
|
|
||||||
return JSON.stringify @_formatModPack modPack
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_formatItem: (item)->
|
|
||||||
result = {}
|
|
||||||
result.id = item.id
|
|
||||||
result.displayName = item.displayName
|
|
||||||
|
|
||||||
if item.isGatherable and item.firstRecipe?
|
|
||||||
result.gatherable = true
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
_formatMod: (mod)->
|
|
||||||
result = {}
|
|
||||||
result.id = mod.id
|
|
||||||
result.displayName = mod.displayName
|
|
||||||
|
|
||||||
for itemId, item of mod.items
|
|
||||||
result.items ?= []
|
|
||||||
@_itemIndexById[item.id] = @_itemIndex++
|
|
||||||
result.items.push @_formatItem item
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
_formatModPack: (modPack)->
|
|
||||||
result = {}
|
|
||||||
result.id = modPack.id
|
|
||||||
result.displayName = modPack.displayName
|
|
||||||
|
|
||||||
for modId, mod of modPack.mods
|
|
||||||
result.mods ?= []
|
|
||||||
result.mods.push @_formatMod mod
|
|
||||||
|
|
||||||
if result.mods?
|
|
||||||
for modResult in result.mods
|
|
||||||
for itemResult in modResult.items
|
|
||||||
item = modPack.mods[modResult.id].items[itemResult.id]
|
|
||||||
for recipeId, recipe of item.recipesAsPrimary
|
|
||||||
itemResult.recipes ?= []
|
|
||||||
itemResult.recipes.push @_formatRecipe recipe
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
_formatRecipe: (recipe)->
|
|
||||||
result = {}
|
|
||||||
result.id = recipe.id
|
|
||||||
|
|
||||||
if recipe.output.quantity > 1
|
|
||||||
result.quantity = recipe.output.quantity
|
|
||||||
|
|
||||||
result.width = recipe.width
|
|
||||||
result.height = recipe.height
|
|
||||||
result.depth = recipe.depth if recipe.depth > 1
|
|
||||||
result.inputs = []
|
|
||||||
|
|
||||||
for x in [0...recipe.width]
|
|
||||||
for y in [0...recipe.height]
|
|
||||||
for z in [0...recipe.depth]
|
|
||||||
inputStack = @_formatStack recipe.getInputAt x, y, z
|
|
||||||
result.inputs.push inputStack
|
|
||||||
|
|
||||||
for itemId, stack of recipe.extras
|
|
||||||
result.extras ?= []
|
|
||||||
result.extras.push @_formatStack stack
|
|
||||||
|
|
||||||
for itemId, item of recipe.tools
|
|
||||||
result.tools ?= []
|
|
||||||
result.tools.push @_itemIndexById[itemId]
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
_formatStack: (stack)->
|
|
||||||
return null unless stack?
|
|
||||||
|
|
||||||
itemIndex = @_itemIndexById[stack.item.id]
|
|
||||||
if stack.quantity is 1 then return itemIndex
|
|
||||||
return [itemIndex, stack.quantity]
|
|
||||||
|
|
||||||
_reset: ->
|
|
||||||
@_itemIndex = 0
|
|
||||||
@_itemIndexById = {}
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_pack_json_parser.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Item = require "../game/item"
|
|
||||||
Mod = require "../game/mod"
|
|
||||||
ModPack = require "../game/mod_pack"
|
|
||||||
Recipe = require "../game/recipe"
|
|
||||||
Stack = require "../game/stack"
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class ModPackJsonParser
|
|
||||||
|
|
||||||
constructor: ->
|
|
||||||
@_reset()
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
parse: (arg, fileName=null)->
|
|
||||||
@_reset()
|
|
||||||
@_fileName = fileName
|
|
||||||
|
|
||||||
if _.isString arg
|
|
||||||
@_parseText arg
|
|
||||||
else
|
|
||||||
@_parseObject arg
|
|
||||||
|
|
||||||
return @_modPack
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_parseInteger: (text, defaultValue)->
|
|
||||||
result = parseInt "#{text}"
|
|
||||||
result = if Number.isNaN result then defaultValue else result
|
|
||||||
return result
|
|
||||||
|
|
||||||
_parseText: (text)->
|
|
||||||
try
|
|
||||||
obj = JSON.parse text
|
|
||||||
catch error
|
|
||||||
@_throwError "could not parse JSON: #{error}"
|
|
||||||
|
|
||||||
@_parseObject obj
|
|
||||||
|
|
||||||
_parseObject: (obj)->
|
|
||||||
@_data = obj
|
|
||||||
@_parseModPack()
|
|
||||||
@_parseMods()
|
|
||||||
@_parseItems()
|
|
||||||
@_parseRecipes()
|
|
||||||
|
|
||||||
_parseModPack: ->
|
|
||||||
if not @_data? then @_throwError "there is no valid data"
|
|
||||||
if not @_data.id? then @_throwError "modPack requires an id"
|
|
||||||
if not @_data.displayName? then @_throwError "modPack requires a displayName"
|
|
||||||
|
|
||||||
@_modPack = new ModPack id:@_data.id, displayName:@_data.displayName
|
|
||||||
|
|
||||||
_parseMods: ->
|
|
||||||
return unless @_data.mods?
|
|
||||||
|
|
||||||
for modData, index in @_data.mods
|
|
||||||
@_location = "mods[#{index}]"
|
|
||||||
if not modData.id? then @_throwError "mod requires an id"
|
|
||||||
if not modData.displayName? then @_throwError "mod requires a displayName"
|
|
||||||
|
|
||||||
new Mod modPack:@_modPack, id:modData.id, displayName:modData.displayName
|
|
||||||
|
|
||||||
_parseItems: ->
|
|
||||||
return unless @_data.mods?
|
|
||||||
|
|
||||||
for modData in @_data.mods
|
|
||||||
continue unless modData.items?
|
|
||||||
|
|
||||||
mod = @_modPack.mods[modData.id]
|
|
||||||
for itemData, index in modData.items
|
|
||||||
@_location = "<#{mod.id}>.items[#{index}]"
|
|
||||||
if not itemData.id? then @_throwError "item requires an id"
|
|
||||||
if not itemData.displayName? then @_throwError "item requires a displayName"
|
|
||||||
|
|
||||||
item = new Item mod:mod, id:itemData.id, displayName:itemData.displayName
|
|
||||||
item.gatherable = itemData.gatherable if itemData.gatherable?
|
|
||||||
@_items.push item
|
|
||||||
|
|
||||||
_parseRecipes: ->
|
|
||||||
return unless @_data.mods?
|
|
||||||
|
|
||||||
for modData in @_data.mods
|
|
||||||
continue unless modData.items?
|
|
||||||
|
|
||||||
mod = @_modPack.mods[modData.id]
|
|
||||||
for itemData, index in modData.items
|
|
||||||
continue unless itemData.recipes?
|
|
||||||
|
|
||||||
item = mod.items[itemData.id]
|
|
||||||
for recipeData, index in itemData.recipes
|
|
||||||
@_location = "<#{itemData.id}>.recipes[#{index}]"
|
|
||||||
if not recipeData.id? then @_throwError "recipe requires id"
|
|
||||||
if not recipeData.inputs? then @_throwError "recipe requires inputs"
|
|
||||||
|
|
||||||
quantity = @_parseInteger recipeData.quantity, 1
|
|
||||||
outputStack = new Stack item:item, quantity:quantity
|
|
||||||
recipe = new Recipe id:recipeData.id, output:outputStack
|
|
||||||
|
|
||||||
depth = @_parseInteger recipeData.depth, 1
|
|
||||||
height = @_parseInteger recipeData.height, 3
|
|
||||||
width = @_parseInteger recipeData.width, 3
|
|
||||||
|
|
||||||
index = 0
|
|
||||||
for x in [0...width]
|
|
||||||
for y in [0...height]
|
|
||||||
for z in [0...depth]
|
|
||||||
stack = @_parseStack recipeData.inputs[index]
|
|
||||||
if stack? then recipe.setInputAt x, y, z, stack
|
|
||||||
index++
|
|
||||||
|
|
||||||
if recipeData.extras
|
|
||||||
for stackData in recipeData.extras
|
|
||||||
recipe.addExtra @_parseStack stackData
|
|
||||||
|
|
||||||
if recipeData.tools
|
|
||||||
for index in recipeData.tools
|
|
||||||
toolItem = @_items[index]
|
|
||||||
if not toolItem? then @_throwError "there is no item #{index}"
|
|
||||||
recipe.addTool toolItem
|
|
||||||
|
|
||||||
_parseStack: (stackData)->
|
|
||||||
return null unless stackData?
|
|
||||||
if _.isArray(stackData)
|
|
||||||
if stackData.length isnt 2 then @_throwError "input stacks must have an item index and a quantity"
|
|
||||||
index = stackData[0]
|
|
||||||
quantity = stackData[1]
|
|
||||||
else
|
|
||||||
index = stackData
|
|
||||||
quantity = 1
|
|
||||||
|
|
||||||
item = @_items[index]
|
|
||||||
if not item? then @_throwError "there is no item #{index}"
|
|
||||||
|
|
||||||
return new Stack item:item, quantity:quantity
|
|
||||||
|
|
||||||
_reset: ->
|
|
||||||
@_data = null
|
|
||||||
@_fileName = null
|
|
||||||
@_items = []
|
|
||||||
@_location = null
|
|
||||||
@_modPack = null
|
|
||||||
|
|
||||||
_throwError: (message, cause=null)->
|
|
||||||
if @_location? then message = "#{@_location}: #{message}"
|
|
||||||
if @_fileName? and @_location? then message = "@#{message}"
|
|
||||||
if @_fileName? then message = "#{@_fileName}#{message}"
|
|
||||||
if cause? then message = "#{message}: #{cause}"
|
|
||||||
|
|
||||||
error = new Error message
|
|
||||||
error.cause = cause if cause?
|
|
||||||
error.fileName = @_fileName if @_fileName?
|
|
||||||
error.location = @_location if @_location?
|
|
||||||
|
|
||||||
throw error
|
|
||||||
@@ -5,10 +5,12 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
CraftingGuideCommon = require "crafting-guide-common"
|
||||||
Craftsman = require '../crafting/craftsman'
|
|
||||||
Inventory = require '../game/inventory'
|
{BaseModel} = CraftingGuideCommon.deprecated
|
||||||
ModPack = require '../game/mod_pack'
|
{Craftsman} = CraftingGuideCommon.deprecated.crafting
|
||||||
|
{Inventory} = CraftingGuideCommon.deprecated.game
|
||||||
|
{ModPack} = CraftingGuideCommon.deprecated.game
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
{BaseModel} = require("crafting-guide-common").deprecated
|
||||||
|
w = require "when"
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
w = require "when"
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
module.exports = class FileCache extends Backbone.Events
|
module.exports = class FileCache extends Backbone.Events
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
#
|
||||||
|
# Crafting Guide - file_cache.test.coffee
|
||||||
|
#
|
||||||
|
# Copyright © 2014-2016 by Redwood Labs
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
FileCache = require './file_cache'
|
FileCache = require './file_cache'
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
{BaseModel} = require("crafting-guide-common").deprecated
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
{BaseModel} = require("crafting-guide-common").deprecated
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
{BaseModel} = require('crafting-guide-common').deprecated
|
||||||
ItemSlug = require '../game/item_slug'
|
{ItemSlug} = require('crafting-guide-common').deprecated.game
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
{BaseModel} = require('crafting-guide-common').deprecated
|
||||||
MarkdownImage = require './markdown_image'
|
MarkdownImage = require './markdown_image'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - tutorial.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class Tutorial extends BaseModel
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
if not attributes.name?.length > 0 then throw new Error "attributes.name cannot be empty"
|
|
||||||
attributes.modSlug ?= null
|
|
||||||
attributes.officialUrl ?= null
|
|
||||||
attributes.sections ?= []
|
|
||||||
attributes.slug ?= _.slugify attributes.name
|
|
||||||
attributes.videos ?= []
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
# Backbone.Model Overrides #####################################################################
|
|
||||||
|
|
||||||
parse: (text)->
|
|
||||||
TutorialParser = require '../parsing/tutorial_parser' # to avoid require cycles
|
|
||||||
@_parser ?= new TutorialParser model:this
|
|
||||||
@_parser.parse text
|
|
||||||
|
|
||||||
return null # prevent calling `set`
|
|
||||||
|
|
||||||
url: ->
|
|
||||||
return c.url.tutorialData modSlug:@modSlug, tutorialSlug:@slug
|
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
ModPackJsonParser = require "../parsing/mod_pack_json_parser"
|
ModPackJsonParser = require "../parsing/mod_pack_json_parser"
|
||||||
|
w = require "when"
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
{CraftingGuideClient} = require('crafting-guide-common').api
|
||||||
ModBallotController = require './mod_ballot/mod_ballot_controller'
|
ModBallotController = require './mod_ballot/mod_ballot_controller'
|
||||||
ModTileController = require './mod_tile/mod_tile_controller'
|
ModTileController = require './mod_tile/mod_tile_controller'
|
||||||
PageController = require '../page_controller'
|
PageController = require '../page_controller'
|
||||||
{CraftingGuideClient} = require 'crafting-guide-common'
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ module.exports = class AdsenseController
|
|||||||
adCount = @_computeAdCount @_computeAdType()
|
adCount = @_computeAdCount @_computeAdType()
|
||||||
|
|
||||||
if adCount is 0
|
if adCount is 0
|
||||||
logger.verbose "Adsense is waiting for room to insert ads"
|
logger.trace "Adsense is waiting for room to insert ads"
|
||||||
@_waiting = true
|
@_waiting = true
|
||||||
_.delay (=> @_waiting = false; @_waitForPageReadiness()), c.adsense.readinessCheckInterval
|
_.delay (=> @_waiting = false; @_waitForPageReadiness()), c.adsense.readinessCheckInterval
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
BaseController = require '../../base_controller'
|
BaseController = require '../../base_controller'
|
||||||
ItemSelector = require '../../../models/site/item_selector'
|
ItemSelector = require '../../../models/site/item_selector'
|
||||||
ElementController = require './element/element_controller'
|
ElementController = require './element/element_controller'
|
||||||
|
w = require "when"
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
BaseController = require '../../base_controller'
|
BaseController = require '../../base_controller'
|
||||||
CraftingGridController = require '../crafting_grid/crafting_grid_controller'
|
CraftingGridController = require '../crafting_grid/crafting_grid_controller'
|
||||||
SlotController = require '../slot/slot_controller'
|
SlotController = require '../slot/slot_controller'
|
||||||
{StringBuilder} = require 'crafting-guide-common'
|
{StringBuilder} = require('crafting-guide-common').util
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
|
|
||||||
BaseController = require '../base_controller'
|
BaseController = require '../base_controller'
|
||||||
CraftPage = require '../../models/site/craft_page'
|
CraftPage = require '../../models/site/craft_page'
|
||||||
Craftsman = require '../../models/crafting/craftsman'
|
{Craftsman} = require('crafting-guide-common').deprecated.crafting
|
||||||
CraftsmanWorkingController = require './craftsman_working/craftsman_working_controller'
|
CraftsmanWorkingController = require './craftsman_working/craftsman_working_controller'
|
||||||
InventoryController = require '../common/inventory/inventory_controller'
|
InventoryController = require '../common/inventory/inventory_controller'
|
||||||
PageController = require '../page_controller'
|
PageController = require '../page_controller'
|
||||||
SimpleInventory = require '../../models/crafting/simple_inventory'
|
{SimpleInventory} = require('crafting-guide-common').deprecated.crafting
|
||||||
StepController = require './step/step_controller'
|
StepController = require './step/step_controller'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
@@ -64,6 +64,7 @@ module.exports = class CraftPageController extends PageController
|
|||||||
sampleInventory.parse inventoryText
|
sampleInventory.parse inventoryText
|
||||||
@model.craftsman.want.addInventory sampleInventory
|
@model.craftsman.want.addInventory sampleInventory
|
||||||
@_scrollTo @$workingSection
|
@_scrollTo @$workingSection
|
||||||
|
@onWantInventoryChanged()
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
BaseController = require '../../base_controller'
|
BaseController = require '../../base_controller'
|
||||||
Craftsman = require '../../../models/crafting/craftsman'
|
{Craftsman} = require('crafting-guide-common').deprecated.crafting
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
BaseController = require '../../base_controller'
|
BaseController = require '../../base_controller'
|
||||||
InventoryController = require '../../common/inventory/inventory_controller'
|
InventoryController = require '../../common/inventory/inventory_controller'
|
||||||
RecipeController = require '../../common/recipe/recipe_controller'
|
RecipeController = require '../../common/recipe/recipe_controller'
|
||||||
SimpleInventory = require '../../../models/crafting/simple_inventory'
|
{SimpleInventory} = require('crafting-guide-common').deprecated.crafting
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
BaseController = require '../base_controller'
|
BaseController = require '../base_controller'
|
||||||
EmailClient = require '../../models/site/email_client'
|
EmailClient = require '../../models/site/email_client'
|
||||||
|
w = require "when"
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -6,15 +6,16 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
EditableFile = require '../../models/site/editable_file'
|
EditableFile = require '../../models/site/editable_file'
|
||||||
Item = require '../../models/game/item'
|
{Item} = require('crafting-guide-common').deprecated.game
|
||||||
ItemGroupController = require '../common/item_group/item_group_controller'
|
ItemGroupController = require '../common/item_group/item_group_controller'
|
||||||
ItemPage = require '../../models/site/item_page'
|
ItemPage = require '../../models/site/item_page'
|
||||||
ItemSlug = require '../../models/game/item_slug'
|
{ItemSlug} = require('crafting-guide-common').deprecated.game
|
||||||
MarkdownSectionController = require '../common/markdown_section/markdown_section_controller'
|
MarkdownSectionController = require '../common/markdown_section/markdown_section_controller'
|
||||||
MultiblockViewerController = require './multiblock_viewer/multiblock_viewer_controller'
|
MultiblockViewerController = require './multiblock_viewer/multiblock_viewer_controller'
|
||||||
PageController = require '../page_controller'
|
PageController = require '../page_controller'
|
||||||
RecipeDetailController = require './recipe_detail/recipe_detail_controller'
|
RecipeDetailController = require './recipe_detail/recipe_detail_controller'
|
||||||
VideoController = require '../common/video/video_controller'
|
VideoController = require '../common/video/video_controller'
|
||||||
|
w = require "when"
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
BaseController = require '../../base_controller'
|
BaseController = require '../../base_controller'
|
||||||
CraftingGridController = require '../../common/crafting_grid/crafting_grid_controller'
|
CraftingGridController = require '../../common/crafting_grid/crafting_grid_controller'
|
||||||
Inventory = require '../../../models/game/inventory'
|
{Inventory} = require('crafting-guide-common').deprecated.game
|
||||||
InventoryController = require '../../common/inventory/inventory_controller'
|
InventoryController = require '../../common/inventory/inventory_controller'
|
||||||
{StringBuilder} = require 'crafting-guide-common'
|
{StringBuilder} = require('crafting-guide-common').util
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
PageController = require '../page_controller'
|
PageController = require '../page_controller'
|
||||||
GitHubUser = require '../../models/site/github_user'
|
GitHubUser = require '../../models/site/github_user'
|
||||||
{CraftingGuideClient} = require 'crafting-guide-common'
|
{CraftingGuideClient} = require('crafting-guide-common').api
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
PageController = require '../page_controller'
|
{Item} = require('crafting-guide-common').deprecated.game
|
||||||
Item = require '../../models/game/item'
|
|
||||||
ItemGroupController = require '../common/item_group/item_group_controller'
|
ItemGroupController = require '../common/item_group/item_group_controller'
|
||||||
Mod = require '../../models/game/mod'
|
{Mod} = require('crafting-guide-common').deprecated.game
|
||||||
|
PageController = require '../page_controller'
|
||||||
TutorialController = require './tutorial/tutorial_controller'
|
TutorialController = require './tutorial/tutorial_controller'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
BrowsePageController = require './browse_page/browse_page_controller'
|
BrowsePageController = require './browse_page/browse_page_controller'
|
||||||
CraftPageController = require './craft_page/craft_page_controller'
|
CraftPageController = require './craft_page/craft_page_controller'
|
||||||
ItemPageController = require './item_page/item_page_controller'
|
ItemPageController = require './item_page/item_page_controller'
|
||||||
ItemSlug = require '../models/game/item_slug'
|
{ItemSlug} = require('crafting-guide-common').deprecated.game
|
||||||
LoginPageController = require './login_page/login_page_controller'
|
LoginPageController = require './login_page/login_page_controller'
|
||||||
ModPageController = require './mod_page/mod_page_controller'
|
ModPageController = require './mod_page/mod_page_controller'
|
||||||
NewsPageController = require './news_page/news_page_controller'
|
NewsPageController = require './news_page/news_page_controller'
|
||||||
TutorialPageController = require './tutorial_page/tutorial_page_controller'
|
TutorialPageController = require './tutorial_page/tutorial_page_controller'
|
||||||
UrlParams = require './url_params'
|
UrlParams = require './url_params'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,8 @@ FooterController = require './footer/footer_controller'
|
|||||||
GitHubUser = require '../models/site/github_user'
|
GitHubUser = require '../models/site/github_user'
|
||||||
HeaderController = require './header/header_controller'
|
HeaderController = require './header/header_controller'
|
||||||
ImageLoader = require './image_loader'
|
ImageLoader = require './image_loader'
|
||||||
Mod = require '../models/game/mod'
|
{Mod} = require('crafting-guide-common').deprecated.game
|
||||||
ModPack = require '../models/game/mod_pack'
|
{ModPack} = require('crafting-guide-common').deprecated.game
|
||||||
ModPackStore = require '../models/store/mod_pack_store'
|
|
||||||
Router = require './router'
|
Router = require './router'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
@@ -31,6 +30,7 @@ module.exports = class SiteController extends BaseController
|
|||||||
@client = options.client
|
@client = options.client
|
||||||
@fileCache = new FileCache c.url.modpackArchive()
|
@fileCache = new FileCache c.url.modpackArchive()
|
||||||
@imageLoader = new ImageLoader defaultUrl:'/images/unknown.png'
|
@imageLoader = new ImageLoader defaultUrl:'/images/unknown.png'
|
||||||
|
@modPack = new ModPack {}, fileCache:@fileCache
|
||||||
@router = new Router this
|
@router = new Router this
|
||||||
@storage = options.storage
|
@storage = options.storage
|
||||||
|
|
||||||
@@ -41,6 +41,23 @@ module.exports = class SiteController extends BaseController
|
|||||||
|
|
||||||
# Public Methods ###############################################################################
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
|
loadDefaultModPack: ->
|
||||||
|
makeResponder = (m)-> return ->
|
||||||
|
m.activeModVersion.fetch() if m.activeModVersion?
|
||||||
|
|
||||||
|
for modSlug, modData of c.defaultMods
|
||||||
|
mod = new Mod {slug:modSlug}, {fileCache:@fileCache}
|
||||||
|
mod.on c.event.change + ':activeModVersion', makeResponder mod
|
||||||
|
@storage.register "mod:#{mod.slug}", mod, 'activeVersion', modData.defaultVersion
|
||||||
|
mod.fetch()
|
||||||
|
|
||||||
|
@modPack.addMod mod
|
||||||
|
|
||||||
|
if global.env isnt 'prerender'
|
||||||
|
@modPack.once c.event.sync, =>
|
||||||
|
@$pageContent.removeClass 'hidden'
|
||||||
|
@$pageContentLoading.addClass 'hidden'
|
||||||
|
|
||||||
loadCurrentUser: ->
|
loadCurrentUser: ->
|
||||||
@client.getCurrentUser()
|
@client.getCurrentUser()
|
||||||
.then (response)=>
|
.then (response)=>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
w = require "when"
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
module.exports = class Tracker
|
module.exports = class Tracker
|
||||||
|
|||||||
+43
-48
@@ -5,6 +5,9 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
_ = require "./underscore"
|
||||||
|
_.extend exports, require("crafting-guide-common").constants
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
exports.adsense = adsense = {}
|
exports.adsense = adsense = {}
|
||||||
@@ -29,31 +32,52 @@ adsense.skyscraper.margin = 24 # px
|
|||||||
adsense.skyscraper.slotIds = ['7613920409', '9574673605', '3388539204']
|
adsense.skyscraper.slotIds = ['7613920409', '9574673605', '3388539204']
|
||||||
adsense.skyscraper.width = 160 # px
|
adsense.skyscraper.width = 160 # px
|
||||||
|
|
||||||
|
exports.defaultMods = defaultMods = {}
|
||||||
|
defaultMods.minecraft = { defaultVersion: '1.7.10' } # Minecraft must be first
|
||||||
|
|
||||||
|
defaultMods.advanced_solar_panels = { defaultVersion: '3.5.1' }
|
||||||
|
defaultMods.agricraft = { defaultVersion: '1.7.10_1.5.0' }
|
||||||
|
defaultMods.applied_energistics_2 = { defaultVersion: 'rv1-stable-1' }
|
||||||
|
defaultMods.big_reactors = { defaultVersion: '0.4.2A2' }
|
||||||
|
defaultMods.buildcraft = { defaultVersion: '1.7.18' }
|
||||||
|
defaultMods.computercraft = { defaultVersion: '1.74' }
|
||||||
|
defaultMods.draconic_evolution = { defaultVersion: '1.0.2h' }
|
||||||
|
defaultMods.ender_storage = { defaultVersion: '1.4.5.29' }
|
||||||
|
defaultMods.enderio = { defaultVersion: '2.2.7.325' }
|
||||||
|
defaultMods.extra_cells = { defaultVersion: '2.2.73b129' }
|
||||||
|
defaultMods.extra_utilities = { defaultVersion: '1.2.2' }
|
||||||
|
defaultMods.forestry = { defaultVersion: '3.4.0.7' }
|
||||||
|
defaultMods.forge_multipart = { defaultVersion: '1.2.0.345' }
|
||||||
|
defaultMods.galacticraft = { defaultVersion: '3.0.12.404' }
|
||||||
|
defaultMods.hydraulicraft = { defaultVersion: '2.1.242' }
|
||||||
|
defaultMods.ic2_classic = { defaultVersion: 'none' }
|
||||||
|
defaultMods.industrial_craft_2 = { defaultVersion: '2.2.663' }
|
||||||
|
defaultMods.iron_chests = { defaultVersion: '6.0.62.742' }
|
||||||
|
defaultMods.jabba = { defaultVersion: '1.2.1a' }
|
||||||
|
defaultMods.logistics_pipes = { defaultVersion: '0.9.3.100' }
|
||||||
|
defaultMods.mekanism = { defaultVersion: '7.1.1.127' }
|
||||||
|
defaultMods.minefactory_reloaded = { defaultVersion: '2.8.0RC8-86' }
|
||||||
|
defaultMods.modular_powersuits = { defaultVersion: '0.11.0-300-thermal-expansion' }
|
||||||
|
defaultMods.opencomputers = { defaultVersion: '1.5.22' }
|
||||||
|
defaultMods.quantum_flux = { defaultVersion: '1.3.4' }
|
||||||
|
defaultMods.project_red = { defaultVersion: '4.5.16.77' }
|
||||||
|
defaultMods.redstone_arsenal = { defaultVersion: '9.5.0' }
|
||||||
|
defaultMods.railcraft = { defaultVersion: '9.5.0' }
|
||||||
|
defaultMods.simply_jetpacks = { defaultVersion: '1.4.1' }
|
||||||
|
defaultMods.solar_expansion = { defaultVersion: '1.6a' }
|
||||||
|
defaultMods.solar_flux = { defaultVersion: '0.5b' }
|
||||||
|
defaultMods.storage_drawers = { defaultVersion: '1.7.10-1.6.2' }
|
||||||
|
defaultMods.thermal_dynamics = { defaultVersion: '1.7.10r1.2.0' }
|
||||||
|
defaultMods.thermal_expansion = { defaultVersion: '1.7.10r4.1.4' }
|
||||||
|
defaultMods.thermal_foundation = { defaultVersion: '1.7.10r1.2.5' }
|
||||||
|
defaultMods.tinkers_construct = { defaultVersion: '1.7.10-1.8.8' }
|
||||||
|
|
||||||
exports.duration = duration = {}
|
exports.duration = duration = {}
|
||||||
duration.snap = 100
|
duration.snap = 100
|
||||||
duration.fast = 200
|
duration.fast = 200
|
||||||
duration.normal = 400
|
duration.normal = 400
|
||||||
duration.slow = 1200
|
duration.slow = 1200
|
||||||
|
|
||||||
exports.event = event = {}
|
|
||||||
event.add = 'add' # collection, item...
|
|
||||||
event.button = {}
|
|
||||||
event.button.complete = 'button:complete' # controller
|
|
||||||
event.button.first = 'button:first' # controller, buttonType
|
|
||||||
event.button.second = 'button:second' # controller, buttonType
|
|
||||||
event.change = 'change' # model
|
|
||||||
event.click = 'click' # event
|
|
||||||
event.load = {}
|
|
||||||
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...
|
|
||||||
event.request = 'request' # model
|
|
||||||
event.route = 'route'
|
|
||||||
event.sort = 'sort'
|
|
||||||
event.sync = 'sync' # model, response
|
|
||||||
|
|
||||||
exports.gitHub = gitHub = {}
|
exports.gitHub = gitHub = {}
|
||||||
gitHub.file = {}
|
gitHub.file = {}
|
||||||
gitHub.file.itemDescription = {}
|
gitHub.file.itemDescription = {}
|
||||||
@@ -67,10 +91,6 @@ key.escape = 27
|
|||||||
key.upArrow = 38
|
key.upArrow = 38
|
||||||
key.downArrow = 40
|
key.downArrow = 40
|
||||||
|
|
||||||
exports.limits = limits = {}
|
|
||||||
limits.maximumGraphSize = 5000
|
|
||||||
limits.maximumPlanCount = 5000
|
|
||||||
|
|
||||||
exports.login = login = {}
|
exports.login = login = {}
|
||||||
login.authorizeUrl = _.template "https://github.com/login/oauth/authorize" +
|
login.authorizeUrl = _.template "https://github.com/login/oauth/authorize" +
|
||||||
"?client_id=<%= clientId %>&scope=public_repo&state=<%= state %>"
|
"?client_id=<%= clientId %>&scope=public_repo&state=<%= state %>"
|
||||||
@@ -82,12 +102,6 @@ login.clientIds =
|
|||||||
exports.modpack = modpack = {}
|
exports.modpack = modpack = {}
|
||||||
modpack.default = "crafting-guide-default"
|
modpack.default = "crafting-guide-default"
|
||||||
|
|
||||||
exports.modelState = modelState = {}
|
|
||||||
modelState.unloaded = 'unloaded'
|
|
||||||
modelState.loading = 'loading'
|
|
||||||
modelState.loaded = 'loaded'
|
|
||||||
modelState.failed = 'failed'
|
|
||||||
|
|
||||||
exports.opacity = opacity = {}
|
exports.opacity = opacity = {}
|
||||||
opacity.hidden = 1e-6
|
opacity.hidden = 1e-6
|
||||||
opacity.shown = 1
|
opacity.shown = 1
|
||||||
@@ -138,22 +152,3 @@ tracking.category.modVote = 'mod-vote'
|
|||||||
tracking.category.multiblock = 'multiblock'
|
tracking.category.multiblock = 'multiblock'
|
||||||
tracking.category.navigate = 'navigate'
|
tracking.category.navigate = 'navigate'
|
||||||
tracking.category.search = 'search'
|
tracking.category.search = 'search'
|
||||||
|
|
||||||
exports.url = url = {}
|
|
||||||
url.crafting = _.template "/craft/<%= inventoryText %>"
|
|
||||||
url.item = _.template "/browse/<%= modSlug %>/<%= itemSlug %>/"
|
|
||||||
url.itemData = _.template "/data/<%= modSlug %>/items/<%= itemSlug %>/item.cg"
|
|
||||||
url.itemIcon = _.template "/data/<%= modSlug %>/items/<%= itemSlug %>/icon.png"
|
|
||||||
url.itemImageDir = _.template "/data/<%= modSlug %>/items/<%= itemSlug %>"
|
|
||||||
url.login = _.template "/login"
|
|
||||||
url.mod = _.template "/browse/<%= modSlug %>/"
|
|
||||||
url.modData = _.template "/data/<%= modSlug %>/mod.cg"
|
|
||||||
url.modIcon = _.template "/data/<%= modSlug %>/icon.png"
|
|
||||||
url.modPackData = _.template "/data/<%= modPackId %>/modpack.json"
|
|
||||||
url.modVersionData = _.template "/data/<%= modSlug %>/versions/<%= modVersion %>/mod-version.cg"
|
|
||||||
url.root = _.template "/"
|
|
||||||
url.tutorial = _.template "/browse/<%= modSlug %>/tutorials/<%= tutorialSlug %>/"
|
|
||||||
url.tutorialData = _.template "/data/<%= modSlug %>/tutorials/<%= tutorialSlug %>/tutorial.cg"
|
|
||||||
url.tutorialIcon = _.template "/data/<%= modSlug %>/tutorials/<%= tutorialSlug %>/icon.png"
|
|
||||||
url.tutorialIcon = _.template "/data/<%= modSlug %>/tutorials/<%= tutorialSlug %>/icon.png"
|
|
||||||
url.tutorialImageDir = _.template "/data/<%= modSlug %>/tutorials/<%= tutorialSlug %>"
|
|
||||||
|
|||||||
@@ -5,33 +5,4 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
module.exports = _ = require 'underscore'
|
module.exports = _ = require('crafting-guide-common')._
|
||||||
|
|
||||||
_.mixin require 'underscore.inflections'
|
|
||||||
_.mixin require('crafting-guide-common').stringMixins
|
|
||||||
|
|
||||||
_.mixin
|
|
||||||
parseMarkdown: (text)->
|
|
||||||
return markdown.parse text, 'Maruku'
|
|
||||||
|
|
||||||
slugify: (text)->
|
|
||||||
return null unless text?
|
|
||||||
|
|
||||||
result = text.toLowerCase()
|
|
||||||
result = result.replace /[^a-zA-Z0-9_]/g, '_'
|
|
||||||
result = result.replace /__+/g, '_'
|
|
||||||
result = result.replace /^_/, ''
|
|
||||||
result = result.replace /_$/, ''
|
|
||||||
return result
|
|
||||||
|
|
||||||
composeSlugs: (part1, part2)->
|
|
||||||
return "#{part1}__#{part2}"
|
|
||||||
|
|
||||||
decomposeSlug: (slug)->
|
|
||||||
return [null, null] unless slug?
|
|
||||||
|
|
||||||
parts = slug.split '__'
|
|
||||||
if parts.length is 1
|
|
||||||
parts = [ null, parts[0] ]
|
|
||||||
|
|
||||||
return parts
|
|
||||||
|
|||||||
+1
-10
@@ -7,13 +7,4 @@
|
|||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
global.Backbone = require 'backbone'
|
# Just to keep NPM happy... nothing is actually exported.
|
||||||
global._ = require './common/underscore'
|
|
||||||
global.c = require './common/constants'
|
|
||||||
|
|
||||||
module.exports =
|
|
||||||
constants: require './common/constants'
|
|
||||||
Mod: require './client/models/game/mod'
|
|
||||||
ModParser: require './client/models/parsing/mod_parser'
|
|
||||||
ModVersion: require './client/models/game/mod_version'
|
|
||||||
ModVersionParser: require './client/models/parsing/mod_version_parser'
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ express = require 'express'
|
|||||||
http = require 'http'
|
http = require 'http'
|
||||||
middleware = require './middleware'
|
middleware = require './middleware'
|
||||||
routes = require './routes'
|
routes = require './routes'
|
||||||
|
w = require "when"
|
||||||
|
|
||||||
############################################################################################################
|
############################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,6 @@ require('dotenv').config()
|
|||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
global._ = require './underscore'
|
|
||||||
global.c = require './constants'
|
|
||||||
global.w = require 'when'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
CraftingGuideServer = require './crafting_guide_server'
|
CraftingGuideServer = require './crafting_guide_server'
|
||||||
server = new CraftingGuideServer process.env.WEBSITE_SERVER_PORT
|
server = new CraftingGuideServer process.env.WEBSITE_SERVER_PORT
|
||||||
server.start()
|
server.start()
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
###
|
#
|
||||||
# Crafting Guide - test.coffee
|
# Crafting Guide - test.coffee
|
||||||
#
|
#
|
||||||
# Copyright (c) 2014-2015 by Redwood Labs
|
# Copyright (c) 2014-2015 by Redwood Labs
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
###
|
#
|
||||||
|
|
||||||
# Test Set-up ##########################################################################################################
|
# Test Set-up ##########################################################################################################
|
||||||
|
|
||||||
@@ -27,5 +27,5 @@ global.π = Math.PI
|
|||||||
global.Backbone = Backbone = require 'backbone'
|
global.Backbone = Backbone = require 'backbone'
|
||||||
Backbone.$ = $
|
Backbone.$ = $
|
||||||
|
|
||||||
{Logger} = require 'crafting-guide-common'
|
{Logger} = require('crafting-guide-common').util
|
||||||
global.logger = new Logger level:Logger.FATAL
|
global.logger = new Logger level:Logger.FATAL
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#
|
||||||
|
# Crafting Guide - underscore.coffee
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014-2017 by Redwood Labs
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
########################################################################################################################
|
||||||
|
|
||||||
|
module.exports = _ = require("crafting-guide-common")._
|
||||||
Reference in New Issue
Block a user