From cbb39403600e3b840e38d536c20a8bf0e3e3591a Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Tue, 16 Aug 2016 18:56:07 -0700 Subject: [PATCH] Add script to parse Minetweaker output --- .gitignore | 1 + items.json | 814 +++++++++++++++++++++++++++ package.json | 1 + scripts/convert-dump | 526 +++++++++++++++++ scripts/convert-nei-dump | 35 +- scripts/mods/buildcraft.coffee | 73 +++ scripts/mods/minecraft-1.7.10.coffee | 53 ++ 7 files changed, 1494 insertions(+), 9 deletions(-) create mode 100644 items.json create mode 100755 scripts/convert-dump create mode 100644 scripts/mods/buildcraft.coffee create mode 100644 scripts/mods/minecraft-1.7.10.coffee diff --git a/.gitignore b/.gitignore index 9bb93ea3..3dcce298 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build dist node_modules data/modpack.cg +tmp.cg diff --git a/items.json b/items.json new file mode 100644 index 00000000..5b2b9a14 --- /dev/null +++ b/items.json @@ -0,0 +1,814 @@ +item: Advanced Crafting Table + recipe: + input: Chest, Crafting Table, Obsidian, Redstone Chipset + pattern: 212 202 232 + +item: Architect Table + recipe: + input: Blueprint, Crafting Table, Dandelion Yellow, Diamond Gear, Ink Sac, Land Mark + pattern: 454 212 303 + +item: Assembly Table + recipe: + input: Diamond, Diamond Gear, Obsidian, Redstone + pattern: 232 202 212 + +item: Auto Workbench + recipe: + input: Crafting Table, Wood Gear + pattern: .1. 101 .1. + +item: Basic Gate + gatherable: true + +item: Basic Gate (Autarchic Pulsar) + gatherable: true + +item: Basic Gate (Clock Timer) + gatherable: true + +item: Basic Gate (Redstone Fader) + gatherable: true + +item: Blue Pipe Wire + gatherable: true + +item: Blueprint + recipe: + input: Lapis Lazuli, Paper + pattern: 111 101 111 + +item: Blueprint Library + recipe: + input: Blueprint, Bookshelf + pattern: 000 010 000 + +item: Builder + recipe: + input: Chest, Crafting Table, Dandelion Yellow, Diamond Gear, Ink Sac, Land Mark + pattern: 454 212 303 + +item: Charging Table + recipe: + input: Gold Gear, Obsidian, Redstone, Redstone Chipset + pattern: 121 131 101 + +item: Chute + recipe: + input: Chest, Iron Ingot, Stone Gear + pattern: 101 121 .1. + +item: Clay Transport Pipe + recipe: + input: Clay, Glass + pattern: 010 ... ... + +item: Cobblestone Fluid Pipe + gatherable: true + +item: Cobblestone Kinesis Pipe + gatherable: true + +item: Cobblestone Structure Pipe + gatherable: true + +item: Cobblestone Transport Pipe + recipe: + input: Cobblestone, Glass + pattern: 010 ... ... + +item: Combustion Engine + recipe: + input: Glass, Iron Gear, Iron Ingot, Piston + pattern: 222 .0. 131 + +item: Construction Mark + recipe: + input: Gold Gear, Redstone Torch + pattern: 0.1 ... ... + +item: Creative Engine + gatherable: true + +item: Daizuli Transport Pipe + recipe: + input: Diamond, Glass, Lapis Lazuli Block + pattern: 210 ... ... + +item: Diamond AND Gate + gatherable: true + +item: Diamond AND Gate (Autarchic Pulsar) + gatherable: true + +item: Diamond AND Gate (Clock Timer) + gatherable: true + +item: Diamond AND Gate (Redstone Fader) + gatherable: true + +item: Diamond Chipset + gatherable: true + +item: Diamond Fluid Pipe + gatherable: true + +item: Diamond Gear + recipe: + input: Diamond, Gold Gear + pattern: .0. 010 .0. + +item: Diamond Kinesis Pipe + gatherable: true + +item: Diamond OR Gate + gatherable: true + +item: Diamond OR Gate (Autarchic Pulsar) + gatherable: true + +item: Diamond OR Gate (Clock Timer) + gatherable: true + +item: Diamond OR Gate (Redstone Fader) + gatherable: true + +item: Diamond Transport Pipe + recipe: + input: Diamond, Glass + pattern: 010 ... ... + +item: Docking Station + recipe: + input: Golden Chipset, Iron Ingot + pattern: ... .1. 101 + +item: Emerald AND Gate + gatherable: true + +item: Emerald AND Gate (Autarchic Pulsar) + gatherable: true + +item: Emerald AND Gate (Clock Timer) + gatherable: true + +item: Emerald AND Gate (Redstone Fader) + gatherable: true + +item: Emerald Chipset + gatherable: true + +item: Emerald Fluid Pipe + gatherable: true + +item: Emerald Kinesis Pipe + gatherable: true + +item: Emerald OR Gate + gatherable: true + +item: Emerald OR Gate (Autarchic Pulsar) + gatherable: true + +item: Emerald OR Gate (Clock Timer) + gatherable: true + +item: Emerald OR Gate (Redstone Fader) + gatherable: true + +item: Emerald Transport Pipe + recipe: + input: Emerald, Glass + pattern: 010 ... ... + +item: Emzuli Transport Pipe + recipe: + input: Emerald, Glass, Lapis Lazuli Block + pattern: 210 ... ... + +item: End Stone Facade + gatherable: true + +item: End Stone Hollow Facade + gatherable: true + +item: Filler + recipe: + input: Chest, Crafting Table, Dandelion Yellow, Gold Gear, Ink Sac, Land Mark + pattern: 454 212 303 + +item: Filter (Black) + gatherable: true + +item: Filter (Blue) + gatherable: true + +item: Filter (Brown) + gatherable: true + +item: Filter (Cyan) + gatherable: true + +item: Filter (Gray) + gatherable: true + +item: Filter (Green) + gatherable: true + +item: Filter (Light Blue) + gatherable: true + +item: Filter (Light Gray) + gatherable: true + +item: Filter (Lime) + gatherable: true + +item: Filter (Magenta) + gatherable: true + +item: Filter (Orange) + gatherable: true + +item: Filter (Pink) + gatherable: true + +item: Filter (Purple) + gatherable: true + +item: Filter (Red) + gatherable: true + +item: Filter (White) + gatherable: true + +item: Filter (Yellow) + gatherable: true + +item: Filtered Buffer + recipe: + input: Chest, Diamond Transport Pipe, Oak Wood Planks, Piston + pattern: 212 202 232 + +item: Flood Gate + recipe: + input: Iron Bars, Iron Gear, Iron Ingot, Tank + pattern: 212 030 202 + +item: Frame + gatherable: true + +item: Fuel + gatherable: true + +item: Fuel Bucket + gatherable: true + +item: Gate Copier + gatherable: true + +item: Gold AND Gate + gatherable: true + +item: Gold AND Gate (Autarchic Pulsar) + gatherable: true + +item: Gold AND Gate (Clock Timer) + gatherable: true + +item: Gold AND Gate (Redstone Fader) + gatherable: true + +item: Gold Gear + recipe: + input: Gold Ingot, Iron Gear + pattern: .0. 010 .0. + +item: Gold OR Gate + gatherable: true + +item: Gold OR Gate (Autarchic Pulsar) + gatherable: true + +item: Gold OR Gate (Clock Timer) + gatherable: true + +item: Gold OR Gate (Redstone Fader) + gatherable: true + +item: Golden Chipset + gatherable: true + +item: Golden Fluid Pipe + gatherable: true + +item: Golden Kinesis Pipe + gatherable: true + +item: Golden Transport Pipe + recipe: + input: Glass, Gold Ingot + pattern: 101 ... ... + +item: Green Pipe Wire + gatherable: true + +item: Integration Table + recipe: + input: Diamond Gear, Obsidian, Redstone, Redstone Chipset + pattern: 121 131 101 + +item: Iron AND Gate + gatherable: true + +item: Iron AND Gate (Autarchic Pulsar) + gatherable: true + +item: Iron AND Gate (Clock Timer) + gatherable: true + +item: Iron AND Gate (Redstone Fader) + gatherable: true + +item: Iron Chipset + gatherable: true + +item: Iron Fluid Pipe + gatherable: true + +item: Iron Gear + recipe: + input: Iron Ingot, Stone Gear + pattern: .0. 010 .0. + +item: Iron Kinesis Pipe + gatherable: true + +item: Iron OR Gate + gatherable: true + +item: Iron OR Gate (Autarchic Pulsar) + gatherable: true + +item: Iron OR Gate (Clock Timer) + gatherable: true + +item: Iron OR Gate (Redstone Fader) + gatherable: true + +item: Iron Transport Pipe + recipe: + input: Glass, Iron Ingot + pattern: 101 ... ... + +item: Land Mark + recipe: + input: Lapis Lazuli, Redstone Torch + pattern: 0.1 ... ... + +item: Laser + recipe: + input: Diamond, Obsidian, Redstone + pattern: 122 002 122 + +item: Lazuli Transport Pipe + recipe: + input: Glass, Lapis Lazuli Block + pattern: 101 ... ... + +item: Lens (Black) + gatherable: true + +item: Lens (Blue) + gatherable: true + +item: Lens (Brown) + gatherable: true + +item: Lens (Cyan) + gatherable: true + +item: Lens (Gray) + gatherable: true + +item: Lens (Green) + gatherable: true + +item: Lens (Light Blue) + gatherable: true + +item: Lens (Light Gray) + gatherable: true + +item: Lens (Lime) + gatherable: true + +item: Lens (Magenta) + gatherable: true + +item: Lens (Orange) + gatherable: true + +item: Lens (Pink) + gatherable: true + +item: Lens (Purple) + gatherable: true + +item: Lens (Red) + gatherable: true + +item: Lens (White) + gatherable: true + +item: Lens (Yellow) + gatherable: true + +item: List + recipe: + input: Cactus Green, Paper + pattern: 111 101 111 + +item: Map Location + recipe: + input: Dandelion Yellow, Paper + pattern: 111 101 111 + +item: Mining Pipe + gatherable: true + +item: Mining Well + recipe: + input: Iron Gear, Iron Ingot, Iron Pickaxe, Redstone + pattern: 131 101 121 + +item: Moss Stone Facade + gatherable: true + +item: Moss Stone Hollow Facade + gatherable: true + +item: Obsidian Transport Pipe + recipe: + input: Glass, Obsidian + pattern: 101 ... ... + +item: Oil + gatherable: true + +item: Oil Bucket + gatherable: true + +item: Path Mark + recipe: + input: Cactus Green, Redstone Torch + pattern: 0.1 ... ... + +item: Pipe Plug + gatherable: true + +item: Pipe Sealant + gatherable: true + +item: Programming Table + recipe: + input: Diamond Gear, Emerald, Obsidian, Redstone + pattern: 232 212 202 + +item: Pulsating Chipset + gatherable: true + +item: Pump + recipe: + input: Mining Well, Tank + pattern: 10. ... ... + +item: Quarry + recipe: + input: Diamond Gear, Diamond Pickaxe, Gold Gear, Iron Gear, Redstone + pattern: 343 232 010 + +item: Quartz AND Gate + gatherable: true + +item: Quartz AND Gate (Autarchic Pulsar) + gatherable: true + +item: Quartz AND Gate (Clock Timer) + gatherable: true + +item: Quartz AND Gate (Redstone Fader) + gatherable: true + +item: Quartz Chipset + gatherable: true + +item: Quartz Fluid Pipe + gatherable: true + +item: Quartz Kinesis Pipe + gatherable: true + +item: Quartz OR Gate + gatherable: true + +item: Quartz OR Gate (Autarchic Pulsar) + gatherable: true + +item: Quartz OR Gate (Clock Timer) + gatherable: true + +item: Quartz OR Gate (Redstone Fader) + gatherable: true + +item: Quartz Transport Pipe + recipe: + input: Block of Quartz, Glass + pattern: 010 ... ... + +item: Red Pipe Wire + gatherable: true + +item: Redstone Board (Bomber) + recipe: + input: Paper, Redstone + pattern: 000 010 000 + +item: Redstone Board (Builder) + gatherable: true + +item: Redstone Board (Butcher) + gatherable: true + +item: Redstone Board (Carrier) + gatherable: true + +item: Redstone Board (Crafter) + gatherable: true + +item: Redstone Board (Delivery) + gatherable: true + +item: Redstone Board (Farmer) + gatherable: true + +item: Redstone Board (Harvester) + gatherable: true + +item: Redstone Board (Knight) + gatherable: true + +item: Redstone Board (Leaf Cutter) + gatherable: true + +item: Redstone Board (Lumberjack) + gatherable: true + +item: Redstone Board (Miner) + gatherable: true + +item: Redstone Board (Picker) + gatherable: true + +item: Redstone Board (Planter) + gatherable: true + +item: Redstone Board (Pump) + gatherable: true + +item: Redstone Board (Shovelman) + gatherable: true + +item: Redstone Board (Stripes) + gatherable: true + +item: Redstone Board (Tank) + gatherable: true + +item: Redstone Chipset + gatherable: true + +item: Redstone Comp Chipset + gatherable: true + +item: Redstone Crystal + gatherable: true + +item: Redstone Engine + recipe: + input: Glass, Oak Wood Planks, Piston, Wood Gear + pattern: 111 .0. 323 + +item: Refinery + recipe: + input: Diamond Gear, Redstone Torch, Tank + pattern: 121 202 ... + +item: Requester + recipe: + input: Chest, Iron Gear, Iron Ingot, Piston, Redstone + pattern: 232 101 242 + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot + gatherable: true + +item: Robot (Bomber) + recipe: + input: Diamond Chipset, Iron Ingot, Redstone Crystal + pattern: 111 121 0.0 + +item: Robot (Builder) + gatherable: true + +item: Robot (Butcher) + gatherable: true + +item: Robot (Carrier) + gatherable: true + +item: Robot (Crafter) + gatherable: true + +item: Robot (Delivery) + gatherable: true + +item: Robot (Farmer) + gatherable: true + +item: Robot (Harvester) + gatherable: true + +item: Robot (Knight) + gatherable: true + +item: Robot (Leaf Cutter) + gatherable: true + +item: Robot (Lumberjack) + gatherable: true + +item: Robot (Miner) + gatherable: true + +item: Robot (Picker) + gatherable: true + +item: Robot (Planter) + gatherable: true + +item: Robot (Pump) + gatherable: true + +item: Robot (Shovelman) + gatherable: true + +item: Robot (Stripes) + gatherable: true + +item: Robot (Tank) + gatherable: true + +item: Sandstone Fluid Pipe + gatherable: true + +item: Sandstone Kinesis Pipe + gatherable: true + +item: Sandstone Transport Pipe + recipe: + input: Glass, Sandstone + pattern: 101 ... ... + +item: Stirling Engine + recipe: + input: Cobblestone, Glass, Piston, Stone Gear + pattern: 000 .1. 323 + +item: Stone Facade + gatherable: true + +item: Stone Fluid Pipe + gatherable: true + +item: Stone Gear + recipe: + input: Cobblestone, Wood Gear + pattern: .0. 010 .0. + +item: Stone Hollow Facade + gatherable: true + +item: Stone Kinesis Pipe + gatherable: true + +item: Stone Transport Pipe + recipe: + input: Glass, Stone + pattern: 101 ... ... + +item: Stripes Transport Pipe + recipe: + input: Glass, Gold Gear + pattern: 101 ... ... + +item: Tank + recipe: + input: Glass + pattern: 000 0.0 000 + +item: Template + recipe: + input: Ink Sac, Paper + pattern: 111 101 111 + +item: Void Fluid Pipe + gatherable: true + +item: Void Transport Pipe + recipe: + input: Glass, Ink Sac, Redstone + pattern: 102 ... ... + +item: Wood Gear + recipe: + input: Stick + pattern: .0. 0.0 .0. + +item: Wooden Fluid Pipe + gatherable: true + +item: Wooden Kinesis Pipe + gatherable: true + +item: Wooden Transport Pipe + recipe: + input: Glass, Oak Wood Planks + pattern: 101 ... ... + +item: Wrench + recipe: + input: Iron Ingot, Stone Gear + pattern: 0.0 .1. .0. + +item: Yellow Pipe Wire + gatherable: true + +item: Zone Planner + recipe: + input: Diamond Gear, Empty Map, Gold Gear, Iron Ingot, Redstone + pattern: 343 212 303 + diff --git a/package.json b/package.json index 347bf254..4e6f2e94 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "coffee-script": "^1.9.2", "crafting-guide": "^3.0.0", "crafting-guide-common": "^1.3.0", + "fs-extra": "^0.30.0", "grunt": "^0.4.5", "grunt-contrib-clean": "^1.0.0", "grunt-contrib-compress": "^1.2.0", diff --git a/scripts/convert-dump b/scripts/convert-dump new file mode 100755 index 00000000..d25524a4 --- /dev/null +++ b/scripts/convert-dump @@ -0,0 +1,526 @@ +#!/usr/bin/env coffee + +_ = require 'underscore' +fs = require 'fs-extra' +path = require 'path' +util = require 'util' + +# Global Data ########################################################################################################## + +DEBUG = false +MOD = null +MOD_SCRIPT = null +WORLD_DIR = null +INDENT = 0 + +# Constants ############################################################################################################ + +MINETWEAKER_LOG = 'minetweaker.log' +ICON_DIR = path.join 'dumps', 'itempanel_icons' +ITEMPANEL_CSV = path.join 'dumps', 'itempanel.csv' + +# Process Arguments #################################################################################################### + +process.argv.shift() # coffee +process.argv.shift() # script file + +while process.argv.length > 0 + switch process.argv[0] + when '--world' + process.argv.shift() + WORLD_DIR = process.argv[0] + + when '--mod' + process.argv.shift() + MOD_SCRIPT = process.argv[0] + + process.argv.shift() + +if not WORLD_DIR? then throw new Error '--world is required' +if not MOD_SCRIPT? then throw new Error '--mod is required' + +# Classes ############################################################################################################## + +class MinecraftId + + constructor: (@mod, @name, @meta=null, @variant=null)-> + @meta = null if not @meta or @meta in ['0', '*'] + @meta = parseInt @meta + @meta = null if Number.isNaN @meta + + compareTo: (other)-> + return -1 unless other? + + if @mod isnt other.mod + return if @mod < other.mod then -1 else +1 + if @name isnt other.name + return if @name < other.name then -1 else +1 + if @meta isnt other.meta + if not @meta? then return -1 + if not other.meta? then return +1 + return if @meta < other.meta then -1 else +1 + if @variant isnt other.variant + if not @variant? then return -1 + if not other.variant? then return +1 + return if @variant < other.variant then -1 else +1 + + return 0 + + createVariant: (variant)-> + return new MinecraftId @mod, @name, @meta, variant + + equals: (other)-> + return @compareTo(other) is 0 + + matches: (other)-> + return false unless other? + return false unless @mod is other.mod + return false unless @name is other.name + return true if other.meta is null + return false unless @meta is other.meta + return true + + toString: -> + if not @_string + @_string = "#{@mod}:#{@name}" + if @meta? then @_string += ":#{@meta}" + if @variant? then @_string += ":#{@variant}" + return @_string + +# Helper Functions ##################################################################################################### + +addItem = (item)-> + if not item? then throw new Error "cannot add null item" + + MOD.items.push item + +computePattern = (recipe, inputGrid)-> + pattern = [['.', '.', '.'], ['.', '.', '.'], ['.', '.', '.']] + cells = _.compact _.flatten inputGrid + recipe.inputNames = (_.uniq (cell.item.displayName for cell in cells)).sort() + + for row, rowIndex in inputGrid + for gridCell, columnIndex in row + continue unless gridCell? + + itemNumber = recipe.inputNames.indexOf gridCell.item.displayName + + if itemNumber >= 0 + pattern[rowIndex][columnIndex] = "#{itemNumber}" + + p = pattern + recipe.pattern = "#{p[0][0]}#{p[0][1]}#{p[0][2]} #{p[1][0]}#{p[1][1]}#{p[1][2]} #{p[2][0]}#{p[2][1]}#{p[2][2]}" + +computeGridSize = (grid)-> + grid.width = grid.height = 0 + + grid.rawHeight = grid.length + grid.rawWidth = 0 + for row, rowIndex in grid + grid.rawWidth = Math.max grid.rawWidth, row.length + + for cell, colIndex in row + continue unless cell? + grid.height = Math.max grid.height, rowIndex + 1 + grid.width = Math.max grid.width, colIndex + 1 + + grid.isLarge = grid.width > 2 or grid.height > 2 + +computeSlug = (text)-> + return null unless text? + + result = text.toLowerCase() + result = result.replace /[^a-zA-Z0-9_]/g, '_' + result = result.replace /__+/g, '_' + result = result.replace /^_/, '' + result = result.replace /_$/, '' + return result + +debugLog = (text)-> + return unless DEBUG + console.error text + +findItem = (search)-> + for item in MOD.items + matches = true + for field, value of search + if field is 'minecraftId' + if not item.minecraftId.equals(search.minecraftId) + matches = false + break + + else if item[field] isnt value + matches = false + break + + return item if matches + + return null + +lookUpItems = (itemIds)-> + for row in itemIds + for gridCell in row + continue unless gridCell? + + item = findItem minecraftId:gridCell.minecraftId + if not item? + console.error "could not find an item for id: #{gridCell.minecraftId}" + return null + + gridCell.item = item + +normalizeMinecraftId = (minecraftId)-> + return minecraftId unless minecraftId.mod is 'ore' + entries = MOD.oreDict[minecraftId.toString()] + return minecraftId unless entries? + return minecraftId unless entries.length > 0 + return entries[0] + +normalizeRecipeGrid = (grid)-> + result = grid + + if grid.rawWidth is 2 and grid.rawHeight is 3 + result = [ + [ grid[0][0], grid[0][1], grid[1][0] ], + [ grid[1][1], grid[2][0], grid[2][1] ], + [ null, null, null ] + ] + else if grid.rawWidth is 1 and grid.rawHeight is 3 + result = [ + [ grid[0][0], grid[1][0], grid[2][0] ], + [ null, null, null ] + [ null, null, null ] + ] + else if grid.rawWidth is 1 and grid.rawHeight is 2 + result = [ + [ grid[0][0], grid[1][0], null ], + [ null, null, null ] + [ null, null, null ] + ] + + computeGridSize result + + return result + +parseMinecraftId = (minecraftId)-> + match = /^([^:]*):([^:]*)(:([0-9*]*))?$/.exec minecraftId + return null unless match? + return new MinecraftId match[1], match[2], match[4] + +parseRecipeInputs = (recipeType, text)-> + allRows = [] + currentRow = null + currentItem = '' + + completeCurrentItem = -> + currentItem = currentItem.trim() + if currentItem isnt 'null' + match = currentItem.match /^ *([^ ]*)( \* ([0-9]+))?/ + quantity = parseInt match[3] + if _.isNaN(quantity) then quantity = 1 + if match? + currentRow.push( + minecraftId: normalizeMinecraftId parseMinecraftId match[1] + quantity: quantity + ) + else + currentRow.push null + currentItem = '' + + inputText = if recipeType is 'Shapeless' then text[0..text.length] else text[1..text.length - 2] + for c in inputText.split '' + if c is '[' + currentRow = [] + else if c is ']' + completeCurrentItem() + allRows.push currentRow + currentRow = null + else if c is ',' + if currentRow? + completeCurrentItem() + else if c in ['<', '>'] + # do nothing + else + if currentItem? + currentItem += c + + if recipeType is 'Shapeless' + a = _.flatten allRows + allRows = [ + [ a[0], a[1], a[4] ] + [ a[2], a[3], a[5] ] + [ a[6], a[7], a[8] ] + ] + + computeGridSize allRows + + return allRows + +# Stage Functions ###################################################################################################### + +copyImages = -> + for item in MOD.items + continue unless item.iconFile? + + itemPath = path.join 'data', MOD.slug, 'items', item.slug + fs.ensureDir itemPath + fs.copySync item.iconFile, path.join(itemPath, 'icon.png'), clobber:true + +discardNonModItems = -> + result = [] + for item in MOD.items + if MOD.contains item + result.push item + + MOD.items = result + +loadModHelper = -> + MOD = require "./mods/#{MOD_SCRIPT}" + + MOD.slug ?= MOD_SCRIPT + MOD.version ?= '1.0' + + MOD.items ?= [] + MOD.oreDict ?= {} + + MOD.adjustRecipe ?= (recipe)-> return recipe + MOD.contains ?= (id)-> return true + MOD.correctItem ?= (item)-> # do nothing + MOD.makeCorrections ?= -> # do nothing + MOD.normalizeName ?= (item, index=1)-> return item.gameName + MOD.shouldNormalizeGrid ?= (item, grid)-> return false + +makeCorrections = -> + for item in MOD.items + MOD.correctItem item + + MOD.makeCorrections() + +removeDuplicates = -> + result = [] + + lastDisplayName = null + for item in MOD.items + continue if item.displayName is lastDisplayName + result.push item + lastDisplayName = item.displayName + + MOD.items = result + +scanFurnaceRecipes = -> + fileText = fs.readFileSync path.join(WORLD_DIR, MINETWEAKER_LOG), 'utf-8' + for line in fileText.split '\n' + match = line.match /furnace.addRecipe\(<([^>]*)>, <([^>]*)>/ + continue unless match? + + outputId = parseMinecraftId match[1] + outputItem = findItem minecraftId:outputId + if not outputItem? + console.error "Skipping furnace recipe for #{match[1]} because no matching item could be found" + continue + + inputId = parseMinecraftId match[2] + inputItem = findItem minecraftId:inputId + if not inputItem? + console.error "Skipping furnace recipe for #{match[1]} because input item #{match[2]} could not be found" + continue + + recipe = + inputNames: [ inputItem.gameName, 'furnace fuel' ] + quantity: 1 + extras: [] + tools: [ 'Furnace' ] + pattern: '.0. ... .1.' + + outputItem.recipes ?= [] + outputItem.recipes.push recipe + +scanIconFiles = -> + iconFiles = fs.readdirSync path.join WORLD_DIR, ICON_DIR + for fileName in iconFiles + match = fileName.match /^(.*?)(_([0-9]+))?\.png$/ + continue unless match? + + baseItem = findItem iconFileName:match[1] + if not baseItem? + console.error "Could not find item for icon file: #{fileName}" + continue + + index = parseInt match[3] + index = if Number.isNaN(index) then 1 else index + + if index is 1 + item = baseItem + item.displayName = MOD.normalizeName baseItem, index + else + displayName = MOD.normalizeName baseItem, index + if displayName + item = + minecraftId: baseItem.minecraftId.createVariant index + gameName: baseItem.gameName + displayName: displayName + iconFileName: fileName + slug: computeSlug displayName + + addItem item + else + console.error "Could not find item for icon file: #{fileName}" + + item.iconFile = path.join WORLD_DIR, ICON_DIR, fileName + +scanItemPanelCsv = -> + fileText = fs.readFileSync path.join(WORLD_DIR, ITEMPANEL_CSV), 'utf-8' + lines = fileText.split '\n' + lines.sort() + + priorLine = null + for line, lineNumber in lines + continue if lineNumber is 0 + continue if priorLine is line + + match = line.match /^([^,]*),([^,]*),([^,]*),([^,]*),([^,]*)$/ + continue unless match + + item = + minecraftId: parseMinecraftId "#{match[1]}:#{match[3]}" + gameName: match[5] + iconFileName: match[5].replace(/:/g, '_') + + item.displayName = MOD.normalizeName item + item.slug = computeSlug item.displayName + + addItem item + + priorLine = line + +scanOreDict = -> + fileText = fs.readFileSync path.join(WORLD_DIR, MINETWEAKER_LOG), 'utf-8' + lines = fileText.split '\n' + + index = 0 + while true + break if index >= lines.length + line = lines[index] + index += 1 + + match = line.match /^Ore entries for <([^>]*)> :$/ + continue unless match? + + genericIdText = match[1] + while true + break if index >= lines.length + line = lines[index] + + match = line.match /^ <([^>]*)>$/ + break unless match? + index += 1 + + MOD.oreDict[genericIdText] ?= [] + MOD.oreDict[genericIdText].push parseMinecraftId match[1] + +scanRecipes = -> + fileText = fs.readFileSync path.join(WORLD_DIR, MINETWEAKER_LOG), 'utf-8' + for line in fileText.split '\n' + match = line.match /recipes.add(Shaped|Shapeless)\(<([^>]*)>( \* ([0-9]+))?, (.*)\);/ + continue unless match? + + recipeType = match[1] + + outputId = parseMinecraftId match[2] + outputItem = findItem minecraftId:outputId + if not outputItem? + console.error "Ignoring recipe because there's no matching item: #{line}" + continue + + quantity = parseInt match[4] + quantity = if _.isNaN(quantity) then 1 else quantity + + inputGrid = parseRecipeInputs recipeType, match[5] + if MOD.shouldNormalizeGrid outputItem, inputGrid + inputGrid = normalizeRecipeGrid inputGrid + continue if inputGrid is null + + recipe = + inputNames: [] + quantity: quantity + extras: [] + tools: if inputGrid.isLarge then ['Crafting Table'] else [] + + lookUpItems inputGrid + computePattern recipe, inputGrid + + recipe = MOD.adjustRecipe recipe + if recipe? + outputItem.recipes ?= [] + outputItem.recipes.push recipe + +sortItems = -> + MOD.items.sort (a, b)-> + if a.displayName isnt b.displayName + return if a.displayName < b.displayName then -1 else +1 + + return a.minecraftId.compareTo b.minecraftId + +writeModVersionFile = -> + lines = ['schema: 1', ''] + indent = 0 + + write = (lineText)-> + for i in [0...indent] + lineText = ' ' + lineText + lines.push lineText + + for item in MOD.items + write "item: #{item.displayName}" + indent++ + + if (not item.isGatherable? and not item.recipes?) or item.isGatherable + write 'gatherable: yes' + + if item.recipes? + for recipe in item.recipes + write 'recipe:' + indent++ + + write "input: #{recipe.inputNames.join(', ')}" + + write "pattern: #{recipe.pattern}" + + if recipe.quantity isnt 1 + write "quantity: #{recipe.quantity}" + + if recipe.tools.length > 0 + write "tools: #{recipe.tools.join(', ')}" + + indent-- + + indent-- + write '' + + versionPath = path.join 'data', MOD.slug, 'versions', MOD.version + fs.ensureDirSync versionPath + fs.writeFileSync path.join(versionPath, 'mod-version.cg'), lines.join('\n'), encoding:'utf-8' + +writeJsonDump = -> + versionPath = path.join 'data', MOD.slug, 'versions', MOD.version + fs.ensureDirSync versionPath + fs.writeFileSync path.join(versionPath, 'mod-version.json'), JSON.stringify(MOD.items, null, 4), encoding:'utf-8' + +######################################################################################################################## + +loadModHelper() + +scanItemPanelCsv() +scanIconFiles() +scanOreDict() +scanRecipes() +scanFurnaceRecipes() + +discardNonModItems() +sortItems() +removeDuplicates() +makeCorrections() + +writeModVersionFile() +writeJsonDump() +copyImages() diff --git a/scripts/convert-nei-dump b/scripts/convert-nei-dump index fe1aed57..e1b483de 100755 --- a/scripts/convert-nei-dump +++ b/scripts/convert-nei-dump @@ -1,23 +1,40 @@ #!/bin/bash read -d '' USAGE < +USAGE: convert-dump - Converts output from NEI dump files into a format which can be used by - Crafting Guide. In particlar, this script looks for the "Item Panel" dump - both in PNG and CSV formats and assumes they contain all the data for the - target mod. + Converts output from various dump files into a format which can be used by + Crafting Guide. BEFORE RUNNING THIS SCRIPT, YOU MUST FIRST CREATE THE + NECESSARY DUMP FILES. - Unfortunate, NEI doesn't generate enough information to fill in all the - recipes for each item: only their names. Once this script has converted the - data, you will need to manually enter the missing content. + First, set up your Minecraft profile with the mod you want to dump as well + as NEI and MineTweaker 3. Then, launch the game into a single player world + and run the following console commands: + + /mt oredict + /mt recipes + /mt recipes furnace + + Then, open your inventory and click the "NEI Subsets" button at the top. + Right-click the "Mod" menu, and then left-click the one mod you want to + dump. This should change the icons displayed on the right to just those + belonging to that mod. + + Now, click the "Options" button in the lower-right corner. Then click + "Tools" followed by "Data Dumps". Next to "Item Panel", click the center + button until it says "PNG" and then the left hand button until it says + "48x48". Finally, click the "Dump" button. + + Next, click the center button on the "Item Panel" row until it says "CSV", + and then click "Dump" again. + + You have now created all the dump files needed by this script. : the "dumps" directory where NEI dropped its content : the slug of the mod being converted : the version of the mod being converted END - SOURCE_DIR="$1"; shift MOD_SLUG="$1"; shift MOD_VERSION="$1"; shift diff --git a/scripts/mods/buildcraft.coffee b/scripts/mods/buildcraft.coffee new file mode 100644 index 00000000..0075e6d9 --- /dev/null +++ b/scripts/mods/buildcraft.coffee @@ -0,0 +1,73 @@ + +FACADE_PATTERN = /^Facade: ([^(]*)$/ + +GATE_PATTERN = /^.*(OR|AND|Basic) Gate$/ + +HOLLOW_FACADE_PATTERN = /^Facade: ([^(]*) \(item.Facade.state_hollow\)/ + +exports.contains = (item)-> + return false if item.minecraftId.mod.indexOf('BuildCraft') is -1 + return false if item.displayName.match /^tile/ + return true if item.displayName.match /Stone( Hollow)? Facade/ + return false if item.displayName.match /Facade/ + return false if item.displayName.match /(Water|Oil) Spring$/ + return true + +exports.normalizeName = (item, index=1)-> + name = item.gameName + + if match = name.match FACADE_PATTERN + return "#{match[1]} Facade" + + if match = name.match GATE_PATTERN + switch index + when 2 then return "#{name} (Autarchic Pulsar)" + when 3 then return "#{name} (Clock Timer)" + when 4 then return "#{name} (Redstone Fader)" + + if match = name.match HOLLOW_FACADE_PATTERN + return "#{match[1]} Hollow Facade" + + if name is 'Redstone Board' + switch index + when 1 then return "Redstone Board (Bomber)" + when 2 then return "Redstone Board (Builder)" + when 3 then return "Redstone Board (Butcher)" + when 4 then return "Redstone Board (Carrier)" + when 5 then return "Redstone Board (Crafter)" + when 6 then return "Redstone Board (Delivery)" + when 7 then return "Redstone Board (Farmer)" + when 8 then return "Redstone Board (Knight)" + when 9 then return "Redstone Board (Harvester)" + when 10 then return "Redstone Board (Leaf Cutter)" + when 11 then return "Redstone Board (Lumberjack)" + when 12 then return "Redstone Board (Miner)" + when 13 then return "Redstone Board (Picker)" + when 14 then return "Redstone Board (Planter)" + when 15 then return "Redstone Board (Pump)" + when 16 then return "Redstone Board (Shovelman)" + when 17 then return "Redstone Board (Stripes)" + when 18 then return "Redstone Board (Tank)" + + if name is 'Robot' + switch index + when 1 then return "Robot (Bomber)" + when 2 then return "Robot (Builder)" + when 3 then return "Robot (Butcher)" + when 4 then return "Robot (Carrier)" + when 5 then return "Robot (Crafter)" + when 6 then return "Robot (Delivery)" + when 7 then return "Robot (Farmer)" + when 8 then return "Robot (Knight)" + when 9 then return "Robot (Harvester)" + when 10 then return "Robot (Leaf Cutter)" + when 11 then return "Robot (Lumberjack)" + when 12 then return "Robot (Miner)" + when 13 then return "Robot (Picker)" + when 14 then return "Robot (Planter)" + when 15 then return "Robot (Pump)" + when 16 then return "Robot (Shovelman)" + when 17 then return "Robot (Stripes)" + when 18 then return "Robot (Tank)" + + return name diff --git a/scripts/mods/minecraft-1.7.10.coffee b/scripts/mods/minecraft-1.7.10.coffee new file mode 100644 index 00000000..b024af64 --- /dev/null +++ b/scripts/mods/minecraft-1.7.10.coffee @@ -0,0 +1,53 @@ +WOOD_TYPES = [ 'Oak Wood', 'Birch', 'Spruce', 'Jungle', 'Acacia Wood', 'Dark Oak' ] + +exports.slug = 'minecraft' +exports.version = '1.7.10' + +exports.contains = (item)-> + return false if item.minecraftId.mod.indexOf('minecraft') is -1 + return false if item.minecraftId.name in [ 'end_portal', 'portal', 'mob_spawner', 'monster_egg', 'spawn_egg' ] + return true + +exports.correctItem = (item)-> + item.isGatherable = true if item.gameName in [ + 'Coal', 'Diamond', 'Emerald', 'Lapis Lazuli', 'Nether Quartz', 'Redstone', 'Stone', 'Wheat', 'Wool' + ] + + item.isGatherable = true if item.minecraftId.name in [ + 'melon_block' + ] + + item.isGatherable = false if item.gameName in [ + 'Bottle o\' Enchanting', 'Command Block', 'Farmland', 'Lava', 'Water', 'Written Book' + ] + +exports.normalizeName = (item)-> + name = item.gameName + + switch item.minecraftId.name + when 'clay' then name = 'Clay (block)' + when 'clay_ball' then name = 'Clay (item)' + when 'filled_map' then name = 'Map (filled)' + when 'melon_block' then name = 'Melon (block)' + when 'netherbrick' then name = 'Nether Brick (item)' + when 'nether_brick' then name = 'Nether Brick (block)' + when 'planks' + index = item.minecraftId.meta || 0 + name = "#{WOOD_TYPES[index]} Planks" + when 'stone_button' then name = 'Button (stone)' + when 'stone_pressure_plate' then name = 'Pressure Plate (stone)' + when 'wooden_button' then name = 'Button (wooden)' + when 'wooden_pressure_plate' then name = 'Pressure Plate (wooden)' + when 'wooden_slab' + index = item.minecraftId.meta || 0 + name = "#{WOOD_TYPES[index]} Slab" + + return name + +exports.shouldNormalizeGrid = (item, grid)-> + return true if item.gameName in [ + 'Bed', 'Bowl', 'Boat', 'Brewing Stand', 'Bucket', 'Cobblestone Slab', 'Diamond Boots', 'Diamond Helmet', + 'Minecart', 'Oak Wood Slab', 'Stone Slab', 'Trapdoor', 'Weighted Pressure Plate (Heavy)', + 'Weighted Pressure Plate (Light)' + ] + return false \ No newline at end of file