feat(skill): crafting

This commit is contained in:
RainbowBird
2025-01-08 23:26:21 +08:00
parent f997fe13ad
commit 405da9c3ad
+29 -40
View File
@@ -1,18 +1,15 @@
import type { SkillContext } from './base'
import * as world from '../composables/world'
import { createWorldContext } from '../composables/world'
import * as mc from '../utils/mcdata'
import { log } from './base'
import { placeBlock } from './blocks'
import { collectBlock, placeBlock } from './blocks'
import { goToPosition } from './movement'
/**
* Craft items from a recipe
*/
export async function craftRecipe(ctx: SkillContext, itemName: string, num = 1): Promise<boolean> {
const { bot } = ctx
let placedTable = false
if (mc.getItemCraftingRecipes(itemName).length === 0) {
if (mc.getItemCraftingRecipes(itemName)?.length === 0) {
log(ctx, `${itemName} is either not an item, or it does not have a crafting recipe!`)
return false
}
@@ -24,19 +21,19 @@ export async function craftRecipe(ctx: SkillContext, itemName: string, num = 1):
return false
}
let recipes = bot.recipesFor(itemId, null, 1, null)
let recipes = ctx.bot.recipesFor(itemId, null, 1, null)
let craftingTable = null
const craftingTableRange = 32
if (!recipes || recipes.length === 0) {
recipes = bot.recipesFor(itemId, null, 1, true)
recipes = ctx.bot.recipesFor(itemId, null, 1, true)
if (!recipes || recipes.length === 0) {
log(ctx, `You do not have the resources to craft a ${itemName}.`)
return false
}
// Look for crafting table
const worldCtx = { bot, botCtx: { bot, botName: bot.username } }
const worldCtx = createWorldContext(ctx.botCtx)
craftingTable = world.getNearestBlock(worldCtx, 'crafting_table', craftingTableRange)
if (!craftingTable) {
// Try to place crafting table
@@ -48,7 +45,7 @@ export async function craftRecipe(ctx: SkillContext, itemName: string, num = 1):
await placeBlock(ctx, 'crafting_table', pos.x, pos.y, pos.z)
craftingTable = world.getNearestBlock(worldCtx, 'crafting_table', craftingTableRange)
if (craftingTable) {
recipes = bot.recipesFor(itemId, null, 1, craftingTable)
recipes = ctx.bot.recipesFor(itemId, null, 1, craftingTable)
placedTable = true
}
}
@@ -59,34 +56,34 @@ export async function craftRecipe(ctx: SkillContext, itemName: string, num = 1):
}
}
else {
recipes = bot.recipesFor(itemId, null, 1, craftingTable)
recipes = ctx.bot.recipesFor(itemId, null, 1, craftingTable)
}
}
if (!recipes || recipes.length === 0) {
log(ctx, `You do not have the resources to craft a ${itemName}. It requires: ${
Object.entries(mc.getItemCraftingRecipes(itemName)[0])
Object.entries(mc.getItemCraftingRecipes(itemName)?.[0] ?? {})
.map(([key, value]) => `${key}: ${value}`)
.join(', ')
}.`)
if (placedTable && craftingTable) {
await bot.collectBlock.collect(craftingTable)
await collectBlock(ctx, 'crafting_table', 1)
}
return false
}
if (craftingTable && bot.entity.position.distanceTo(craftingTable.position) > 4) {
if (craftingTable && ctx.bot.entity.position.distanceTo(craftingTable.position) > 4) {
await goToPosition(ctx, craftingTable.position.x, craftingTable.position.y, craftingTable.position.z, 4)
}
const recipe = recipes[0]
// Check that the agent has sufficient items to use the recipe `num` times
const worldCtx = { bot, botCtx: { bot, botName: bot.username } }
const worldCtx = createWorldContext(ctx.botCtx)
const inventory = world.getInventoryCounts(worldCtx) // Items in the agents inventory
const requiredIngredients = mc.ingredientsFromPrismarineRecipe(recipe) // Items required to use the recipe once
const craftLimit = mc.calculateLimitingResource(inventory, requiredIngredients)
await bot.craft(recipe, Math.min(craftLimit.num, num), craftingTable)
await ctx.bot.craft(recipe, Math.min(craftLimit.num, num), craftingTable ?? undefined)
if (craftLimit.num < num) {
log(ctx, `Not enough ${craftLimit.limitingResource} to craft ${num}, crafted ${craftLimit.num}. You now have ${world.getInventoryCounts(worldCtx)[itemName]} ${itemName}.`)
@@ -96,20 +93,16 @@ export async function craftRecipe(ctx: SkillContext, itemName: string, num = 1):
}
if (placedTable && craftingTable) {
await bot.collectBlock.collect(craftingTable)
await collectBlock(ctx, 'crafting_table', 1)
}
// Equip any armor the bot may have crafted
bot.armorManager.equipAll()
ctx.bot.armorManager.equipAll()
return true
}
/**
* Smelt items in a furnace
*/
export async function smeltItem(ctx: SkillContext, itemName: string, num = 1): Promise<boolean> {
const { bot } = ctx
if (!mc.isSmeltable(itemName)) {
log(ctx, `Cannot smelt ${itemName}. Hint: make sure you are smelting the 'raw' item.`)
return false
@@ -117,7 +110,7 @@ export async function smeltItem(ctx: SkillContext, itemName: string, num = 1): P
let placedFurnace = false
const furnaceRange = 32
const worldCtx = { bot, botCtx: { bot, botName: bot.username } }
const worldCtx = createWorldContext(ctx.botCtx)
let furnaceBlock = world.getNearestBlock(worldCtx, 'furnace', furnaceRange)
if (!furnaceBlock) {
@@ -139,13 +132,13 @@ export async function smeltItem(ctx: SkillContext, itemName: string, num = 1): P
return false
}
if (bot.entity.position.distanceTo(furnaceBlock.position) > 4) {
if (ctx.bot.entity.position.distanceTo(furnaceBlock.position) > 4) {
await goToPosition(ctx, furnaceBlock.position.x, furnaceBlock.position.y, furnaceBlock.position.z, 4)
}
await bot.lookAt(furnaceBlock.position)
await ctx.bot.lookAt(furnaceBlock.position)
const furnace = await bot.openFurnace(furnaceBlock)
const furnace = await ctx.bot.openFurnace(furnaceBlock)
// Check if the furnace is already smelting something
const inputItem = furnace.inputItem()
@@ -158,7 +151,7 @@ export async function smeltItem(ctx: SkillContext, itemName: string, num = 1): P
if (inputItem && inputItem.type !== itemId && inputItem.count > 0) {
log(ctx, `The furnace is currently smelting ${mc.getItemName(inputItem.type) ?? 'unknown'}.`)
if (placedFurnace) {
await bot.collectBlock.collect(furnaceBlock)
await collectBlock(ctx, 'furnace', 1)
}
return false
}
@@ -168,18 +161,18 @@ export async function smeltItem(ctx: SkillContext, itemName: string, num = 1): P
if (!invCounts[itemName] || invCounts[itemName] < num) {
log(ctx, `You do not have enough ${itemName} to smelt.`)
if (placedFurnace) {
await bot.collectBlock.collect(furnaceBlock)
await collectBlock(ctx, 'furnace', 1)
}
return false
}
// Fuel the furnace
if (!furnace.fuelItem()) {
const fuel = mc.getSmeltingFuel(bot)
const fuel = mc.getSmeltingFuel(ctx.bot)
if (!fuel) {
log(ctx, `You have no fuel to smelt ${itemName}, you need coal, charcoal, or wood.`)
if (placedFurnace) {
await bot.collectBlock.collect(furnaceBlock)
await collectBlock(ctx, 'furnace', 1)
}
return false
}
@@ -190,7 +183,7 @@ export async function smeltItem(ctx: SkillContext, itemName: string, num = 1): P
if (fuel.count < putFuel) {
log(ctx, `You don't have enough ${fuel.name} to smelt ${num} ${itemName}; you need ${putFuel}.`)
if (placedFurnace) {
await bot.collectBlock.collect(furnaceBlock)
await collectBlock(ctx, 'furnace', 1)
}
return false
}
@@ -231,10 +224,10 @@ export async function smeltItem(ctx: SkillContext, itemName: string, num = 1): P
}
}
await bot.closeWindow(furnace)
await ctx.bot.closeWindow(furnace)
if (placedFurnace) {
await bot.collectBlock.collect(furnaceBlock)
await collectBlock(ctx, 'furnace', 1)
}
if (total === 0) {
@@ -251,23 +244,19 @@ export async function smeltItem(ctx: SkillContext, itemName: string, num = 1): P
return true
}
/**
* Clear the nearest furnace
*/
export async function clearNearestFurnace(ctx: SkillContext): Promise<boolean> {
const { bot } = ctx
const worldCtx = { bot, botCtx: { bot, botName: bot.username } }
const worldCtx = createWorldContext(ctx.botCtx)
const furnaceBlock = world.getNearestBlock(worldCtx, 'furnace', 32)
if (!furnaceBlock) {
log(ctx, 'No furnace nearby to clear.')
return false
}
if (bot.entity.position.distanceTo(furnaceBlock.position) > 4) {
if (ctx.bot.entity.position.distanceTo(furnaceBlock.position) > 4) {
await goToPosition(ctx, furnaceBlock.position.x, furnaceBlock.position.y, furnaceBlock.position.z, 4)
}
const furnace = await bot.openFurnace(furnaceBlock)
const furnace = await ctx.bot.openFurnace(furnaceBlock)
// Take the items out of the furnace
let smeltedItem, inputItem, fuelItem