Qualify all item slugs to the mod which adds them #53

* Change default item URL to fall under /mod/<mod slug>/<item slug>
  but kept the old URL pattern around with the old behavior (i.e.,
  find the first item with that slug)
* Update Item to have a `qualifiedSlug` property which combines the
  item's own slug with its Mod's slug.  Updated a lot of code all
  over to use this in preference to the plain slug.
* Add a `localizeTo` method to Inventory to allow non-qualified slugs
  to be converted into qualified slugs within the inventory's stacks
* Update ModPack to work with either qualified or unqualified slugs
  for relevant methods
* Fix model state changes to happen before related events are
  triggered
* Add a debounce to the CraftingPlan object when triggering a recraft
  due to changes in related objects (esp. its inventories)
* Update the Inventory's sort to keep items sorted by mod before name
  with the Minecraft items sorted to the top
* Fix `ModPack.findItemByName` to actually recursively use the Mod's
  method instead of slugifying the given name and looking for the
  slug (since this won't necessarily work anymore).
* Ensure each Mod is assigned a reference to its containing ModPack
* Fix NameFinder to only consider an item non-gatherable if it is
  also craftable
* Update ModVersionParserV1 to assign qualified slugs to recipes
  for any items which are in the same mod (leaving any other slugs
  unqualified)
* Add Underscore mixins for composing and decomposing slugs
* Update old tests for the use of qualified slugs, and some new tests
  for the new methods added
This commit is contained in:
Andrew Miner
2015-02-10 17:56:02 -08:00
parent 246dbcafa9
commit 380a78ef61
28 changed files with 278 additions and 101 deletions
+14 -6
View File
@@ -44,25 +44,29 @@ describe 'crafting_plan.coffee', ->
plan.want.add 'oak_plank'
plan.craft()
plan.need.toList().should.eql ['oak_log']
plan.result.toList().should.eql [[4, 'oak_plank']]
plan.result.toList().should.eql [[4, 'minecraft__oak_plank']]
it 'can craft a multi-step recipe', ->
plan.want.add 'crafting_table'
plan.craft()
plan.need.toList().should.eql ['oak_log']
plan.result.toList().should.eql ['crafting_table']
plan.result.toList().should.eql ['minecraft__crafting_table']
it 'can craft a multi-step recipe using tools', ->
plan.want.add 'furnace'
plan.craft()
plan.need.toList().should.eql [[8, 'cobblestone']]
plan.result.toList().should.eql ['furnace']
plan.result.toList().should.eql ['minecraft__furnace']
it 'can craft a multi-step recipe re-using tools', ->
plan.want.add 'iron_sword'
plan.craft()
plan.need.toList().should.eql [[2, 'furnace_fuel'], [2, 'iron_ore'], 'oak_log']
plan.result.toList().should.eql ['iron_sword', [2, 'oak_plank'], [3, 'stick']]
plan.result.toList().should.eql [
'minecraft__iron_sword',
[2, 'minecraft__oak_plank'],
[3, 'minecraft__stick']
]
describe 'with building tools', ->
@@ -71,7 +75,7 @@ describe 'crafting_plan.coffee', ->
plan.want.add 'furnace'
plan.craft()
plan.need.toList().should.eql [[8, 'cobblestone'], 'oak_log']
plan.result.toList().should.eql ['crafting_table', 'furnace']
plan.result.toList().should.eql ['minecraft__crafting_table', 'minecraft__furnace']
it 'can craft a multi-step recipe re-using tools', ->
plan.includingTools = true
@@ -82,5 +86,9 @@ describe 'crafting_plan.coffee', ->
[8, 'cobblestone'], [2, 'furnace_fuel'], [2, 'iron_ore'], [2, 'oak_log']
]
plan.result.toList().should.eql [
'crafting_table', 'furnace', 'iron_sword', [2, 'oak_plank'], [3, 'stick']
'minecraft__crafting_table',
'minecraft__furnace',
'minecraft__iron_sword',
[2, 'minecraft__oak_plank'],
[3, 'minecraft__stick']
]
+34 -1
View File
@@ -12,7 +12,7 @@ Item = require '../src/scripts/models/item'
########################################################################################################################
inventory = null
inventory = modPack = null
########################################################################################################################
@@ -107,6 +107,39 @@ describe 'inventory.coffee', ->
inventory.hasAtLeast('wool', 4).should.be.true
inventory.hasAtLeast('wool', 5).should.be.false
describe 'localizeTo', ->
before ->
modPack =
map:
wool: 'minecraft__wool'
string: 'minecraft__string'
boat: 'minecraft__boat'
stone_gear: 'buildcraft__stone_gear'
findItem: (slug)->
[modSlug, itemSlug] = _.decomposeSlug slug
return slug:itemSlug, qualifiedSlug:@map[itemSlug]
it 'replaces item slugs with qualified slugs', ->
inventory.add 'stone_gear'
inventory.localizeTo modPack
inventory.toList().should.eql [
'minecraft__boat',
[20, 'minecraft__string'],
[4, 'minecraft__wool'],
'buildcraft__stone_gear'
]
it 'ignores qualified slugs', ->
inventory.add 'buildcraft__stone_gear'
inventory.localizeTo modPack
inventory.toList().should.eql [
'minecraft__boat',
[20, 'minecraft__string'],
[4, 'minecraft__wool'],
'buildcraft__stone_gear'
]
describe 'pop', ->
it 'returns null for an empty inventory', ->
+3 -3
View File
@@ -24,7 +24,7 @@ describe 'inventory_parser.coffee', ->
result.toList().should.eql []
it 'can parse a single item without quantity', ->
result = parser.parse 'Wool'
result = parser.parse 'wool'
result.toList().should.eql ['wool']
it 'can parse a single item with quantity', ->
@@ -32,11 +32,11 @@ describe 'inventory_parser.coffee', ->
result.toList().should.eql [[4, 'wool']]
it 'can parse multiple mixed-type items', ->
result = parser.parse '4.Wool:10.String:Boat'
result = parser.parse '4.wool:10.string:boat'
result.toList().should.eql ['boat', [10, 'string'], [4, 'wool']]
it 're-uses the given inventory object', ->
inventory = new Inventory
inventory.add 'string', 8
result = parser.parse '4.Wool', inventory
result = parser.parse '4.wool', inventory
result.toList().should.eql [[8, 'string'], [4, 'wool']]
+32 -7
View File
@@ -19,22 +19,23 @@ buildcraft = industrialCraft = minecraft = modPack = null
describe 'mod_pack.coffee', ->
beforeEach ->
minecraft = new Mod slug:'minecraft'
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.registerSlug 'iron_chestplate', 'Iron Chestplate'
buildcraft = new Mod slug:'buildcraft'
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:'Bed', recipes:['']
buildcraft.activeModVersion.addItem new Item name:'Wrench', recipes:['']
buildcraft.activeVersion = Mod.Version.None
industrialCraft = new Mod slug:'industrial_craft'
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
@@ -42,6 +43,30 @@ describe 'mod_pack.coffee', ->
modPack.addMod buildcraft
modPack.addMod industrialCraft
describe 'findItem', ->
it 'can find an item by partial slug', ->
item = modPack.findItem 'wool'
item.qualifiedSlug.should.equal 'minecraft__wool'
it 'can find an item by full slug', ->
item = modPack.findItem '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 '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 'wrench'
item.name.should.equal 'Wrench'
item.modVersion.mod.name.should.equal 'Buildcraft'
describe 'findItemByName', ->
it 'finds the requested item', ->
@@ -57,7 +82,7 @@ describe 'mod_pack.coffee', ->
it 'returns all data for a regular Minecraft item', ->
display = modPack.findItemDisplay 'bed'
display.iconUrl.should.equal '/data/minecraft/1.7.10/images/bed.png'
display.itemUrl.should.equal '/item/bed'
display.itemUrl.should.equal '/mod/minecraft/bed'
display.itemName.should.equal 'Bed'
display.modSlug.should.equal 'minecraft'
@@ -65,13 +90,13 @@ describe 'mod_pack.coffee', ->
buildcraft.activeVersion = '6.2.6'
display = modPack.findItemDisplay 'stone_gear'
display.iconUrl.should.equal '/data/buildcraft/6.2.6/images/stone_gear.png'
display.itemUrl.should.equal '/item/stone_gear'
display.itemUrl.should.equal '/mod/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 'iron_chestplate'
display.iconUrl.should.equal '/data/minecraft/1.7.10/images/iron_chestplate.png'
display.itemUrl.should.equal '/item/iron_chestplate'
display.itemUrl.should.equal '/mod/minecraft/iron_chestplate'
display.itemName.should.equal 'Iron Chestplate'
display.modSlug.should.equal 'minecraft'
+2 -2
View File
@@ -91,5 +91,5 @@ describe 'mod_version.coffee', ->
"""
it 'finds all recipes which list item as output', ->
recipes = modVersion.findRecipes 'bucket'
(r.output[0].slug for r in recipes).sort().should.eql ['bucket', 'cake', 'cake']
recipes = modVersion.findRecipes 'test__bucket'
(r.output[0].slug for r in recipes).sort().should.eql ['test__bucket', 'test__cake', 'test__cake']
@@ -68,7 +68,7 @@ describe 'mod_version_parser_v1.coffee', ->
it 'adds "input" when present', ->
modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo, Charlie; pattern: ... 012 ...'
slugs = (s.slug for s in modVersion._items.charlie._recipes[0].input)
slugs.should.eql ['alpha', 'bravo', 'charlie']
slugs.should.eql ['alpha', 'bravo', 'test__charlie']
it 'requires an "input" declaration', ->
func = -> parser.parse baseText + 'recipe:; pattern: ... .0. ...'
@@ -155,20 +155,20 @@ describe 'mod_version_parser_v1.coffee', ->
describe 'output', ->
beforeEach ->
baseText = 'item:Bravo; recipe:; input:Charlie; pattern:... .0. ...; '
baseText = 'item: Delta; item:Bravo; recipe:; input:Charlie; pattern:... .0. ...; '
it 'adds a single item as the default output', ->
modVersion = parser.parse baseText
stack = modVersion._items.bravo._recipes[0].output[0]
stack.slug.should.equal 'bravo'
stack.slug.should.equal 'test__bravo'
stack.quantity.should.equal 1
it 'can add multiple extras with quantities', ->
modVersion = parser.parse baseText + 'extras:2 Delta, 4 Echo'
output = modVersion._items.bravo._recipes[0].output
output[0].slug.should.equal 'bravo'
output[0].slug.should.equal 'test__bravo'
output[0].quantity.should.equal 1
output[1].slug.should.equal 'delta'
output[1].slug.should.equal 'test__delta'
output[1].quantity.should.equal 2
output[2].slug.should.equal 'echo'
output[2].quantity.should.equal 4
+3 -1
View File
@@ -5,6 +5,7 @@ Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
Item = require '../src/scripts/models/item'
Recipe = require '../src/scripts/models/recipe'
Stack = require '../src/scripts/models/stack'
@@ -32,7 +33,8 @@ describe 'recipe.coffee', ->
expect(-> new Recipe name:'Gold Gear', input:input).to.throw Error, 'attributes.pattern is required'
it 'allows an item to provide required attributes', ->
recipe = new Recipe item:{name:'Gold Gear', slug:'gold_gear'}, input:input, pattern:pattern
item = new Item name:'Gold Gear'
recipe = new Recipe item:item, input:input, pattern:pattern
recipe.name.should.equal 'Gold Gear'
(o.slug for o in recipe.output).should.eql ['gold_gear']