Smart crafting: walk to table, check materials, report results
Bridge craft_item now: - Fuzzy-matches item names (wooden_pickaxe, wood_pickaxe, etc.) - Searches for crafting table within 32 blocks - Walks to crafting table if found but not close enough - Checks if materials are available - Reports specific failure reasons to chat: "I need a crafting table" / "I don't have the materials" - Reports success: "Done! Crafted 1 wooden pickaxe." Brain now: - Reports craft success/failure to in-game chat - Reports any HIGH priority task failure to chat - Handles craft_item as async (waits for pathfinding + crafting) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
813f8704bb
commit
513507e941
2 changed files with 102 additions and 10 deletions
|
|
@ -647,18 +647,83 @@ async function handleAction(action, params = {}) {
|
|||
case 'craft_item': {
|
||||
const { itemName, count: craftCount } = params;
|
||||
const mcData = require('minecraft-data')(bot.version);
|
||||
const item = mcData.itemsByName[itemName];
|
||||
if (!item) throw new Error(`Unknown item: ${itemName}`);
|
||||
|
||||
// Find crafting table nearby if needed
|
||||
const craftingTable = bot.findBlock({
|
||||
matching: (block) => block.name.includes('crafting_table'),
|
||||
maxDistance: 4,
|
||||
// Try to find item — handle both "wooden_pickaxe" and "planks" style names
|
||||
let item = mcData.itemsByName[itemName];
|
||||
if (!item) {
|
||||
// Try common Bedrock name variants
|
||||
const variants = [
|
||||
itemName,
|
||||
itemName.replace('wooden_', 'wood_'),
|
||||
itemName.replace('wood_', 'wooden_'),
|
||||
`minecraft:${itemName}`,
|
||||
itemName.replace('_', ''),
|
||||
];
|
||||
for (const v of variants) {
|
||||
item = mcData.itemsByName[v];
|
||||
if (item) break;
|
||||
}
|
||||
// Try fuzzy match
|
||||
if (!item) {
|
||||
for (const [name, data] of Object.entries(mcData.itemsByName)) {
|
||||
if (name.includes(itemName) || itemName.includes(name)) {
|
||||
item = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!item) {
|
||||
return { crafted: false, error: `I don't know what "${itemName.replace(/_/g, ' ')}" is.` };
|
||||
}
|
||||
|
||||
// Step 1: Find crafting table (search wider radius)
|
||||
let craftingTable = bot.findBlock({
|
||||
matching: (block) => block.name.includes('crafting_table') || block.name.includes('workbench'),
|
||||
maxDistance: 32,
|
||||
});
|
||||
|
||||
// Step 2: Walk to crafting table if found but not close enough
|
||||
if (craftingTable) {
|
||||
const dist = bot.entity.position.distanceTo(craftingTable.position);
|
||||
if (dist > 4) {
|
||||
log('client', 'INFO', `Walking to crafting table at ${craftingTable.position}`);
|
||||
bot.pathfinder.setGoal(new GoalNear(
|
||||
craftingTable.position.x, craftingTable.position.y, craftingTable.position.z, 2
|
||||
));
|
||||
// Wait for arrival (up to 15 seconds)
|
||||
await new Promise((resolve) => {
|
||||
const check = setInterval(() => {
|
||||
const d = bot.entity.position.distanceTo(craftingTable.position);
|
||||
if (d <= 3) { clearInterval(check); resolve(); }
|
||||
}, 500);
|
||||
setTimeout(() => { clearInterval(check); resolve(); }, 15000);
|
||||
});
|
||||
// Re-find the table now that we're close
|
||||
craftingTable = bot.findBlock({
|
||||
matching: (block) => block.name.includes('crafting_table') || block.name.includes('workbench'),
|
||||
maxDistance: 4,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Check recipes
|
||||
const recipes = bot.recipesFor(item.id, null, null, craftingTable || undefined);
|
||||
if (recipes.length === 0) throw new Error(`No recipe found for ${itemName}`);
|
||||
await bot.craft(recipes[0], craftCount || 1, craftingTable || undefined);
|
||||
return { crafted: itemName, count: craftCount || 1 };
|
||||
if (recipes.length === 0) {
|
||||
const reason = craftingTable
|
||||
? `I don't have the materials to craft ${itemName.replace(/_/g, ' ')}.`
|
||||
: `I need a crafting table to make ${itemName.replace(/_/g, ' ')}.`;
|
||||
return { crafted: false, error: reason };
|
||||
}
|
||||
|
||||
// Step 4: Craft!
|
||||
try {
|
||||
await bot.craft(recipes[0], craftCount || 1, craftingTable || undefined);
|
||||
log('client', 'INFO', `Crafted ${craftCount || 1}x ${itemName}`);
|
||||
return { crafted: true, item: itemName, count: craftCount || 1 };
|
||||
} catch (e) {
|
||||
return { crafted: false, error: `Crafting failed: ${e.message}` };
|
||||
}
|
||||
}
|
||||
|
||||
// --- Use/Activate Block ---
|
||||
|
|
|
|||
|
|
@ -214,6 +214,29 @@ class DougBrain(QObject):
|
|||
|
||||
def on_response(resp: ResponseMessage):
|
||||
if resp.status == "success":
|
||||
data = resp.data or {}
|
||||
|
||||
# Handle craft results specifically
|
||||
if task.action == "craft_item":
|
||||
self._waiting_for_action = False
|
||||
if data.get("crafted"):
|
||||
item = data.get("item", "item").replace("_", " ")
|
||||
self._ws.send_request("send_chat", {
|
||||
"message": f"Done! Crafted {data.get('count', 1)} {item}."
|
||||
})
|
||||
self._tasks.complete()
|
||||
else:
|
||||
error = data.get("error", "Something went wrong.")
|
||||
self._ws.send_request("send_chat", {"message": error})
|
||||
self._tasks.cancel()
|
||||
return
|
||||
|
||||
# Handle other results with error messages
|
||||
if task.action in ("open_chest", "dig_block", "equip_item"):
|
||||
self._waiting_for_action = False
|
||||
self._tasks.complete()
|
||||
return
|
||||
|
||||
# For non-movement actions, complete immediately
|
||||
if task.action not in ("move_to", "move_relative", "follow_player"):
|
||||
self._waiting_for_action = False
|
||||
|
|
@ -221,7 +244,11 @@ class DougBrain(QObject):
|
|||
else:
|
||||
self._waiting_for_action = False
|
||||
self._tasks.cancel()
|
||||
log.debug(f"Action failed: {resp.error}")
|
||||
error = resp.error or "Something went wrong"
|
||||
log.debug(f"Action failed: {error}")
|
||||
# Report failure to chat for player-initiated tasks
|
||||
if task.priority >= Priority.HIGH:
|
||||
self._ws.send_request("send_chat", {"message": error})
|
||||
|
||||
self._ws.send_request(task.action, task.params, on_response)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue