wip
This commit is contained in:
+1
-1
@@ -130,7 +130,7 @@ module.exports = (grunt)->
|
|||||||
|
|
||||||
mochaTest:
|
mochaTest:
|
||||||
options:
|
options:
|
||||||
bail: true
|
bail: false
|
||||||
color: true
|
color: true
|
||||||
reporter: 'list'
|
reporter: 'list'
|
||||||
require: [
|
require: [
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#
|
||||||
|
# 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}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#
|
||||||
|
# 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()
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#
|
||||||
|
# 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}"
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
Inventory = require "./inventory"
|
Inventory = require "../game/inventory"
|
||||||
{StringBuilder} = require "crafting-guide-common"
|
{StringBuilder} = require "crafting-guide-common"
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
@@ -20,8 +20,8 @@ module.exports = class CraftingPlan
|
|||||||
@steps = attributes.steps
|
@steps = attributes.steps
|
||||||
@want = attributes.want
|
@want = attributes.want
|
||||||
|
|
||||||
@_consolidateSteps()
|
|
||||||
@_computeResources()
|
@_computeResources()
|
||||||
|
@_consolidateSteps()
|
||||||
|
|
||||||
# Properties ###################################################################################
|
# Properties ###################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
CraftingPlan = require './crafting_plan'
|
CraftingPlan = require './crafting_plan'
|
||||||
CraftingPlanStep = require './crafting_plan_step'
|
CraftingPlanStep = require './crafting_plan_step'
|
||||||
Inventory = require './inventory'
|
Inventory = require '../game/inventory'
|
||||||
fixtures = require './fixtures'
|
fixtures = require '../fixtures'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ module.exports = class Evaluation
|
|||||||
@recipe = attributes.recipe if attributes.recipe?
|
@recipe = attributes.recipe if attributes.recipe?
|
||||||
@baseScore = attributes.baseScore
|
@baseScore = attributes.baseScore
|
||||||
|
|
||||||
@_baseEvaluations = []
|
@_id = _.uniqueId "evaluation-"
|
||||||
@_includedTools = {}
|
@_includedTools = {}
|
||||||
@_toolScore = null
|
@_toolScore = null
|
||||||
|
|
||||||
@@ -23,10 +23,6 @@ module.exports = class Evaluation
|
|||||||
|
|
||||||
Object.defineProperties @prototype,
|
Object.defineProperties @prototype,
|
||||||
|
|
||||||
baseEvaluations:
|
|
||||||
get: -> return @_baseEvaluations
|
|
||||||
set: -> throw new Error "baseEvaluations cannot be assigned"
|
|
||||||
|
|
||||||
baseScore:
|
baseScore:
|
||||||
get: -> return @_baseScore
|
get: -> return @_baseScore
|
||||||
set: (baseScore)->
|
set: (baseScore)->
|
||||||
@@ -70,13 +66,15 @@ module.exports = class Evaluation
|
|||||||
|
|
||||||
# Public Methods ###############################################################################
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
addBaseEvaluation: (evaluation)->
|
|
||||||
@_baseEvaluations.push evaluation
|
|
||||||
|
|
||||||
addIncludedTool: (item)->
|
addIncludedTool: (item)->
|
||||||
|
return if @_includedTools[item.id]?
|
||||||
@_includedTools[item.id] = item
|
@_includedTools[item.id] = item
|
||||||
@_toolScore = null
|
@_toolScore = null
|
||||||
|
|
||||||
|
addIncludedToolsFrom: (evaluation)->
|
||||||
|
for id, toolItem of evaluation.includedTools
|
||||||
|
@addIncludedTool toolItem
|
||||||
|
|
||||||
computeTotalScore: (quantity=1)->
|
computeTotalScore: (quantity=1)->
|
||||||
if @item?
|
if @item?
|
||||||
multiplier = quantity
|
multiplier = quantity
|
||||||
@@ -86,18 +84,13 @@ module.exports = class Evaluation
|
|||||||
return @baseScore * multiplier + @toolScore
|
return @baseScore * multiplier + @toolScore
|
||||||
|
|
||||||
isToolIncluded: (item)->
|
isToolIncluded: (item)->
|
||||||
return true if @_includedTools[item.id]?
|
return @_includedTools[item.id]?
|
||||||
|
|
||||||
for baseEvaluation in @_baseEvaluations
|
|
||||||
return true if baseEvaluation.isToolIncluded item
|
|
||||||
|
|
||||||
return false
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
# Object Overrides #############################################################################
|
||||||
|
|
||||||
toString: ->
|
toString: ->
|
||||||
obj = if @item? then @item else @recipe
|
obj = if @item? then @item else @recipe
|
||||||
return "#{@evaluator}=>#{obj}@#{@baseScore}"
|
return "#{@evaluator.constructor.name}:#{obj}@#{@baseScore}<#{@_id}>"
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
# Private Methods ##############################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ module.exports = class Evaluator
|
|||||||
|
|
||||||
recipeEvaluation = @_findBestRecipeEvaluationFor item
|
recipeEvaluation = @_findBestRecipeEvaluationFor item
|
||||||
if recipeEvaluation?
|
if recipeEvaluation?
|
||||||
evaluation.addBaseEvaluation recipeEvaluation
|
|
||||||
evaluation.baseScore = recipeEvaluation.baseScore
|
evaluation.baseScore = recipeEvaluation.baseScore
|
||||||
|
evaluation.addIncludedToolsFrom recipeEvaluation
|
||||||
else
|
else
|
||||||
@_computeGatherableItemScore item, evaluation
|
@_computeGatherableItemScore item, evaluation
|
||||||
|
|
||||||
@@ -49,12 +49,13 @@ module.exports = class Evaluator
|
|||||||
evaluation = @_evaluations[recipe.id] = new Evaluation evaluator:this, recipe:recipe
|
evaluation = @_evaluations[recipe.id] = new Evaluation evaluator:this, recipe:recipe
|
||||||
@_computeRecipeScore recipe, evaluation
|
@_computeRecipeScore recipe, evaluation
|
||||||
|
|
||||||
|
for id, item of recipe.inputs
|
||||||
|
inputEvaluation = @evaluateItem item
|
||||||
|
evaluation.addIncludedToolsFrom inputEvaluation
|
||||||
|
|
||||||
for id, toolItem of recipe.tools
|
for id, toolItem of recipe.tools
|
||||||
evaluation.addIncludedTool toolItem
|
evaluation.addIncludedTool toolItem
|
||||||
|
evaluation.addIncludedToolsFrom @evaluateItem toolItem
|
||||||
for id, toolItem of @evaluateItem(toolItem).includedTools
|
|
||||||
evaluation.addIncludedTool toolItem
|
|
||||||
|
|
||||||
|
|
||||||
return evaluation
|
return evaluation
|
||||||
|
|
||||||
@@ -79,20 +80,19 @@ module.exports = class Evaluator
|
|||||||
|
|
||||||
# Overrideable Methods #########################################################################
|
# Overrideable Methods #########################################################################
|
||||||
|
|
||||||
_computeRecipeScore: (recipe, evaluation)->
|
|
||||||
throw new Error "#{@constructor.name} must override _computeRecipeScore"
|
|
||||||
|
|
||||||
_computeGatherableItemScore: (item, evaluation)->
|
_computeGatherableItemScore: (item, evaluation)->
|
||||||
throw new Error "#{@constructor.name} must override _computeGatherableItemScore"
|
throw new Error "#{@constructor.name} must override _computeGatherableItemScore"
|
||||||
|
|
||||||
|
_computeRecipeScore: (recipe, evaluation)->
|
||||||
|
throw new Error "#{@constructor.name} must override _computeRecipeScore"
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
# Private Methods ##############################################################################
|
||||||
|
|
||||||
_findBestRecipeEvaluationFor: (item)->
|
_findBestRecipeEvaluationFor: (item)->
|
||||||
return null if item.isGatherable
|
return null if item.isGatherable
|
||||||
result = null
|
result = null
|
||||||
|
|
||||||
for recipeMap in [item.recipes, item.recipesAsExtra]
|
for id, recipe of item.recipes
|
||||||
for id, recipe of recipeMap
|
|
||||||
evaluation = @evaluateRecipe recipe
|
evaluation = @evaluateRecipe recipe
|
||||||
continue unless evaluation?.baseScore?
|
continue unless evaluation?.baseScore?
|
||||||
|
|
||||||
|
|||||||
@@ -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,93 +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:
|
|
||||||
get: -> return @_displayName
|
|
||||||
set: (displayName)->
|
|
||||||
if not displayName? then throw new Error "displayName is required"
|
|
||||||
@_displayName = displayName
|
|
||||||
|
|
||||||
firstRecipe:
|
|
||||||
get: -> return recipe for id, recipe of @recipes
|
|
||||||
set: -> throw new Error "firstRecipe cannot be assigned"
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
isGatherable:
|
|
||||||
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:
|
|
||||||
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:
|
|
||||||
get: -> return @_mod.modPack
|
|
||||||
set: -> throw new Error "modPack cannot be replaced"
|
|
||||||
|
|
||||||
recipes:
|
|
||||||
get: -> return if @_hasPrimaryRecipe then @_recipesAsPrimary else @_recipesAsExtra
|
|
||||||
set: -> throw new Error "recipes cannot be assigned"
|
|
||||||
|
|
||||||
recipesAsPrimary:
|
|
||||||
get: -> return @_recipesAsPrimary
|
|
||||||
set: -> throw new Error "recipes cannot be assigned"
|
|
||||||
|
|
||||||
recipesAsExtra:
|
|
||||||
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] 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,52 +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 = {}
|
|
||||||
@_oreDict = {}
|
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
|
|
||||||
displayName:
|
|
||||||
get: -> return @_displayName
|
|
||||||
set: (displayName)->
|
|
||||||
if not displayName? then throw new Error "displayName is required"
|
|
||||||
if @_displayName is displayName then return
|
|
||||||
@_displayName = displayName
|
|
||||||
|
|
||||||
id:
|
|
||||||
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"
|
|
||||||
|
|
||||||
mods:
|
|
||||||
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
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "ModPack:#{@displayName}<#{@id}>"
|
|
||||||
@@ -5,14 +5,14 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
fixtures = require './fixtures'
|
fixtures = require '../fixtures'
|
||||||
Inventory = require './inventory'
|
Inventory = require '../game/inventory'
|
||||||
PlanBuilder = require './plan_builder'
|
PlanBuilder = require './plan_builder'
|
||||||
ResourcesEvaluator = require './resources_evaluator'
|
ResourcesEvaluator = require './resources_evaluator'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
describe.only "PlanBuilder", ->
|
describe "PlanBuilder", ->
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@planner = new PlanBuilder new ResourcesEvaluator
|
@planner = new PlanBuilder new ResourcesEvaluator
|
||||||
|
|||||||
@@ -1,148 +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={})->
|
|
||||||
@id = attributes.id
|
|
||||||
@height = attributes.height
|
|
||||||
@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"
|
|
||||||
|
|
||||||
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
|
|
||||||
@_extras[stack.item.id] = stack
|
|
||||||
|
|
||||||
addTool: (item)->
|
|
||||||
return unless item
|
|
||||||
@_tools[item.id] = item
|
|
||||||
|
|
||||||
computeQuantityRequired: (item)->
|
|
||||||
result = 0
|
|
||||||
|
|
||||||
for row in [0...@height]
|
|
||||||
for col in [0...@width]
|
|
||||||
stack = @_inputGrid[row]?[col]
|
|
||||||
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: (row, col)->
|
|
||||||
return @_inputGrid[row]?[col]
|
|
||||||
|
|
||||||
setInputAt: (row, col, stack)->
|
|
||||||
@_height = Math.max @_height, row + 1
|
|
||||||
@_width = Math.max @_width, col + 1
|
|
||||||
|
|
||||||
@_inputGrid[row] ?= []
|
|
||||||
@_inputGrid[row][col] = stack
|
|
||||||
@_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}>"
|
|
||||||
@@ -16,18 +16,17 @@ module.exports = class ResourcesEvaluator extends Evaluator
|
|||||||
_computeRecipeScore: (recipe, evaluation)->
|
_computeRecipeScore: (recipe, evaluation)->
|
||||||
evaluation.baseScore = 0
|
evaluation.baseScore = 0
|
||||||
|
|
||||||
for row in [0...recipe.height]
|
for x in [0...recipe.width]
|
||||||
for col in [0...recipe.width]
|
for y in [0...recipe.height]
|
||||||
stack = recipe.getInputAt row, col
|
for z in [0...recipe.depth]
|
||||||
|
stack = recipe.getInputAt x, y, z
|
||||||
continue unless stack?
|
continue unless stack?
|
||||||
|
|
||||||
inputEvaluation = @evaluateItem stack.item
|
inputEvaluation = @evaluateItem stack.item
|
||||||
if inputEvaluation?.baseScore?
|
if inputEvaluation?.baseScore?
|
||||||
evaluation.baseScore += inputEvaluation.baseScore * stack.quantity
|
evaluation.baseScore += inputEvaluation.baseScore * stack.quantity
|
||||||
evaluation.addBaseEvaluation inputEvaluation
|
|
||||||
else
|
else
|
||||||
evaluation.baseScore = null
|
evaluation.baseScore = null
|
||||||
logger.outdent()
|
|
||||||
return
|
return
|
||||||
|
|
||||||
for id, extraStack of recipe.extras
|
for id, extraStack of recipe.extras
|
||||||
@@ -35,10 +34,6 @@ module.exports = class ResourcesEvaluator extends Evaluator
|
|||||||
continue unless extraEvaluation?.baseScore?
|
continue unless extraEvaluation?.baseScore?
|
||||||
evaluation.baseScore -= extraStack.quantity * extraEvaluation.baseScore
|
evaluation.baseScore -= extraStack.quantity * extraEvaluation.baseScore
|
||||||
|
|
||||||
for id, toolItem of recipe.tools
|
|
||||||
toolEvaluation = @evaluateItem toolItem
|
|
||||||
evaluation.addBaseEvaluation toolEvaluation
|
|
||||||
|
|
||||||
evaluation.baseScore = evaluation.baseScore / recipe.output.quantity
|
evaluation.baseScore = evaluation.baseScore / recipe.output.quantity
|
||||||
|
|
||||||
_computeGatherableItemScore: (item, evaluation)->
|
_computeGatherableItemScore: (item, evaluation)->
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
ResourcesEvaluator = require './resources_evaluator'
|
ResourcesEvaluator = require './resources_evaluator'
|
||||||
fixtures = require './fixtures'
|
fixtures = require '../fixtures'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
@@ -123,3 +123,7 @@ describe "ResourcesEvaluator", ->
|
|||||||
# 1 oak wood ==> 4 oak planks (0.25)
|
# 1 oak wood ==> 4 oak planks (0.25)
|
||||||
# 4 planks ==> 1 crafting table (1)
|
# 4 planks ==> 1 crafting table (1)
|
||||||
@evaluation.computeTotalScore().should.equal 18
|
@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,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}"
|
|
||||||
@@ -22,7 +22,6 @@ module.exports = class StepsEvaluator extends Evaluator
|
|||||||
evaluation.score = null
|
evaluation.score = null
|
||||||
return
|
return
|
||||||
|
|
||||||
evaluation.addBaseEvaluation inputEvaluation
|
|
||||||
evaluation.score = Math.min evaluation.score, inputEvaluation.score + 1
|
evaluation.score = Math.min evaluation.score, inputEvaluation.score + 1
|
||||||
|
|
||||||
for id, item of recipe.tools
|
for id, item of recipe.tools
|
||||||
@@ -33,7 +32,6 @@ module.exports = class StepsEvaluator extends Evaluator
|
|||||||
evaluation.score = null
|
evaluation.score = null
|
||||||
return
|
return
|
||||||
|
|
||||||
evaluation.addBaseEvaluation evaluation
|
|
||||||
evaluation.addIncludedTool item
|
evaluation.addIncludedTool item
|
||||||
evaluation.score += toolEvaluation.score
|
evaluation.score += toolEvaluation.score
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
Item = require "./item"
|
Item = require "./game/item"
|
||||||
Mod = require "./mod"
|
Mod = require "./game/mod"
|
||||||
ModPack = require "./mod_pack"
|
ModPack = require "./game/mod_pack"
|
||||||
Recipe = require "./recipe"
|
Recipe = require "./game/recipe"
|
||||||
Stack = require "./stack"
|
Stack = require "./game/stack"
|
||||||
|
|
||||||
# Instance Creation Fixtures ###########################################################################################
|
# Instance Creation Fixtures ###########################################################################################
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ exports.createStack = createStack = (attributes={})->
|
|||||||
# Item Configuration Fixtures ##########################################################################################
|
# Item Configuration Fixtures ##########################################################################################
|
||||||
|
|
||||||
exports.configureBucket = configureBucket = (mod)->
|
exports.configureBucket = configureBucket = (mod)->
|
||||||
bucket = mod.items["bucket"]
|
bucket = mod.modPack.findItem "bucket"
|
||||||
if not bucket?
|
if not bucket?
|
||||||
ironIngot = configureIronIngot mod
|
ironIngot = configureIronIngot mod
|
||||||
bucket = createItem mod:mod, id:"bucket", displayName:"Bucket"
|
bucket = createItem mod:mod, id:"bucket", displayName:"Bucket"
|
||||||
@@ -52,13 +52,13 @@ exports.configureBucket = configureBucket = (mod)->
|
|||||||
recipe = createRecipe output:createStack item:bucket
|
recipe = createRecipe output:createStack item:bucket
|
||||||
recipe.setInputAt 0, 0, createStack item:ironIngot
|
recipe.setInputAt 0, 0, createStack item:ironIngot
|
||||||
recipe.setInputAt 1, 1, createStack item:ironIngot
|
recipe.setInputAt 1, 1, createStack item:ironIngot
|
||||||
recipe.setInputAt 0, 2, createStack item:ironIngot
|
recipe.setInputAt 2, 0, createStack item:ironIngot
|
||||||
recipe.addTool craftingTable
|
recipe.addTool craftingTable
|
||||||
|
|
||||||
return bucket
|
return bucket
|
||||||
|
|
||||||
exports.configureCake = configureCake = (mod)->
|
exports.configureCake = configureCake = (mod)->
|
||||||
cake = mod.items["cake"]
|
cake = mod.modPack.findItem "cake"
|
||||||
if not cake?
|
if not cake?
|
||||||
bucket = configureBucket mod
|
bucket = configureBucket mod
|
||||||
cake = createItem mod:mod, id:"cake", displayName:"Cake"
|
cake = createItem mod:mod, id:"cake", displayName:"Cake"
|
||||||
@@ -70,13 +70,13 @@ exports.configureCake = configureCake = (mod)->
|
|||||||
|
|
||||||
recipe = createRecipe output:createStack item:cake
|
recipe = createRecipe output:createStack item:cake
|
||||||
recipe.setInputAt 0, 0, createStack item:milkBucket
|
recipe.setInputAt 0, 0, createStack item:milkBucket
|
||||||
recipe.setInputAt 0, 1, createStack item:milkBucket
|
recipe.setInputAt 1, 0, createStack item:milkBucket
|
||||||
recipe.setInputAt 0, 2, createStack item:milkBucket
|
recipe.setInputAt 2, 0, createStack item:milkBucket
|
||||||
recipe.setInputAt 1, 0, createStack item:sugar
|
recipe.setInputAt 0, 1, createStack item:sugar
|
||||||
recipe.setInputAt 1, 1, createStack item:egg
|
recipe.setInputAt 1, 1, createStack item:egg
|
||||||
recipe.setInputAt 1, 2, createStack item:sugar
|
recipe.setInputAt 2, 1, createStack item:sugar
|
||||||
recipe.setInputAt 2, 0, createStack item:wheat
|
recipe.setInputAt 0, 2, createStack item:wheat
|
||||||
recipe.setInputAt 2, 1, createStack item:wheat
|
recipe.setInputAt 1, 2, createStack item:wheat
|
||||||
recipe.setInputAt 2, 2, createStack item:wheat
|
recipe.setInputAt 2, 2, createStack item:wheat
|
||||||
recipe.addTool craftingTable
|
recipe.addTool craftingTable
|
||||||
recipe.addExtra createStack item:bucket, quantity:3
|
recipe.addExtra createStack item:bucket, quantity:3
|
||||||
@@ -84,39 +84,39 @@ exports.configureCake = configureCake = (mod)->
|
|||||||
return cake
|
return cake
|
||||||
|
|
||||||
exports.configureCoal = configureCoal = (mod)->
|
exports.configureCoal = configureCoal = (mod)->
|
||||||
coal = mod.items["coal"]
|
coal = mod.modPack.findItem "coal"
|
||||||
if not coal?
|
if not coal?
|
||||||
coal = createItem mod:mod, id:"coal", displayName:"Coal", isGatherable:true
|
coal = createItem mod:mod, id:"coal", displayName:"Coal"
|
||||||
return coal
|
return coal
|
||||||
|
|
||||||
exports.configureCobblestone = configureCobblestone = (mod)->
|
exports.configureCobblestone = configureCobblestone = (mod)->
|
||||||
cobblestone = mod.items["cobblestone"]
|
cobblestone = mod.modPack.findItem "cobblestone"
|
||||||
if not cobblestone?
|
if not cobblestone?
|
||||||
cobblestone = createItem mod:mod, displayName:"Cobblestone", isGatherable:true
|
cobblestone = createItem mod:mod, displayName:"Cobblestone"
|
||||||
return cobblestone
|
return cobblestone
|
||||||
|
|
||||||
exports.configureCraftingTable = configureCraftingTable = (mod)->
|
exports.configureCraftingTable = configureCraftingTable = (mod)->
|
||||||
craftingTable = mod.items["crafting_table"]
|
craftingTable = mod.modPack.findItem "crafting_table"
|
||||||
if not craftingTable?
|
if not craftingTable?
|
||||||
craftingTable = createItem mod:mod, id:"crafting_table", displayName:"Crafting Table"
|
craftingTable = createItem mod:mod, id:"crafting_table", displayName:"Crafting Table"
|
||||||
oakPlanks = configureOakPlank mod
|
oakPlanks = configureOakPlank mod
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:craftingTable
|
recipe = createRecipe output:createStack item:craftingTable
|
||||||
recipe.setInputAt 0, 0, createStack item:oakPlanks
|
recipe.setInputAt 0, 0, createStack item:oakPlanks
|
||||||
recipe.setInputAt 0, 1, createStack item:oakPlanks
|
|
||||||
recipe.setInputAt 1, 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
|
recipe.setInputAt 1, 1, createStack item:oakPlanks
|
||||||
|
|
||||||
return craftingTable
|
return craftingTable
|
||||||
|
|
||||||
exports.configureEgg = configureEgg = (mod)->
|
exports.configureEgg = configureEgg = (mod)->
|
||||||
egg = mod.items["egg"]
|
egg = mod.modPack.findItem "egg"
|
||||||
if not egg?
|
if not egg?
|
||||||
egg = createItem mod:mod, id:"egg", displayName:"Egg", isGatherable:true
|
egg = createItem mod:mod, id:"egg", displayName:"Egg"
|
||||||
return egg
|
return egg
|
||||||
|
|
||||||
exports.configureFurnace = configureFurnace = (mod)->
|
exports.configureFurnace = configureFurnace = (mod)->
|
||||||
furnace = mod.items["furnace"]
|
furnace = mod.modPack.findItem "furnace"
|
||||||
if not furnace?
|
if not furnace?
|
||||||
cobblestone = configureCobblestone mod
|
cobblestone = configureCobblestone mod
|
||||||
craftingTable = configureCraftingTable mod
|
craftingTable = configureCraftingTable mod
|
||||||
@@ -124,19 +124,19 @@ exports.configureFurnace = configureFurnace = (mod)->
|
|||||||
|
|
||||||
recipe = createRecipe output:createStack item:furnace
|
recipe = createRecipe output:createStack item:furnace
|
||||||
recipe.setInputAt 0, 0, createStack item:cobblestone
|
recipe.setInputAt 0, 0, createStack item:cobblestone
|
||||||
recipe.setInputAt 0, 1, createStack item:cobblestone
|
|
||||||
recipe.setInputAt 0, 2, createStack item:cobblestone
|
|
||||||
recipe.setInputAt 1, 0, createStack item:cobblestone
|
recipe.setInputAt 1, 0, createStack item:cobblestone
|
||||||
recipe.setInputAt 1, 2, createStack item:cobblestone
|
|
||||||
recipe.setInputAt 2, 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 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.setInputAt 2, 2, createStack item:cobblestone
|
||||||
recipe.addTool craftingTable
|
recipe.addTool craftingTable
|
||||||
|
|
||||||
return furnace
|
return furnace
|
||||||
|
|
||||||
exports.configureIronIngot = configureIronIngot = (mod)->
|
exports.configureIronIngot = configureIronIngot = (mod)->
|
||||||
ironIngot = mod.items["iron_ingot"]
|
ironIngot = mod.modPack.findItem "iron_ingot"
|
||||||
if not ironIngot?
|
if not ironIngot?
|
||||||
coal = configureCoal mod
|
coal = configureCoal mod
|
||||||
furnace = configureFurnace mod
|
furnace = configureFurnace mod
|
||||||
@@ -144,23 +144,23 @@ exports.configureIronIngot = configureIronIngot = (mod)->
|
|||||||
ironOre = configureIronOre mod
|
ironOre = configureIronOre mod
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:ironIngot, quantity:8
|
recipe = createRecipe output:createStack item:ironIngot, quantity:8
|
||||||
recipe.setInputAt 0, 1, createStack item:ironOre, quantity:8
|
recipe.setInputAt 1, 0, createStack item:ironOre, quantity:8
|
||||||
recipe.setInputAt 2, 1, createStack item:coal
|
recipe.setInputAt 1, 2, createStack item:coal
|
||||||
recipe.addTool furnace
|
recipe.addTool furnace
|
||||||
|
|
||||||
return ironIngot
|
return ironIngot
|
||||||
|
|
||||||
exports.configureIronBlock = configureIronBlock = (mod)->
|
exports.configureIronBlock = configureIronBlock = (mod)->
|
||||||
ironBlock = mod.items["iron_block"]
|
ironBlock = mod.modPack.findItem "iron_block"
|
||||||
if not ironBlock?
|
if not ironBlock?
|
||||||
craftingTable = configureCraftingTable mod
|
craftingTable = configureCraftingTable mod
|
||||||
ironBlock = createItem mod:mod, id:"iron_block", displayName:"Iron Block"
|
ironBlock = createItem mod:mod, id:"iron_block", displayName:"Iron Block"
|
||||||
ironIngot = configureIronIngot mod
|
ironIngot = configureIronIngot mod
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:ironBlock
|
recipe = createRecipe output:createStack item:ironBlock
|
||||||
for row in [0..2]
|
for x in [0..2]
|
||||||
for col in [0..2]
|
for y in [0..2]
|
||||||
recipe.setInputAt row, col, createStack item:ironIngot
|
recipe.setInputAt x, y, createStack item:ironIngot
|
||||||
recipe.addTool craftingTable
|
recipe.addTool craftingTable
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:ironIngot, quantity:9
|
recipe = createRecipe output:createStack item:ironIngot, quantity:9
|
||||||
@@ -169,7 +169,7 @@ exports.configureIronBlock = configureIronBlock = (mod)->
|
|||||||
return ironBlock
|
return ironBlock
|
||||||
|
|
||||||
exports.configureIronSword = configureIronSword = (mod)->
|
exports.configureIronSword = configureIronSword = (mod)->
|
||||||
ironSword = mod.items["iron_sword"]
|
ironSword = mod.modPack.findItem "iron_sword"
|
||||||
if not ironSword?
|
if not ironSword?
|
||||||
craftingTable = configureCraftingTable mod
|
craftingTable = configureCraftingTable mod
|
||||||
ironIngot = configureIronIngot mod
|
ironIngot = configureIronIngot mod
|
||||||
@@ -177,15 +177,15 @@ exports.configureIronSword = configureIronSword = (mod)->
|
|||||||
stick = configureStick mod
|
stick = configureStick mod
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:ironSword
|
recipe = createRecipe output:createStack item:ironSword
|
||||||
recipe.setInputAt 0, 1, createStack item:ironIngot
|
recipe.setInputAt 1, 0, createStack item:ironIngot
|
||||||
recipe.setInputAt 1, 1, createStack item:ironIngot
|
recipe.setInputAt 1, 1, createStack item:ironIngot
|
||||||
recipe.setInputAt 2, 1, createStack item:stick
|
recipe.setInputAt 1, 2, createStack item:stick
|
||||||
recipe.addTool craftingTable
|
recipe.addTool craftingTable
|
||||||
|
|
||||||
return ironSword
|
return ironSword
|
||||||
|
|
||||||
exports.configureIronShovel = configureIronShovel = (mod)->
|
exports.configureIronShovel = configureIronShovel = (mod)->
|
||||||
ironShovel = mod.items["iron_shovel"]
|
ironShovel = mod.modPack.findItem "iron_shovel"
|
||||||
if not ironShovel?
|
if not ironShovel?
|
||||||
craftingTable = configureCraftingTable mod
|
craftingTable = configureCraftingTable mod
|
||||||
ironIngot = configureIronIngot mod
|
ironIngot = configureIronIngot mod
|
||||||
@@ -193,40 +193,40 @@ exports.configureIronShovel = configureIronShovel = (mod)->
|
|||||||
stick = configureStick mod
|
stick = configureStick mod
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:ironShovel
|
recipe = createRecipe output:createStack item:ironShovel
|
||||||
recipe.setInputAt 0, 1, createStack item:ironIngot
|
recipe.setInputAt 1, 0, createStack item:ironIngot
|
||||||
recipe.setInputAt 1, 1, createStack item:stick
|
recipe.setInputAt 1, 1, createStack item:stick
|
||||||
recipe.setInputAt 2, 1, createStack item:stick
|
recipe.setInputAt 1, 2, createStack item:stick
|
||||||
recipe.addTool craftingTable
|
recipe.addTool craftingTable
|
||||||
|
|
||||||
return ironShovel
|
return ironShovel
|
||||||
|
|
||||||
exports.configureIronOre = configureIronOre = (mod)->
|
exports.configureIronOre = configureIronOre = (mod)->
|
||||||
ironOre = mod.items["iron_ore"]
|
ironOre = mod.modPack.findItem "iron_ore"
|
||||||
if not ironOre?
|
if not ironOre?
|
||||||
ironOre = createItem mod:mod, id:"iron_ore", displayName:"Iron Ore", isGatherable:true
|
ironOre = createItem mod:mod, id:"iron_ore", displayName:"Iron Ore"
|
||||||
return ironOre
|
return ironOre
|
||||||
|
|
||||||
exports.configureMilk = configureMilk = (mod)->
|
exports.configureMilk = configureMilk = (mod)->
|
||||||
milk = mod.items["milk"]
|
milk = mod.modPack.findItem "milk"
|
||||||
if not milk?
|
if not milk?
|
||||||
milk = createItem mod:mod, id:"milk", displayName:"Milk", isGatherable:true
|
milk = createItem mod:mod, id:"milk", displayName:"Milk"
|
||||||
return milk
|
return milk
|
||||||
|
|
||||||
exports.configureMilkBucket = configureMilkBucket = (mod)->
|
exports.configureMilkBucket = configureMilkBucket = (mod)->
|
||||||
milkBucket = mod.items["milk_bucket"]
|
milkBucket = mod.modPack.findItem "milk_bucket"
|
||||||
if not milkBucket?
|
if not milkBucket?
|
||||||
bucket = configureBucket mod
|
bucket = configureBucket mod
|
||||||
milk = configureMilk mod
|
milk = configureMilk mod
|
||||||
milkBucket = createItem mod:mod, id:"milkBucket", displayName:"Milk Bucket"
|
milkBucket = createItem mod:mod, id:"milkBucket", displayName:"Milk Bucket"
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:milkBucket
|
recipe = createRecipe output:createStack item:milkBucket
|
||||||
recipe.setInputAt 0, 1, createStack item:milk
|
recipe.setInputAt 1, 0, createStack item:milk
|
||||||
recipe.setInputAt 1, 1, createStack item:bucket
|
recipe.setInputAt 1, 1, createStack item:bucket
|
||||||
|
|
||||||
return milkBucket
|
return milkBucket
|
||||||
|
|
||||||
exports.configureOakPlank = configureOakPlank = (mod)->
|
exports.configureOakPlank = configureOakPlank = (mod)->
|
||||||
oakPlanks = mod.items["oak_planks"]
|
oakPlanks = mod.modPack.findItem "oak_planks"
|
||||||
if not oakPlanks?
|
if not oakPlanks?
|
||||||
oakPlanks = createItem mod:mod, id:"oak_planks", displayName:"Oak Planks"
|
oakPlanks = createItem mod:mod, id:"oak_planks", displayName:"Oak Planks"
|
||||||
oakWood = configureOakWood mod
|
oakWood = configureOakWood mod
|
||||||
@@ -237,31 +237,37 @@ exports.configureOakPlank = configureOakPlank = (mod)->
|
|||||||
return oakPlanks
|
return oakPlanks
|
||||||
|
|
||||||
exports.configureOakWood = configureOakWood = (mod)->
|
exports.configureOakWood = configureOakWood = (mod)->
|
||||||
oakWood = mod.items["oak_wood"]
|
oakWood = mod.modPack.findItem "oak_wood"
|
||||||
if not oakWood?
|
if not oakWood?
|
||||||
oakWood = createItem mod:mod, id:"oak_wood", displayName:"Oak Wood", isGatherable:true
|
oakWood = createItem mod:mod, id:"oak_wood", displayName:"Oak Wood"
|
||||||
return oakWood
|
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)->
|
exports.configureRedstoneDust = configureRedstoneDust = (mod)->
|
||||||
redstoneDust = mod.items["redstone_dust"]
|
redstoneDust = mod.modPack.findItem "redstone_dust"
|
||||||
if not redstoneDust?
|
if not redstoneDust?
|
||||||
redstoneDust = createItem mod:mod, id:"redstone_dust", displayName:"Redstone Dust", isGatherable:true
|
redstoneDust = createItem mod:mod, id:"redstone_dust", displayName:"Redstone Dust"
|
||||||
return redstoneDust
|
return redstoneDust
|
||||||
|
|
||||||
exports.configureStick = configureStick = (mod)->
|
exports.configureStick = configureStick = (mod)->
|
||||||
stick = mod.items["stick"]
|
stick = mod.modPack.findItem "stick"
|
||||||
if not stick?
|
if not stick?
|
||||||
oakPlanks = configureOakPlank mod
|
oakPlanks = configureOakPlank mod
|
||||||
stick = createItem mod:mod, id:"stick", displayName:"Stick"
|
stick = createItem mod:mod, id:"stick", displayName:"Stick"
|
||||||
|
|
||||||
recipe = createRecipe output:createStack item:stick, quantity:4
|
recipe = createRecipe output:createStack item:stick, quantity:4
|
||||||
recipe.setInputAt 0, 0, createStack item:oakPlanks
|
recipe.setInputAt 0, 0, createStack item:oakPlanks
|
||||||
recipe.setInputAt 1, 0, createStack item:oakPlanks
|
recipe.setInputAt 0, 1, createStack item:oakPlanks
|
||||||
|
|
||||||
return stick
|
return stick
|
||||||
|
|
||||||
exports.configureSugar = configureSugar = (mod)->
|
exports.configureSugar = configureSugar = (mod)->
|
||||||
sugar = mod.items["sugar"]
|
sugar = mod.modPack.findItem "sugar"
|
||||||
if not sugar?
|
if not sugar?
|
||||||
sugar = createItem mod:mod, id:"sugar", displayName:"Sugar"
|
sugar = createItem mod:mod, id:"sugar", displayName:"Sugar"
|
||||||
sugarCane = configureSugarCane mod
|
sugarCane = configureSugarCane mod
|
||||||
@@ -272,7 +278,7 @@ exports.configureSugar = configureSugar = (mod)->
|
|||||||
return sugar
|
return sugar
|
||||||
|
|
||||||
exports.configureSaw = configureSaw = (mod)->
|
exports.configureSaw = configureSaw = (mod)->
|
||||||
saw = mod.items["saw"]
|
saw = mod.modPack.findItem "saw"
|
||||||
if not saw?
|
if not saw?
|
||||||
craftingTable = configureCraftingTable mod
|
craftingTable = configureCraftingTable mod
|
||||||
ironBlock = configureIronBlock mod
|
ironBlock = configureIronBlock mod
|
||||||
@@ -284,13 +290,13 @@ exports.configureSaw = configureSaw = (mod)->
|
|||||||
|
|
||||||
recipe = createRecipe output:createStack item:saw
|
recipe = createRecipe output:createStack item:saw
|
||||||
recipe.setInputAt 0, 0, createStack item:oakPlank
|
recipe.setInputAt 0, 0, createStack item:oakPlank
|
||||||
recipe.setInputAt 0, 1, createStack item:ironIngot
|
recipe.setInputAt 1, 0, createStack item:ironIngot
|
||||||
recipe.setInputAt 0, 2, createStack item:oakPlank
|
|
||||||
recipe.setInputAt 1, 0, createStack item:oakPlank
|
|
||||||
recipe.setInputAt 1, 1, createStack item:ironBlock
|
|
||||||
recipe.setInputAt 1, 2, createStack item:oakPlank
|
|
||||||
recipe.setInputAt 2, 0, createStack item:oakPlank
|
recipe.setInputAt 2, 0, createStack item:oakPlank
|
||||||
recipe.setInputAt 2, 1, createStack item:redstoneDust
|
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.setInputAt 2, 2, createStack item:oakPlank
|
||||||
recipe.addTool craftingTable
|
recipe.addTool craftingTable
|
||||||
|
|
||||||
@@ -301,13 +307,13 @@ exports.configureSaw = configureSaw = (mod)->
|
|||||||
return saw
|
return saw
|
||||||
|
|
||||||
exports.configureSugarCane = configureSugarCane = (mod)->
|
exports.configureSugarCane = configureSugarCane = (mod)->
|
||||||
sugarCane = mod.items["sugar_cane"]
|
sugarCane = mod.modPack.findItem "sugar_cane"
|
||||||
if not sugarCane?
|
if not sugarCane?
|
||||||
sugarCane = createItem mod:mod, id:"sugar_cane", displayName:"Sugar Cane", isGatherable:true
|
sugarCane = createItem mod:mod, id:"sugar_cane", displayName:"Sugar Cane"
|
||||||
return sugarCane
|
return sugarCane
|
||||||
|
|
||||||
exports.configureWheat = configureWheat = (mod)->
|
exports.configureWheat = configureWheat = (mod)->
|
||||||
wheat = mod.items["wheat"]
|
wheat = mod.modPack.findItem "wheat"
|
||||||
if not wheat?
|
if not wheat?
|
||||||
wheat = createItem mod:mod, id:"wheat", displayName:"Wheat", isGatherable:true
|
wheat = createItem mod:mod, id:"wheat", displayName:"Wheat"
|
||||||
return wheat
|
return wheat
|
||||||
@@ -5,239 +5,86 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
Stack = require './stack'
|
Stack = require './stack'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
module.exports = class Inventory extends BaseModel
|
module.exports = class Inventory
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
constructor: (inventory=null)->
|
||||||
super attributes, options
|
@_id = _.uniqueId "inventory-"
|
||||||
attributes.modPack ?= null
|
@_stacks = {}
|
||||||
@clear()
|
|
||||||
|
|
||||||
if options.clone?
|
if inventory? then @merge inventory
|
||||||
@addInventory options.clone
|
|
||||||
|
|
||||||
# Class Methods ################################################################################
|
# Properties ###################################################################################
|
||||||
|
|
||||||
@Delimiters =
|
Object.defineProperties @prototype,
|
||||||
Item: '.'
|
|
||||||
Stack: ':'
|
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 ###############################################################################
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
add: (itemSlug, quantity=1, options={})->
|
add: (item, quantity)->
|
||||||
return this unless quantity > 0
|
return unless item?
|
||||||
|
return if quantity is 0
|
||||||
|
|
||||||
@_add itemSlug, quantity, options
|
existingStack = @_stacks[item.id]
|
||||||
@trigger c.event.add, this, itemSlug, quantity
|
if existingStack?
|
||||||
@trigger c.event.change, this
|
if existingStack.quantity + quantity < 0 then throw new Error "cannot have a negative quantity"
|
||||||
return this
|
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
|
||||||
|
|
||||||
addInventory: (inventory)->
|
if @_stacks[item.id].quantity is 0
|
||||||
inventory.each (stack)=> @_add stack.itemSlug, stack.quantity
|
delete @_stacks[item.id]
|
||||||
|
|
||||||
@trigger c.event.change, this
|
clear: ->
|
||||||
return this
|
|
||||||
|
|
||||||
clear: (options={})->
|
|
||||||
@_stacks = {}
|
@_stacks = {}
|
||||||
@_itemSlugs = []
|
|
||||||
|
|
||||||
@trigger c.event.change, this
|
contains: (item)->
|
||||||
|
return @_stacks[item.id]?
|
||||||
|
|
||||||
clone: ->
|
getQuantity: (item)->
|
||||||
inventory = new Inventory
|
existingStack = @_stacks[item.id]
|
||||||
inventory.addInventory this
|
return 0 unless existingStack?
|
||||||
return inventory
|
return existingStack.quantity
|
||||||
|
|
||||||
each: (callback)->
|
merge: (inventory)->
|
||||||
for itemSlug in @_itemSlugs
|
for id, stack of inventory.stacks
|
||||||
stack = @_stacks[itemSlug]
|
@add stack.item, stack.quantity
|
||||||
continue unless stack?
|
|
||||||
callback stack
|
|
||||||
|
|
||||||
getSlugs: ->
|
remove: (item, quantity)->
|
||||||
return @_itemSlugs[..]
|
@add item, -1 * quantity
|
||||||
|
|
||||||
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 #############################################################################
|
# Object Overrides #############################################################################
|
||||||
|
|
||||||
toString: ->
|
toString: (options={})->
|
||||||
result = [@constructor.name, " (", @cid, ") {items: ["]
|
options.full ?= false
|
||||||
|
|
||||||
|
if options.full
|
||||||
|
result = []
|
||||||
needsDelimiter = false
|
needsDelimiter = false
|
||||||
@each (stack)->
|
|
||||||
if needsDelimiter then result.push ', '
|
stackList = (stack for itemId, stack of @_stacks)
|
||||||
result.push stack.toString()
|
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
|
needsDelimiter = true
|
||||||
result.push ']'
|
|
||||||
|
|
||||||
result.push '}'
|
result.push stack.quantity
|
||||||
return result.join ''
|
result.push " "
|
||||||
|
result.push stack.item.displayName
|
||||||
# Private Methods ##############################################################################
|
return result.join ""
|
||||||
|
|
||||||
_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
|
else
|
||||||
@_itemSlugs.push itemSlug
|
return "Inventory<#{@_id}>@#{(id for id, item of @_stacks).length}"
|
||||||
@_sort()
|
|
||||||
else
|
|
||||||
stack.quantity += quantity
|
|
||||||
|
|
||||||
_sort: ->
|
|
||||||
@_itemSlugs.sort (a, b)-> ItemSlug.compare a, b
|
|
||||||
|
|||||||
@@ -1,213 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - inventory.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
EventRecorder = require '../event_recorder'
|
|
||||||
Inventory = require './inventory'
|
|
||||||
Item = require './item'
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
inventory = modPack = null
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe 'inventory.coffee', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
inventory = new Inventory {}, silent:false
|
|
||||||
inventory.add ItemSlug.slugify('wool'), 4
|
|
||||||
inventory.add ItemSlug.slugify('string'), 20
|
|
||||||
inventory.add ItemSlug.slugify('boat')
|
|
||||||
|
|
||||||
describe 'add', ->
|
|
||||||
|
|
||||||
it 'can add to an empty inventory', ->
|
|
||||||
inventory.add ItemSlug.slugify('iron_ingot'), 4
|
|
||||||
stack = inventory._stacks['iron_ingot']
|
|
||||||
stack.constructor.name.should.equal 'Stack'
|
|
||||||
stack.itemSlug.qualified.should.equal 'iron_ingot'
|
|
||||||
stack.quantity.should.equal 4
|
|
||||||
|
|
||||||
it 'can augment quantity of existing items', ->
|
|
||||||
inventory.add ItemSlug.slugify('wool'), 2
|
|
||||||
inventory.unparse().should.equal 'boat:20.string:6.wool'
|
|
||||||
|
|
||||||
it 'can add zero quantity', ->
|
|
||||||
inventory.add ItemSlug.slugify('wool'), 0
|
|
||||||
inventory.unparse().should.equal 'boat:20.string:4.wool'
|
|
||||||
|
|
||||||
it 'emits the proper events', ->
|
|
||||||
events = new EventRecorder inventory
|
|
||||||
inventory.add ItemSlug.slugify('iron_ingot'), 10
|
|
||||||
events.names.should.eql [c.event.add, c.event.change]
|
|
||||||
|
|
||||||
describe 'addInventory', ->
|
|
||||||
|
|
||||||
it 'can add to an empty inventory', ->
|
|
||||||
newInventory = new Inventory
|
|
||||||
newInventory.addInventory inventory
|
|
||||||
newInventory.unparse().should.equal 'boat:20.string:4.wool'
|
|
||||||
|
|
||||||
it 'can add a mix of new and existing items', ->
|
|
||||||
newInventory = new Inventory
|
|
||||||
newInventory.add ItemSlug.slugify('string'), 2
|
|
||||||
newInventory.addInventory inventory
|
|
||||||
newInventory.unparse().should.equal 'boat:22.string:4.wool'
|
|
||||||
|
|
||||||
describe 'clone', ->
|
|
||||||
|
|
||||||
it 'creates an empty inventory from an empty inventory', ->
|
|
||||||
a = new Inventory
|
|
||||||
b = a.clone()
|
|
||||||
b._itemSlugs.should.eql []
|
|
||||||
|
|
||||||
it 'faithfully copies an existing inventory', ->
|
|
||||||
copy = inventory.clone()
|
|
||||||
copy.unparse().should.equal 'boat:20.string:4.wool'
|
|
||||||
|
|
||||||
describe 'each', ->
|
|
||||||
|
|
||||||
it 'works with an empty inventory', ->
|
|
||||||
inventory = new Inventory
|
|
||||||
result = []
|
|
||||||
inventory.each (item)-> result.push item.name
|
|
||||||
result.should.eql []
|
|
||||||
|
|
||||||
it 'works when items have only been added', ->
|
|
||||||
result = []
|
|
||||||
inventory.each (stack)-> result.push stack.itemSlug.qualified
|
|
||||||
result.should.eql ['boat', 'string', 'wool']
|
|
||||||
|
|
||||||
it 'works when items have been augmented', ->
|
|
||||||
inventory.add ItemSlug.slugify 'iron_ingot'
|
|
||||||
inventory.add ItemSlug.slugify 'boat'
|
|
||||||
inventory.add ItemSlug.slugify('wool'), 2
|
|
||||||
|
|
||||||
result = []
|
|
||||||
inventory.each (stack)-> result.push stack.itemSlug.qualified
|
|
||||||
result.should.eql ['boat', 'iron_ingot', 'string', 'wool']
|
|
||||||
|
|
||||||
describe 'hasAtLeast', ->
|
|
||||||
|
|
||||||
it 'works when the item is completely absent', ->
|
|
||||||
answer = inventory.hasAtLeast 'chicken', 1
|
|
||||||
answer.should.be.false
|
|
||||||
|
|
||||||
it 'always returns true for zero quantity', ->
|
|
||||||
inventory.hasAtLeast('chicken', 0).should.be.true
|
|
||||||
inventory.hasAtLeast('wool', 0).should.be.true
|
|
||||||
|
|
||||||
it 'works for a quantity above 1', ->
|
|
||||||
inventory.hasAtLeast('wool', 3).should.be.true
|
|
||||||
inventory.hasAtLeast('wool', 4).should.be.true
|
|
||||||
inventory.hasAtLeast('wool', 5).should.be.false
|
|
||||||
|
|
||||||
describe 'localize', ->
|
|
||||||
|
|
||||||
before ->
|
|
||||||
modPack =
|
|
||||||
modSlug:
|
|
||||||
wool: 'minecraft'
|
|
||||||
string: 'minecraft'
|
|
||||||
boat: 'minecraft'
|
|
||||||
stone_gear: 'buildcraft'
|
|
||||||
findItem: (slug)->
|
|
||||||
return slug:new ItemSlug @modSlug[slug.item], slug.item
|
|
||||||
|
|
||||||
it 'replaces item slugs with qualified slugs', ->
|
|
||||||
inventory.add ItemSlug.slugify 'stone_gear'
|
|
||||||
inventory.modPack = modPack
|
|
||||||
inventory.localize()
|
|
||||||
|
|
||||||
slugs = []
|
|
||||||
inventory.each (stack)-> slugs.push stack.itemSlug.qualified
|
|
||||||
slugs.should.eql [
|
|
||||||
'minecraft__boat'
|
|
||||||
'buildcraft__stone_gear'
|
|
||||||
'minecraft__string'
|
|
||||||
'minecraft__wool'
|
|
||||||
]
|
|
||||||
|
|
||||||
it 'ignores qualified slugs', ->
|
|
||||||
inventory.add ItemSlug.slugify 'buildcraft__stone_gear'
|
|
||||||
inventory.modPack = modPack
|
|
||||||
inventory.localize()
|
|
||||||
|
|
||||||
slugs = []
|
|
||||||
inventory.each (stack)-> slugs.push stack.itemSlug.qualified
|
|
||||||
slugs.should.eql [
|
|
||||||
'minecraft__boat'
|
|
||||||
'buildcraft__stone_gear'
|
|
||||||
'minecraft__string'
|
|
||||||
'minecraft__wool'
|
|
||||||
]
|
|
||||||
|
|
||||||
describe 'parse', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
inventory = new Inventory {}, silent:false
|
|
||||||
|
|
||||||
it 'ignores an empty string', ->
|
|
||||||
result = inventory.parse ''
|
|
||||||
result.unparse().should.eql ''
|
|
||||||
|
|
||||||
it 'can parse a single item without quantity', ->
|
|
||||||
result = inventory.parse 'wool'
|
|
||||||
result.unparse().should.equal 'wool'
|
|
||||||
|
|
||||||
it 'can parse a single item with quantity', ->
|
|
||||||
result = inventory.parse '4.wool'
|
|
||||||
result.unparse().should.equal '4.wool'
|
|
||||||
|
|
||||||
it 'can parse multiple mixed-type items', ->
|
|
||||||
result = inventory.parse '4.wool:10.string:boat'
|
|
||||||
result.unparse().should.equal 'boat:10.string:4.wool'
|
|
||||||
|
|
||||||
describe 'pop', ->
|
|
||||||
|
|
||||||
it 'returns null for an empty inventory', ->
|
|
||||||
inventory = new Inventory
|
|
||||||
result = inventory.pop()
|
|
||||||
expect(result).to.be.null
|
|
||||||
|
|
||||||
it 'completely removes the last item', ->
|
|
||||||
stack = inventory.pop()
|
|
||||||
stack.itemSlug.qualified.should.equal 'wool'
|
|
||||||
stack.quantity.should.equal 4
|
|
||||||
inventory.unparse().should.equal 'boat:20.string'
|
|
||||||
|
|
||||||
it 'triggers the right events', ->
|
|
||||||
events = new EventRecorder inventory
|
|
||||||
result = inventory.pop()
|
|
||||||
events.names.should.eql [c.event.remove, c.event.change]
|
|
||||||
|
|
||||||
describe 'remove', ->
|
|
||||||
|
|
||||||
it 'does nothing when the item is absent', ->
|
|
||||||
before = inventory.unparse()
|
|
||||||
inventory.remove 'foo'
|
|
||||||
after = inventory.unparse()
|
|
||||||
|
|
||||||
before.should.equal after
|
|
||||||
|
|
||||||
it 'throws when the item has insufficient quantity', ->
|
|
||||||
expect(-> inventory.remove('wool', 10)).to.throw Error,
|
|
||||||
'cannot remove 10: only 4 wool in this inventory'
|
|
||||||
|
|
||||||
it 'removes all items by default', ->
|
|
||||||
inventory.remove 'wool'
|
|
||||||
expect(inventory._stacks.wool).to.be.empty
|
|
||||||
|
|
||||||
it 'removes a quantity above 1', ->
|
|
||||||
inventory.remove 'wool', 3
|
|
||||||
inventory._stacks.wool.quantity.should.equal 1
|
|
||||||
|
|
||||||
it 'emits the proper events', ->
|
|
||||||
events = new EventRecorder inventory
|
|
||||||
inventory.remove 'wool'
|
|
||||||
events.names.should.eql [c.event.change, c.event.remove, c.event.change]
|
|
||||||
@@ -5,86 +5,92 @@
|
|||||||
# All rights reserved.
|
# 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
|
module.exports = class Item
|
||||||
|
|
||||||
@Group = Other:'Other'
|
constructor: (attributes={})->
|
||||||
|
@id = attributes.id
|
||||||
|
@displayName = attributes.displayName
|
||||||
|
@isGatherable = attributes.isGatherable
|
||||||
|
@mod = attributes.mod
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
@_hasPrimaryRecipe = false
|
||||||
if not attributes.name? then throw new Error 'attributes.name is required'
|
@_recipesAsPrimary = {}
|
||||||
|
@_recipesAsExtra = {}
|
||||||
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 #############################################################################
|
# Property Methods #############################################################################
|
||||||
|
|
||||||
getIsCraftable: ->
|
|
||||||
if not @_isCraftable?
|
|
||||||
@_isCraftable = false
|
|
||||||
if @modVersion?
|
|
||||||
@_isCraftable = @modVersion.hasRecipes @slug
|
|
||||||
|
|
||||||
return @_isCraftable
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
Object.defineProperties @prototype,
|
||||||
isCraftable: {get:@prototype.getIsCraftable}
|
|
||||||
|
|
||||||
# Backbone.Model Overrides #####################################################################
|
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
|
||||||
|
|
||||||
parse: (text)->
|
firstRecipe: # the first Recipe returned by iterating the `recipes` property
|
||||||
ItemParser = require '../parsing/item_parser' # to avoid require cycles
|
get: ->
|
||||||
@_parser ?= new ItemParser model:this
|
recipeList = (recipe for id, recipe of @recipes)
|
||||||
@_parser.parse text
|
return null unless recipeList.length > 0
|
||||||
|
return recipeList[0]
|
||||||
|
set: -> throw new Error "firstRecipe cannot be assigned"
|
||||||
|
|
||||||
return null # prevent calling `set`
|
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
|
||||||
|
|
||||||
url: ->
|
isGatherable: # whether this item can be gathered directly without needing to be crafted
|
||||||
return c.url.itemData modSlug:@slug.mod, itemSlug:@slug.item
|
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 #############################################################################
|
# Object Overrides #############################################################################
|
||||||
|
|
||||||
toString: ->
|
toString: ->
|
||||||
builder = new StringBuilder
|
return "Item:#{@displayName}<#{@id}>"
|
||||||
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,122 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - item_slug.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe 'item_slug.coffee', ->
|
|
||||||
|
|
||||||
describe 'constructor', ->
|
|
||||||
|
|
||||||
it 'can handle one argument', ->
|
|
||||||
slug = new ItemSlug 'alpha'
|
|
||||||
slug.item.should.equal 'alpha'
|
|
||||||
expect(slug.mod).to.be.null
|
|
||||||
slug.qualified.should.equal 'alpha'
|
|
||||||
|
|
||||||
it 'can handle two arguments', ->
|
|
||||||
slug = new ItemSlug 'alpha', 'bravo'
|
|
||||||
slug.mod.should.equal 'alpha'
|
|
||||||
slug.item.should.equal 'bravo'
|
|
||||||
slug.qualified.should.equal 'alpha__bravo'
|
|
||||||
|
|
||||||
it 'throws with zero arguments', ->
|
|
||||||
f = -> new ItemSlug
|
|
||||||
expect(f).to.throw Error, 'expected arguments to be "modSlug, itemSlug" or just "itemSlug"'
|
|
||||||
|
|
||||||
it 'throws with more arguments', ->
|
|
||||||
f = -> new ItemSlug 'alpha', 'bravo', 'charlie'
|
|
||||||
expect(f).to.throw Error, 'expected arguments to be "modSlug, itemSlug" or just "itemSlug"'
|
|
||||||
|
|
||||||
describe 'ItemSlug.compare', ->
|
|
||||||
|
|
||||||
it 'sorts by item when both are qualified in the same mod', ->
|
|
||||||
a = new ItemSlug 'alpha', 'bravo'
|
|
||||||
b = new ItemSlug 'charlie', 'bravo'
|
|
||||||
|
|
||||||
ItemSlug.compare(a, b).should.equal -1
|
|
||||||
ItemSlug.compare(b, a).should.equal +1
|
|
||||||
|
|
||||||
it 'sorts by item when not qualified', ->
|
|
||||||
a = new ItemSlug 'alpha'
|
|
||||||
b = new ItemSlug 'bravo'
|
|
||||||
|
|
||||||
ItemSlug.compare(a, b).should.equal -1
|
|
||||||
ItemSlug.compare(b, a).should.equal +1
|
|
||||||
|
|
||||||
describe 'ItemSlug.equal', ->
|
|
||||||
|
|
||||||
it 'requires both to have the same mod', ->
|
|
||||||
a = new ItemSlug 'alpha', 'bravo'
|
|
||||||
b = new ItemSlug 'alpha', 'charlie'
|
|
||||||
c = new ItemSlug 'alpha', 'bravo'
|
|
||||||
|
|
||||||
ItemSlug.equal(a, b).should.be.false
|
|
||||||
ItemSlug.equal(a, c).should.be.true
|
|
||||||
|
|
||||||
it 'requires both to have the same item', ->
|
|
||||||
a = new ItemSlug 'alpha', 'bravo'
|
|
||||||
b = new ItemSlug 'alpha', 'charlie'
|
|
||||||
c = new ItemSlug 'alpha', 'bravo'
|
|
||||||
|
|
||||||
ItemSlug.equal(a, b).should.be.false
|
|
||||||
ItemSlug.equal(a, c).should.be.true
|
|
||||||
|
|
||||||
describe 'ItemSlug.slugify', ->
|
|
||||||
|
|
||||||
it 'can slugify a pure name', ->
|
|
||||||
slug = ItemSlug.slugify 'Alpha Bravo (Charlie)'
|
|
||||||
slug.item.should.equal 'alpha_bravo_charlie'
|
|
||||||
expect(slug.mod).to.be.null
|
|
||||||
|
|
||||||
it 'can slugify a simple item slug', ->
|
|
||||||
slug = ItemSlug.slugify 'alpha_bravo_charlie'
|
|
||||||
slug.item.should.equal 'alpha_bravo_charlie'
|
|
||||||
expect(slug.mod).to.be.null
|
|
||||||
|
|
||||||
it 'can slugify a fully-qualified slug', ->
|
|
||||||
slug = ItemSlug.slugify 'alpha_bravo__charlie_delta'
|
|
||||||
slug.mod.should.equal 'alpha_bravo'
|
|
||||||
slug.item.should.equal 'charlie_delta'
|
|
||||||
|
|
||||||
describe 'matches', ->
|
|
||||||
|
|
||||||
it 'ignores mod when either is unqualified', ->
|
|
||||||
a = new ItemSlug 'alpha', 'bravo'
|
|
||||||
b = new ItemSlug 'bravo'
|
|
||||||
c = new ItemSlug 'charlie'
|
|
||||||
|
|
||||||
a.matches(b).should.be.true
|
|
||||||
a.matches(c).should.be.false
|
|
||||||
|
|
||||||
it 'observes differences in mod when all are qualified', ->
|
|
||||||
a = new ItemSlug 'alpha', 'bravo'
|
|
||||||
b = new ItemSlug 'charlie', 'bravo'
|
|
||||||
c = new ItemSlug 'delta', 'echo'
|
|
||||||
d = new ItemSlug 'alpha', 'bravo'
|
|
||||||
|
|
||||||
a.matches(b).should.be.false
|
|
||||||
a.matches(c).should.be.false
|
|
||||||
a.matches(d).should.be.true
|
|
||||||
|
|
||||||
describe 'isQualified', ->
|
|
||||||
|
|
||||||
it 'returns true only when the mod slug is set', ->
|
|
||||||
a = new ItemSlug 'alpha', 'bravo'
|
|
||||||
b = new ItemSlug 'charlie'
|
|
||||||
a.isQualified.should.be.true
|
|
||||||
b.isQualified.should.be.false
|
|
||||||
|
|
||||||
describe '[]', ->
|
|
||||||
|
|
||||||
it 'allows slugs as a key', ->
|
|
||||||
slug = new ItemSlug 'alpha', 'bravo'
|
|
||||||
data = {}
|
|
||||||
data[slug] = 'foo'
|
|
||||||
data['alpha__bravo'].should.equal 'foo'
|
|
||||||
data[slug].should.equal 'foo'
|
|
||||||
@@ -5,230 +5,58 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
module.exports = class Mod extends BaseModel
|
module.exports = class Mod
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
constructor: (attributes={})->
|
||||||
if not attributes.slug? then throw new Error 'attributes.slug is required'
|
@id = attributes.id
|
||||||
|
@displayName = attributes.displayName
|
||||||
|
@modPack = attributes.modPack
|
||||||
|
|
||||||
attributes.author ?= ''
|
@_items = {}
|
||||||
attributes.description ?= ''
|
|
||||||
attributes.documentationUrl ?= null
|
|
||||||
attributes.downloadUrl ?= null
|
|
||||||
attributes.homePageUrl ?= null
|
|
||||||
attributes.modPack ?= null
|
|
||||||
attributes.name ?= ''
|
|
||||||
|
|
||||||
super attributes, options
|
# Properties ###################################################################################
|
||||||
|
|
||||||
@_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,
|
Object.defineProperties @prototype,
|
||||||
|
|
||||||
activeModVersion:
|
displayName:
|
||||||
get: -> @_activeModVersion
|
get: -> return @_displayName
|
||||||
|
set: (displayName)->
|
||||||
|
if not displayName? then throw new Error "displayName is required"
|
||||||
|
return if @_displayName is displayName
|
||||||
|
@_displayName = displayName
|
||||||
|
|
||||||
activeVersion:
|
id:
|
||||||
get: ->
|
get: -> return @_id
|
||||||
return @_activeVersion
|
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
|
||||||
|
|
||||||
set: (version)->
|
items:
|
||||||
return if version is @_activeVersion
|
get: -> return @_items
|
||||||
|
set: -> throw new Error "items cannot be replaced"
|
||||||
|
|
||||||
version ?= Mod.Version.None
|
modPack:
|
||||||
if version is Mod.Version.Latest then version = _.last(@_modVersions).version
|
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
|
||||||
|
|
||||||
if version is Mod.Version.None
|
# Public Methods ###############################################################################
|
||||||
@_activeVersion = version
|
|
||||||
@_activateModVersion null
|
|
||||||
|
|
||||||
@trigger c.event.change + ':activeVersion', this, @_activeVersion
|
addItem: (item)->
|
||||||
@trigger c.event.change, this
|
if not item? then return
|
||||||
else
|
if @_items[item.id] is item then return
|
||||||
for modVersion in @_modVersions
|
@_items[item.id] = item
|
||||||
if version is modVersion.version
|
item.mod = this
|
||||||
@_activateModVersion modVersion
|
|
||||||
break
|
|
||||||
|
|
||||||
@_activeVersion = version
|
# Object Overrides #############################################################################
|
||||||
@trigger c.event.change + ':activeVersion', this, @_activeVersion
|
|
||||||
@trigger c.event.change, this
|
|
||||||
|
|
||||||
enabled:
|
toString: ->
|
||||||
get: -> @_activeModVersion?
|
return "Mod:#{@displayName}<#{@id}>"
|
||||||
|
|
||||||
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,30 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Mod = require './mod'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
mod = null
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe 'mod.coffee', ->
|
|
||||||
|
|
||||||
beforeEach -> mod = new Mod name:'Test', slug:'test'
|
|
||||||
|
|
||||||
describe 'compareTo', ->
|
|
||||||
|
|
||||||
it 'lists required mods first', ->
|
|
||||||
minecraft = new Mod name:'Minecraft', slug:'minecraft'
|
|
||||||
mod.compareTo(minecraft).should.equal +1
|
|
||||||
minecraft.compareTo(mod).should.equal -1
|
|
||||||
|
|
||||||
it 'sorts by name second', ->
|
|
||||||
buildcraft = new Mod name:'Buildcraft', slug:'buildcraft'
|
|
||||||
mod.compareTo(buildcraft).should.equal +1
|
|
||||||
buildcraft.compareTo(mod).should.equal -1
|
|
||||||
@@ -5,198 +5,55 @@
|
|||||||
# All rights reserved.
|
# 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
|
module.exports = class ModPack
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
constructor: (attributes={})->
|
||||||
super attributes, options
|
@id = attributes.id
|
||||||
|
@displayName = attributes.displayName
|
||||||
|
|
||||||
@_mods = []
|
@_mods = {}
|
||||||
@_cache = {}
|
|
||||||
|
|
||||||
@on c.event.change, => @_cache = {}
|
# Property Methods #############################################################################
|
||||||
|
|
||||||
# Item Methods #################################################################################
|
Object.defineProperties @prototype,
|
||||||
|
|
||||||
chooseRandomItem: ->
|
displayName: # a string containing the user-displayable name of this ModPack
|
||||||
return null unless @_mods.length > 0
|
get: -> return @_displayName
|
||||||
|
set: (displayName)->
|
||||||
|
if not displayName? then throw new Error "displayName is required"
|
||||||
|
if @_displayName is displayName then return
|
||||||
|
@_displayName = displayName
|
||||||
|
|
||||||
modIndex = Math.floor Math.random() * @_mods.length
|
id: # a string which uniquely identifies this ModPack
|
||||||
return @_mods[modIndex].chooseRandomItem()
|
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
|
||||||
|
|
||||||
findItem: (itemSlug, options={})->
|
mods: # a hash of mod id to Mod containing all the mods which are part of this ModPack
|
||||||
options.includeDisabled ?= false
|
get: -> return @_mods
|
||||||
|
set: -> throw new Error "mods cannot be replaced"
|
||||||
|
|
||||||
key = "#{itemSlug}-#{options.includeDisabled}"
|
# Public Methods ###############################################################################
|
||||||
@_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)->
|
addMod: (mod)->
|
||||||
if not mod? then throw new Error 'mod is required'
|
if not mod? then return
|
||||||
return if @_mods.indexOf(mod) isnt -1
|
if @_mods[mod.id] is mod then return
|
||||||
|
@_mods[mod.id] = mod
|
||||||
mod.modPack = this
|
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
|
findItem: (itemId)->
|
||||||
@trigger c.event.sort + ':mod', this
|
for modId, mod of @mods
|
||||||
@trigger c.event.change, this
|
item = mod.items[itemId]
|
||||||
|
return item if item?
|
||||||
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
|
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 #############################################################################
|
# Object Overrides #############################################################################
|
||||||
|
|
||||||
toString: ->
|
toString: ->
|
||||||
return "ModPack (#{@cid}) {modVersions:«#{@_mods.length} items»}"
|
return "ModPack:#{@displayName}<#{@id}>"
|
||||||
|
|
||||||
# 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,103 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_pack.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Item = require './item'
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
Mod = require './mod'
|
|
||||||
ModPack = require './mod_pack'
|
|
||||||
ModVersion = require './mod_version'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
buildcraft = industrialCraft = minecraft = modPack = null
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe 'mod_pack.coffee', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
minecraft = new Mod slug:'minecraft', name:'Minecraft'
|
|
||||||
minecraft.addModVersion new ModVersion modSlug:minecraft.slug, version:'1.7.10'
|
|
||||||
minecraft.activeModVersion.addItem new Item name:'Wool'
|
|
||||||
minecraft.activeModVersion.addItem new Item name:'Bed', recipes:['']
|
|
||||||
minecraft.activeModVersion.registerName ItemSlug.slugify('iron_chestplate'), 'Iron Chestplate'
|
|
||||||
|
|
||||||
buildcraft = new Mod slug:'buildcraft', name:'Buildcraft'
|
|
||||||
buildcraft.addModVersion new ModVersion modSlug:buildcraft.slug, version:'6.2.6'
|
|
||||||
buildcraft.activeModVersion.addItem new Item name:'Stone Gear', recipes:['']
|
|
||||||
buildcraft.activeModVersion.addItem new Item name:'Wrench', recipes:['']
|
|
||||||
buildcraft.activeVersion = Mod.Version.None
|
|
||||||
|
|
||||||
industrialCraft = new Mod slug:'industrial_craft', name:'Industrial Craft'
|
|
||||||
industrialCraft.addModVersion new ModVersion modSlug:industrialCraft.slug, version:'2.0'
|
|
||||||
industrialCraft.activeModVersion.addItem new Item name:'Resin'
|
|
||||||
industrialCraft.activeModVersion.addItem new Item name:'Rubber'
|
|
||||||
industrialCraft.activeModVersion.addItem new Item name:'Wrench', recipes:['']
|
|
||||||
industrialCraft.activeVersion = Mod.Version.None
|
|
||||||
|
|
||||||
modPack = new ModPack
|
|
||||||
modPack.addMod minecraft
|
|
||||||
modPack.addMod buildcraft
|
|
||||||
modPack.addMod industrialCraft
|
|
||||||
|
|
||||||
describe 'findItem', ->
|
|
||||||
|
|
||||||
it 'can find an item by partial slug', ->
|
|
||||||
item = modPack.findItem ItemSlug.slugify 'wool'
|
|
||||||
item.slug.qualified.should.equal 'minecraft__wool'
|
|
||||||
|
|
||||||
it 'can find an item by full slug', ->
|
|
||||||
item = modPack.findItem ItemSlug.slugify 'minecraft__wool'
|
|
||||||
item.name.should.equal 'Wool'
|
|
||||||
|
|
||||||
it 'can find an ambiguous item by full slug', ->
|
|
||||||
buildcraft.activeVersion = Mod.Version.Latest
|
|
||||||
industrialCraft.activeVersion = Mod.Version.Latest
|
|
||||||
item = modPack.findItem ItemSlug.slugify 'industrial_craft__wrench'
|
|
||||||
item.name.should.equal 'Wrench'
|
|
||||||
item.modVersion.mod.name.should.equal 'Industrial Craft'
|
|
||||||
|
|
||||||
it 'can find an ambiguous item by partial slug', ->
|
|
||||||
buildcraft.activeVersion = Mod.Version.Latest
|
|
||||||
industrialCraft.activeVersion = Mod.Version.Latest
|
|
||||||
item = modPack.findItem ItemSlug.slugify 'wrench'
|
|
||||||
item.name.should.equal 'Wrench'
|
|
||||||
item.modVersion.mod.name.should.equal 'Buildcraft'
|
|
||||||
|
|
||||||
describe 'findItemByName', ->
|
|
||||||
|
|
||||||
it 'finds the requested item', ->
|
|
||||||
item = modPack.findItemByName 'Bed'
|
|
||||||
item.name.should.equal 'Bed'
|
|
||||||
|
|
||||||
it 'ignores disabled mod versions', ->
|
|
||||||
item = modPack.findItemByName 'Stone Gear'
|
|
||||||
expect(item).to.be.null
|
|
||||||
|
|
||||||
describe 'findItemDisplay', ->
|
|
||||||
|
|
||||||
it 'returns all data for a regular Minecraft item', ->
|
|
||||||
display = modPack.findItemDisplay ItemSlug.slugify 'bed'
|
|
||||||
display.iconUrl.should.equal '/data/minecraft/items/bed/icon.png'
|
|
||||||
display.itemUrl.should.equal '/browse/minecraft/bed/'
|
|
||||||
display.itemName.should.equal 'Bed'
|
|
||||||
display.modSlug.should.equal 'minecraft'
|
|
||||||
|
|
||||||
it 'returns all data for an item in an enabled mod', ->
|
|
||||||
buildcraft.activeVersion = '6.2.6'
|
|
||||||
display = modPack.findItemDisplay ItemSlug.slugify 'stone_gear'
|
|
||||||
display.iconUrl.should.equal '/data/buildcraft/items/stone_gear/icon.png'
|
|
||||||
display.itemUrl.should.equal '/browse/buildcraft/stone_gear/'
|
|
||||||
display.itemName.should.equal 'Stone Gear'
|
|
||||||
display.modSlug.should.equal 'buildcraft'
|
|
||||||
|
|
||||||
it 'assumes an unfound item is from Minecraft', ->
|
|
||||||
display = modPack.findItemDisplay ItemSlug.slugify 'iron_chestplate'
|
|
||||||
display.iconUrl.should.equal '/data/minecraft/items/iron_chestplate/icon.png'
|
|
||||||
display.itemUrl.should.equal '/browse/minecraft/iron_chestplate/'
|
|
||||||
display.itemName.should.equal 'Iron Chestplate'
|
|
||||||
display.modSlug.should.equal 'minecraft'
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - mod_version.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
Item = require './item'
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
ModVersion = require './mod_version'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
modVersion = null
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe 'mod_version.coffee', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
modVersion = new ModVersion modSlug:'test', version:'0.0'
|
|
||||||
modVersion.addItem new Item name:'underscore', group:'punctuation'
|
|
||||||
modVersion.addItem new Item name:'bravo', group:'letter'
|
|
||||||
modVersion.addItem new Item name:'alpha', group:'letter'
|
|
||||||
modVersion.addItem new Item name:'one', group:'number'
|
|
||||||
modVersion.addItem new Item name:'two', group:'number'
|
|
||||||
|
|
||||||
describe 'constructor', ->
|
|
||||||
|
|
||||||
it 'requires a mod slug', ->
|
|
||||||
expect(-> new ModVersion version:'0.0').to.throw Error, 'attributes.modSlug is required'
|
|
||||||
|
|
||||||
it 'requires a mod version', ->
|
|
||||||
expect(-> new ModVersion modSlug:'test').to.throw Error, 'attributes.version is required'
|
|
||||||
|
|
||||||
describe 'addItem', ->
|
|
||||||
|
|
||||||
it 'refuses to add duplicates', ->
|
|
||||||
modVersion.addItem new Item name:'Wool'
|
|
||||||
expect(-> modVersion.addItem new Item name:'Wool').to.throw Error, 'duplicate item for Wool'
|
|
||||||
|
|
||||||
it 'adds an item indexed by its slug', ->
|
|
||||||
modVersion.addItem new Item name:'Wool'
|
|
||||||
modVersion._items.wool.name.should.equal 'Wool'
|
|
||||||
|
|
||||||
it 'sets the modVersion', ->
|
|
||||||
modVersion.addItem new Item name:'Wool'
|
|
||||||
modVersion._items.wool.modVersion.should.equal modVersion
|
|
||||||
|
|
||||||
describe 'eachGroup', ->
|
|
||||||
|
|
||||||
it 'returns all the groups in order', ->
|
|
||||||
groupNames = []
|
|
||||||
modVersion.eachGroup (groupName)-> groupNames.push groupName
|
|
||||||
groupNames.should.eql ['letter', 'number', 'punctuation']
|
|
||||||
|
|
||||||
describe 'eachItemInGroup', ->
|
|
||||||
|
|
||||||
it 'returns immediately for unknown group', ->
|
|
||||||
slugs = []
|
|
||||||
modVersion.eachItemInGroup 'foobar', (item)-> slugs.push item.slug.qualified
|
|
||||||
slugs.should.eql []
|
|
||||||
|
|
||||||
it 'calls callback for exactly the items in a group in order', ->
|
|
||||||
slugs = []
|
|
||||||
modVersion.eachItemInGroup 'letter', (item)-> slugs.push item.slug.qualified
|
|
||||||
slugs.should.eql ['test__alpha', 'test__bravo']
|
|
||||||
|
|
||||||
slugs = []
|
|
||||||
modVersion.eachItemInGroup 'number', (item)-> slugs.push item.slug.qualified
|
|
||||||
slugs.should.eql ['test__one', 'test__two']
|
|
||||||
|
|
||||||
|
|
||||||
describe 'findItemByName', ->
|
|
||||||
|
|
||||||
it 'locates items by slugified name', ->
|
|
||||||
modVersion.addItem new Item name:'Crafting Table'
|
|
||||||
modVersion.findItemByName('Crafting Table').slug.qualified.should.equal 'test__crafting_table'
|
|
||||||
|
|
||||||
describe 'findRecipes', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
modVersion = new ModVersion modSlug:'test', version:'1.0'
|
|
||||||
modVersion.parse """
|
|
||||||
schema:1
|
|
||||||
|
|
||||||
item: Cake
|
|
||||||
recipe:; input: Milk, Sugar, Egg, Wheat; pattern: 000 121 333; extras: 3 Bucket
|
|
||||||
recipe:; input: Milk, Cocoa Beans, Egg, Wheat; pattern: 000 121 333; extras: 3 Bucket
|
|
||||||
recipe:; input: Cake Slice; pattern: 000 000 000; onlyIf: item Cake Slice
|
|
||||||
item: Bucket
|
|
||||||
recipe:; input: Iron Ingot; pattern: ... 0.0 .0.
|
|
||||||
recipe:; input: Copper Ingot; pattern: ... 0.0 .0.; extras: Copper Nugget
|
|
||||||
"""
|
|
||||||
|
|
||||||
it 'finds all recipes which list item as output', ->
|
|
||||||
recipes = modVersion.findRecipes ItemSlug.slugify('test__bucket')
|
|
||||||
(r.output[0].itemSlug.item for r in recipes).sort().should.eql ['bucket', 'bucket']
|
|
||||||
|
|
||||||
it 'skip recipes whose conditions are not met', ->
|
|
||||||
recipes = modVersion.findRecipes ItemSlug.slugify('test__cake')
|
|
||||||
recipes.length.should.equal 2
|
|
||||||
|
|
||||||
it 'finds recipes for items which are only ever extras', ->
|
|
||||||
recipes = modVersion.findRecipes ItemSlug.slugify('test__copper_nugget')
|
|
||||||
recipes.length.should.equal 1
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
#
|
|
||||||
# Crafting Guide - multiblock.test.coffee
|
|
||||||
#
|
|
||||||
# Copyright © 2014-2016 by Redwood Labs
|
|
||||||
# All rights reserved.
|
|
||||||
#
|
|
||||||
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
Mod = require './mod'
|
|
||||||
Multiblock = require './multiblock'
|
|
||||||
Stack = require './stack'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe 'multiblock.coffee', ->
|
|
||||||
|
|
||||||
describe 'with a single block type', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@input = [ new Stack itemSlug:ItemSlug.slugify('cobblestone'), quantity:1 ]
|
|
||||||
|
|
||||||
it 'correctly parses a 1x1x1 cube', ->
|
|
||||||
model = new Multiblock input:@input, layers:['0']
|
|
||||||
model.depth.should.equal 1
|
|
||||||
model.height.should.equal 1
|
|
||||||
model.width.should.equal 1
|
|
||||||
model.getStackAt(0, 0, 0).toString().should.equal '1 cobblestone'
|
|
||||||
|
|
||||||
it 'correctly parses a solid 3x3x3 cube', ->
|
|
||||||
model = new Multiblock input:@input, layers:['000 000 000', '000 000 000', '000 000 000']
|
|
||||||
model.depth.should.equal 3
|
|
||||||
model.height.should.equal 3
|
|
||||||
model.width.should.equal 3
|
|
||||||
|
|
||||||
for x in [0..2]
|
|
||||||
for y in [0..2]
|
|
||||||
for z in [0..2]
|
|
||||||
model.getStackAt(x, y, z).toString().should.equal '1 cobblestone'
|
|
||||||
|
|
||||||
it 'correctly parses a hollow 3x3x3 cube', ->
|
|
||||||
model = new Multiblock input:@input, layers:['000 000 000', '000 0.0 000', '000 000 000']
|
|
||||||
model.depth.should.equal 3
|
|
||||||
model.height.should.equal 3
|
|
||||||
model.width.should.equal 3
|
|
||||||
|
|
||||||
for x in [0..2]
|
|
||||||
for y in [0..2]
|
|
||||||
for z in [0..2]
|
|
||||||
if x isnt 1 or y isnt 1 or z isnt 1
|
|
||||||
model.getStackAt(x, y, z).toString().should.equal '1 cobblestone'
|
|
||||||
else
|
|
||||||
expect(model.getStackAt(x, y, z)).to.be.null
|
|
||||||
|
|
||||||
it 'correctly parses a 3x2x3 pyramid', ->
|
|
||||||
model = new Multiblock input:@input, layers:['000 000 000', '.. .0 ..']
|
|
||||||
model.depth.should.equal 3
|
|
||||||
model.height.should.equal 2
|
|
||||||
model.width.should.equal 3
|
|
||||||
|
|
||||||
for y in [0..1]
|
|
||||||
for z in [0..2]
|
|
||||||
for x in [0..2]
|
|
||||||
if y is 1 and (x isnt 1 or z isnt 1)
|
|
||||||
expect(model.getStackAt(x, y, z)).to.be.null
|
|
||||||
else
|
|
||||||
model.getStackAt(x, y, z).toString().should.equal '1 cobblestone'
|
|
||||||
|
|
||||||
describe 'with multiple block types', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
|
||||||
@input = [
|
|
||||||
new Stack itemSlug:ItemSlug.slugify('cobblestone'), quantity:1
|
|
||||||
new Stack itemSlug:ItemSlug.slugify('stone'), quantity:1
|
|
||||||
new Stack itemSlug:ItemSlug.slugify('oak wood'), quantity:1
|
|
||||||
]
|
|
||||||
|
|
||||||
it 'correctly parses a 1x3x1 column of different types', ->
|
|
||||||
model = new Multiblock input:@input, layers:['0', '1', '2']
|
|
||||||
model.depth.should.equal 1
|
|
||||||
model.height.should.equal 3
|
|
||||||
model.width.should.equal 1
|
|
||||||
|
|
||||||
model.getStackAt(0, 0, 0).toString().should.equal '1 cobblestone'
|
|
||||||
model.getStackAt(0, 1, 0).toString().should.equal '1 stone'
|
|
||||||
model.getStackAt(0, 2, 0).toString().should.equal '1 oak_wood'
|
|
||||||
@@ -5,243 +5,170 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
Stack = require './stack'
|
|
||||||
{StringBuilder} = require 'crafting-guide-common'
|
{StringBuilder} = require 'crafting-guide-common'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
module.exports = class Recipe extends BaseModel
|
module.exports = class Recipe
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
constructor: (attributes={})->
|
||||||
if not attributes.input? then throw new Error 'attributes.input is required'
|
@depth = attributes.depth
|
||||||
if not attributes.pattern? then throw new Error 'attributes.pattern is required'
|
@height = attributes.height
|
||||||
|
@id = attributes.id
|
||||||
|
@output = attributes.output
|
||||||
|
@width = attributes.width
|
||||||
|
|
||||||
if attributes.itemSlug? and not attributes.output?
|
@_extras = {}
|
||||||
attributes.output = [new Stack itemSlug:attributes.itemSlug, quantity:1]
|
@_inputs = {}
|
||||||
else if attributes.output? and not attributes.itemSlug?
|
@_inputGrid = []
|
||||||
if attributes.output.length is 0 then throw new Error 'attributes.output cannot be empty'
|
@_tools = {}
|
||||||
attributes.itemSlug = attributes.output[0].itemSlug
|
|
||||||
else
|
|
||||||
throw new Error 'attributes.itemSlug or attributes.output is required'
|
|
||||||
|
|
||||||
attributes.pattern = @_parsePattern attributes.pattern
|
# Properties ###################################################################################
|
||||||
|
|
||||||
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,
|
Object.defineProperties @prototype,
|
||||||
|
|
||||||
slug:
|
allProducts: # an array of Stacks starting the the primary output of this recipe
|
||||||
get: ->
|
get: -> return [].concat @output, (stack for id, stack of @extras)
|
||||||
if not @_slug?
|
set: -> throw new Error "allProducts cannot be assigned"
|
||||||
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, ' '
|
depth: # an integer specifying the number of layers to this recipe
|
||||||
builder.push stack.itemSlug.qualified
|
get: -> return @_depth
|
||||||
|
set: (depth)->
|
||||||
|
depth = parseInt "#{depth}"
|
||||||
|
depth = if Number.isNaN(depth) then 0 else Math.max(0, depth)
|
||||||
|
@_depth = depth
|
||||||
|
|
||||||
builder.push '>'
|
extras: # a hash of item id to Stack of all the non-primary outputs of this recipe
|
||||||
builder.push @pattern
|
get: -> return @_extras
|
||||||
builder.push '>'
|
set: -> throw new Error "extras cannot be replaced"
|
||||||
for stack in @tools
|
|
||||||
builder.push stack.itemSlug.qualified
|
|
||||||
builder.push '>'
|
|
||||||
|
|
||||||
delimiterNeeded = false
|
id: # a string which uniquely identifies this recipe
|
||||||
for stack in @output
|
get: -> return @_id
|
||||||
if delimiterNeeded then builder.push ','
|
set: (id)->
|
||||||
delimiterNeeded = true
|
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
|
||||||
|
|
||||||
if stack.quantity > 1 then builder.push stack.quantity, ' '
|
height: # an integer specifying the number of rows needed by this recipe
|
||||||
builder.push stack.itemSlug.qualified
|
get: -> return @_height
|
||||||
|
set: (height)->
|
||||||
|
height = parseInt "#{height}"
|
||||||
|
height = if Number.isNaN(height) then 0 else Math.max(0, height)
|
||||||
|
@_height = height
|
||||||
|
|
||||||
@_slug = builder.toString()
|
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"
|
||||||
|
|
||||||
return @_slug
|
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 #############################################################################
|
# Object Overrides #############################################################################
|
||||||
|
|
||||||
toString: ->
|
toString: (options={})->
|
||||||
result = [@constructor.name, " (", @cid, ") { name:", @name]
|
options.full ?= false
|
||||||
|
|
||||||
result.push ", input:["
|
if options.full
|
||||||
needsDelimiter = false
|
b = new StringBuilder
|
||||||
for stack in @input
|
b.loop (item for id, item of @inputs), delimiter:" + ", onEach:(b, item)=>
|
||||||
if needsDelimiter then result.push ', '
|
b.push @computeQuantityRequired(item), " ", item.displayName
|
||||||
result.push @getQuantityRequired stack.itemSlug
|
b.push " ="
|
||||||
result.push ' '
|
b.onlyIf @needsTools, (b)=>
|
||||||
result.push stack.itemSlug
|
b.push "("
|
||||||
needsDelimiter = true
|
b.loop (toolItem for id, toolItem of @tools), onEach:(b, toolItem)-> b.push toolItem.displayName
|
||||||
result.push ']'
|
b.push ")"
|
||||||
|
b.push "=> "
|
||||||
|
b.loop @allProducts, delimiter:" + ", onEach:(b, stack)=>
|
||||||
|
b.push stack.quantity, " ", stack.item.displayName
|
||||||
|
|
||||||
result.push ", output:["
|
return b.toString()
|
||||||
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
|
else
|
||||||
quantityMap[c] = 1
|
return "Recipe:#{@output}<#{@id}>"
|
||||||
|
|
||||||
@_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
|
|
||||||
|
|||||||
@@ -5,86 +5,68 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
Item = require './item'
|
fixtures = require "../fixtures"
|
||||||
ItemSlug = require './item_slug'
|
Item = require "./item"
|
||||||
Recipe = require './recipe'
|
Recipe = require "./recipe"
|
||||||
Stack = require './stack'
|
Stack = require "./stack"
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
input = output = pattern = recipe = null
|
describe "Recipe", ->
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
describe 'recipe.coffee', ->
|
|
||||||
|
|
||||||
describe 'constructor', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
input = [
|
@mod = fixtures.createMod()
|
||||||
new Stack(itemSlug:new ItemSlug('iron_gear')),
|
@stick = fixtures.configureStick @mod
|
||||||
new Stack(itemSlug:new ItemSlug('gold_ingot'), quantity:4)
|
@ironIngot = fixtures.configureIronIngot @mod
|
||||||
]
|
@obsidian = fixtures.configureObsidian @mod
|
||||||
pattern = '.1. 101 .1.'
|
|
||||||
|
|
||||||
it 'requires input', ->
|
@ironSword = new Item id:"iron_sword", displayName:"Iron Sword", mod:@mod
|
||||||
expect(-> new Recipe slug:'gold_gear', pattern:pattern).to.throw Error, 'attributes.input is required'
|
@obsidianBox = new Item id:"obsidian_box", displayName:"Obsidian Box", mod:@mod
|
||||||
|
|
||||||
it 'requires a pattern', ->
|
describe "getting & setting inputs", ->
|
||||||
expect(-> new Recipe slug:'gold_gear', input:input).to.throw Error, 'attributes.pattern is required'
|
|
||||||
|
|
||||||
it 'requires either outputs or a slug', ->
|
describe "for 2D recipes", ->
|
||||||
f = -> new Recipe input:input, pattern:pattern
|
|
||||||
expect(f).to.throw 'attributes.itemSlug or attributes.output is required'
|
|
||||||
|
|
||||||
it 'creates default output', ->
|
|
||||||
recipe = new Recipe itemSlug:ItemSlug.slugify('gold_gear'), input:input, pattern:pattern
|
|
||||||
recipe.output.length.should.equal 1
|
|
||||||
recipe.output[0].itemSlug.qualified.should.equal 'gold_gear'
|
|
||||||
recipe.output[0].quantity.should.equal 1
|
|
||||||
|
|
||||||
it 'assigns a default slug', ->
|
|
||||||
recipe = new Recipe input:input, pattern:pattern, output:[new Stack itemSlug:ItemSlug.slugify('gold_gear')]
|
|
||||||
recipe.itemSlug.qualified.should.equal 'gold_gear'
|
|
||||||
|
|
||||||
describe 'getStackAtSlot', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
input = [
|
@recipe = new Recipe id:"test1", output:new Stack item:@ironSword
|
||||||
new Stack itemSlug:ItemSlug.slugify('iron_gear')
|
@recipe.setInputAt 1, 0, new Stack item:@ironIngot
|
||||||
new Stack itemSlug:ItemSlug.slugify('gold_ingot'), quantity:4
|
@recipe.setInputAt 1, 1, new Stack item:@ironIngot
|
||||||
]
|
@recipe.setInputAt 1, 2, new Stack item:@stick
|
||||||
recipe = new Recipe itemSlug:'gold_gear', input:input, pattern:'.1. 101 .1.'
|
|
||||||
|
|
||||||
it 'returns the proper item for an early slot', ->
|
it "returns assigned values as expected", ->
|
||||||
stack = recipe.getStackAtSlot(1)
|
expect(@recipe.getInputAt(0, 0)).to.equal null
|
||||||
stack.itemSlug.qualified.should.equal 'gold_ingot'
|
@recipe.getInputAt(1, 0).item.displayName.should.equal "Iron Ingot"
|
||||||
stack.quantity.should.equal 4
|
@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 'returns the proper item for a late slot', ->
|
it "determines the correct dimentions", ->
|
||||||
stack = recipe.getStackAtSlot(4)
|
@recipe.depth.should.equal 1
|
||||||
stack.itemSlug.qualified.should.equal 'iron_gear'
|
@recipe.height.should.equal 3
|
||||||
stack.quantity.should.equal 1
|
@recipe.width.should.equal 2
|
||||||
|
|
||||||
it 'returns null for an invalid slot', ->
|
describe "for 3D recipes", ->
|
||||||
expect(recipe.getStackAtSlot(12)).to.be.null
|
|
||||||
|
|
||||||
describe '_parsePattern', ->
|
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
recipe = new Recipe
|
@recipe = new Recipe id:"test2", output:new Stack item:@obsidianBox
|
||||||
itemSlug: 'oak_wood_planks',
|
for x in [0..2]
|
||||||
input: [new Stack itemSlug:new ItemSlug('oak_wood')],
|
for y in [0..2]
|
||||||
pattern:'... .0. ...'
|
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
|
||||||
|
|
||||||
it 'normalizes invalid characters', ->
|
@recipe.setInputAt x, y, z, new Stack item:@obsidian
|
||||||
recipe._parsePattern('$$0 #() 010').should.equal '..0 ... 010'
|
|
||||||
|
|
||||||
it 'removes extra characters', ->
|
it "returns assigned values as expected", ->
|
||||||
recipe._parsePattern('000 000 000 000').should.equal '000 000 000'
|
@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 'fills in missing characters', ->
|
it "determines the correct dimentions", ->
|
||||||
recipe._parsePattern('000000').should.equal '000 000 ...'
|
@recipe.depth.should.equal 3
|
||||||
|
@recipe.height.should.equal 3
|
||||||
it 'fills in spaces', ->
|
@recipe.width.should.equal 3
|
||||||
recipe._parsePattern('000000000').should.equal '000 000 000'
|
|
||||||
@@ -5,19 +5,38 @@
|
|||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
BaseModel = require '../base_model'
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
module.exports = class Stack extends BaseModel
|
module.exports = class Stack
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
constructor: (attributes={})->
|
||||||
if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required'
|
@item = attributes.item
|
||||||
attributes.quantity ?= 1
|
@quantity = attributes.quantity
|
||||||
options.logEvents ?= false
|
|
||||||
super attributes, options
|
# 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 #############################################################################
|
# Object Overrides #############################################################################
|
||||||
|
|
||||||
toString: ->
|
toString: ->
|
||||||
return "#{@quantity} #{@itemSlug}"
|
return "Stack:#{@item}×#{@quantity}"
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
#
|
||||||
|
# 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 = {}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#
|
||||||
|
# Crafting Guide - mod_pack_store.coffee
|
||||||
|
#
|
||||||
|
# Copyright © 2014-2016 by Redwood Labs
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
ModPackJsonParser = require "../parsing/mod_pack_json_parser"
|
||||||
|
|
||||||
|
########################################################################################################################
|
||||||
|
|
||||||
|
module.exports = class ModPackStore
|
||||||
|
|
||||||
|
constructor: ->
|
||||||
|
@_data = {}
|
||||||
|
@_loading = {}
|
||||||
|
@_parser = new ModPackJsonParser
|
||||||
|
|
||||||
|
# Class Methods ################################################################################
|
||||||
|
|
||||||
|
Object.defineProperties ModPackStore,
|
||||||
|
instance:
|
||||||
|
get: ->
|
||||||
|
@_instance ?= new ModPackStore
|
||||||
|
return @_instance
|
||||||
|
set: ->
|
||||||
|
throw new Error "cannot assign instance"
|
||||||
|
|
||||||
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
|
get: (modPackId)->
|
||||||
|
return @_data[modPackId]
|
||||||
|
|
||||||
|
load: (modPackId)->
|
||||||
|
if not @_loading[modPackId]?
|
||||||
|
@_loading[modPackId] = w.promise (resolve, reject)->
|
||||||
|
url = c.url.modPackArchive modPackId:modPackId
|
||||||
|
|
||||||
|
onError = (xhr, status, message)=>
|
||||||
|
logger.error "failed to load mod pack #{modPackId}: #{status} — #{message}"
|
||||||
|
reject new Error message
|
||||||
|
|
||||||
|
onSuccess = (data, status, xhr)=>
|
||||||
|
logger.info "laoded mod pack: #{modPackId}"
|
||||||
|
try
|
||||||
|
@_parser.reset()
|
||||||
|
modPack = @_parser.parse data, url
|
||||||
|
@_data[modPack.id] = modPack
|
||||||
|
resolve modPack
|
||||||
|
catch error
|
||||||
|
reject error
|
||||||
|
|
||||||
|
$.ajax dataType: "text", error: onError, success: onSuccess, url: url
|
||||||
|
|
||||||
|
return @_loading[modPackId]
|
||||||
@@ -15,6 +15,7 @@ HeaderController = require './header/header_controller'
|
|||||||
ImageLoader = require './image_loader'
|
ImageLoader = require './image_loader'
|
||||||
Mod = require '../models/game/mod'
|
Mod = require '../models/game/mod'
|
||||||
ModPack = require '../models/game/mod_pack'
|
ModPack = require '../models/game/mod_pack'
|
||||||
|
ModPackStore = require '../models/store/mod_pack_store'
|
||||||
Router = require './router'
|
Router = require './router'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
@@ -30,7 +31,6 @@ 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,23 +41,6 @@ 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)=>
|
||||||
|
|||||||
@@ -29,46 +29,6 @@ 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.4.6' }
|
|
||||||
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
|
||||||
@@ -119,6 +79,9 @@ login.clientIds =
|
|||||||
'staging': '3d75ed772ce5004180d6'
|
'staging': '3d75ed772ce5004180d6'
|
||||||
'production': 'ce71be7f66926ff6ff38'
|
'production': 'ce71be7f66926ff6ff38'
|
||||||
|
|
||||||
|
exports.modpack = modpack = {}
|
||||||
|
modpack.default = "crafting-guide-default"
|
||||||
|
|
||||||
exports.modelState = modelState = {}
|
exports.modelState = modelState = {}
|
||||||
modelState.unloaded = 'unloaded'
|
modelState.unloaded = 'unloaded'
|
||||||
modelState.loading = 'loading'
|
modelState.loading = 'loading'
|
||||||
@@ -186,7 +149,7 @@ url.login = _.template "/login"
|
|||||||
url.mod = _.template "/browse/<%= modSlug %>/"
|
url.mod = _.template "/browse/<%= modSlug %>/"
|
||||||
url.modData = _.template "/data/<%= modSlug %>/mod.cg"
|
url.modData = _.template "/data/<%= modSlug %>/mod.cg"
|
||||||
url.modIcon = _.template "/data/<%= modSlug %>/icon.png"
|
url.modIcon = _.template "/data/<%= modSlug %>/icon.png"
|
||||||
url.modpackArchive = _.template "/data/modpack.cg"
|
url.modPackData = _.template "/data/<%= modPackId %>/modpack.json"
|
||||||
url.modVersionData = _.template "/data/<%= modSlug %>/versions/<%= modVersion %>/mod-version.cg"
|
url.modVersionData = _.template "/data/<%= modSlug %>/versions/<%= modVersion %>/mod-version.cg"
|
||||||
url.root = _.template "/"
|
url.root = _.template "/"
|
||||||
url.tutorial = _.template "/browse/<%= modSlug %>/tutorials/<%= tutorialSlug %>/"
|
url.tutorial = _.template "/browse/<%= modSlug %>/tutorials/<%= tutorialSlug %>/"
|
||||||
|
|||||||
Reference in New Issue
Block a user