LookAtMySuitBot/js/node_modules/mineflayer/lib/plugins/craft.js

244 lines
7.4 KiB
JavaScript
Raw Normal View History

2023-12-24 20:08:39 -05:00
const assert = require('assert')
const { once } = require('events')
module.exports = inject
function inject (bot) {
const Item = require('prismarine-item')(bot.registry)
const Recipe = require('prismarine-recipe')(bot.registry).Recipe
let windowCraftingTable
async function craft (recipe, count, craftingTable) {
assert.ok(recipe)
count = parseInt(count ?? 1, 10)
if (recipe.requiresTable && !craftingTable) {
throw new Error('recipe requires craftingTable')
}
try {
for (let i = 0; i < count; i++) {
await craftOnce(recipe, craftingTable)
}
if (windowCraftingTable) {
bot.closeWindow(windowCraftingTable)
windowCraftingTable = undefined
}
} catch (err) {
if (windowCraftingTable) {
bot.closeWindow(windowCraftingTable)
windowCraftingTable = undefined
}
throw new Error(err)
}
}
async function craftOnce (recipe, craftingTable) {
if (craftingTable) {
if (!windowCraftingTable) {
bot.activateBlock(craftingTable)
const [window] = await once(bot, 'windowOpen')
windowCraftingTable = window
}
if (!windowCraftingTable.type.startsWith('minecraft:crafting')) {
throw new Error('crafting: non craftingTable used as craftingTable')
}
await startClicking(windowCraftingTable, 3, 3)
} else {
await startClicking(bot.inventory, 2, 2)
}
async function startClicking (window, w, h) {
const extraSlots = unusedRecipeSlots()
let ingredientIndex = 0
let originalSourceSlot = null
let it
if (recipe.inShape) {
it = {
x: 0,
y: 0,
row: recipe.inShape[0]
}
await clickShape()
} else {
await nextIngredientsClick()
}
function incrementShapeIterator () {
it.x += 1
if (it.x >= it.row.length) {
it.y += 1
if (it.y >= recipe.inShape.length) return null
it.x = 0
it.row = recipe.inShape[it.y]
}
return it
}
async function nextShapeClick () {
if (incrementShapeIterator()) {
await clickShape()
} else if (!recipe.ingredients) {
await putMaterialsAway()
} else {
await nextIngredientsClick()
}
}
async function clickShape () {
const destSlot = slot(it.x, it.y)
const ingredient = it.row[it.x]
if (ingredient.id === -1) return nextShapeClick()
if (!window.selectedItem || window.selectedItem.type !== ingredient.id ||
(ingredient.metadata != null &&
window.selectedItem.metadata !== ingredient.metadata)) {
// we are not holding the item we need. click it.
const sourceItem = window.findInventoryItem(ingredient.id, ingredient.metadata)
if (!sourceItem) throw new Error('missing ingredient')
if (originalSourceSlot == null) originalSourceSlot = sourceItem.slot
await bot.clickWindow(sourceItem.slot, 0, 0)
}
await bot.clickWindow(destSlot, 1, 0)
await nextShapeClick()
}
async function nextIngredientsClick () {
const ingredient = recipe.ingredients[ingredientIndex]
const destSlot = extraSlots.pop()
if (!window.selectedItem || window.selectedItem.type !== ingredient.id ||
(ingredient.metadata != null &&
window.selectedItem.metadata !== ingredient.metadata)) {
// we are not holding the item we need. click it.
const sourceItem = window.findInventoryItem(ingredient.id, ingredient.metadata)
if (!sourceItem) throw new Error('missing ingredient')
if (originalSourceSlot == null) originalSourceSlot = sourceItem.slot
await bot.clickWindow(sourceItem.slot, 0, 0)
}
await bot.clickWindow(destSlot, 1, 0)
if (++ingredientIndex < recipe.ingredients.length) {
await nextIngredientsClick()
} else {
await putMaterialsAway()
}
}
async function putMaterialsAway () {
const start = window.inventoryStart
const end = window.inventoryEnd
await bot.putSelectedItemRange(start, end, window, originalSourceSlot)
await grabResult()
}
async function grabResult () {
assert.strictEqual(window.selectedItem, null)
// Causes a double-emit on 1.12+ --nickelpro
// put the recipe result in the output
const item = new Item(recipe.result.id, recipe.result.count, recipe.result.metadata)
window.updateSlot(0, item)
await bot.putAway(0)
await updateOutShape()
}
async function updateOutShape () {
if (!recipe.outShape) {
for (let i = 1; i <= w * h; i++) {
window.updateSlot(i, null)
}
return
}
const slotsToClick = []
for (let y = 0; y < recipe.outShape.length; ++y) {
const row = recipe.outShape[y]
for (let x = 0; x < row.length; ++x) {
const _slot = slot(x, y)
let item = null
if (row[x].id !== -1) {
item = new Item(row[x].id, row[x].count, row[x].metadata || null)
slotsToClick.push(_slot)
}
window.updateSlot(_slot, item)
}
}
for (const _slot of slotsToClick) {
await bot.putAway(_slot)
}
}
function slot (x, y) {
return 1 + x + w * y
}
function unusedRecipeSlots () {
const result = []
let x
let y
let row
if (recipe.inShape) {
for (y = 0; y < recipe.inShape.length; ++y) {
row = recipe.inShape[y]
for (x = 0; x < row.length; ++x) {
if (row[x].id === -1) result.push(slot(x, y))
}
for (; x < w; ++x) {
result.push(slot(x, y))
}
}
for (; y < h; ++y) {
for (x = 0; x < w; ++x) {
result.push(slot(x, y))
}
}
} else {
for (y = 0; y < h; ++y) {
for (x = 0; x < w; ++x) {
result.push(slot(x, y))
}
}
}
return result
}
}
}
function recipesFor (itemType, metadata, minResultCount, craftingTable) {
minResultCount = minResultCount ?? 1
const results = []
Recipe.find(itemType, metadata).forEach((recipe) => {
if (requirementsMetForRecipe(recipe, minResultCount, craftingTable)) {
results.push(recipe)
}
})
return results
}
function recipesAll (itemType, metadata, craftingTable) {
const results = []
Recipe.find(itemType, metadata).forEach((recipe) => {
if (!recipe.requiresTable || craftingTable) {
results.push(recipe)
}
})
return results
}
function requirementsMetForRecipe (recipe, minResultCount, craftingTable) {
if (recipe.requiresTable && !craftingTable) return false
// how many times we have to perform the craft to achieve minResultCount
const craftCount = Math.ceil(minResultCount / recipe.result.count)
// false if not enough inventory to make all the ones that we want
for (let i = 0; i < recipe.delta.length; ++i) {
const d = recipe.delta[i]
if (bot.inventory.count(d.id, d.metadata) + d.count * craftCount < 0) return false
}
// otherwise true
return true
}
bot.craft = craft
bot.recipesFor = recipesFor
bot.recipesAll = recipesAll
}