// states/Idle.js const { getBot, getActiveTask, isCombatLocked, getStateMachine } = require('../core/context'); const context = require('../core/context'); const { chatWithAI } = require('../lib/ai-helper'); const config = require('../config.json'); const { GoalNear } = require('mineflayer-pathfinder').goals; const { getHomeZone } = require('../memory/locations'); const db = require('../db'); module.exports = async function Idle() { const bot = getBot(); if (getActiveTask() || isCombatLocked()) { console.log('[IDLE] Skipping — locked by task or combat.'); return; } console.log('[STATE] Idle'); return new Promise(resolve => { // Follow and observe a player that is close by. This should eventually become a method to help the player. const players = Object.values(bot.players).filter(p => p.username !== bot.username && p.entity); if (players.length > 0) { const target = players[0].entity; const distance = bot.entity.position.distanceTo(target.position); if (distance > 5 || distance < 2) { bot.chat(`Following ${players[0].username}...`); bot.pathfinder.setGoal(new GoalNear(target.position.x, target.position.y, target.position.z, 2)); } setTimeout(() => { getStateMachine().transition('Observe'); resolve(); }, 10000); return; } getHomeZone(db, async (err, zone) => { const fallbackCenter = { x: 100, y: 64, z: 100 }; const fallbackBounds = { x: 20, y: 10, z: 20 }; const center = zone?.center || fallbackCenter; const bounds = zone?.bounds || fallbackBounds; const safeRadius = Math.min(bounds.x, bounds.z); const prompt = `You're in a safe zone. What would be a useful task to do right now? Your options are: cut grass, tend crops, patrol for mobs.`; const response = await chatWithAI(prompt, config.ai); // Randomize an action to do things around the home area. if (response.includes('grass')) { const grass = bot.findBlock({ matching: block => block.name === 'tall_grass', maxDistance: safeRadius }); // Cut grass if (grass) { await bot.pathfinder.setGoal(new GoalNear(grass.position.x, grass.position.y, grass.position.z, 1)); try { await bot.dig(grass); bot.chat("Trimming grass."); } catch {} } // manage a farm by harvesting crops. } else if (response.includes('crop') || response.includes('farm')) { const crops = bot.findBlocks({ matching: block => ['wheat', 'carrots', 'potatoes'].includes(block.name), maxDistance: safeRadius, count: 5 }); for (const pos of crops) { const crop = bot.blockAt(pos); if (crop.metadata === 7) { await bot.pathfinder.setGoal(new GoalNear(pos.x, pos.y, pos.z, 1)); try { await bot.dig(crop); bot.chat("Harvesting a crop."); // TODO: Replanting logic needs to be added here. This might be a state or a task.. not sure yet. } catch {} break; } } // Find and attack mobs. } else if (response.includes('mob') || response.includes('patrol')) { const mob = Object.values(bot.entities).find(e => e.type === 'mob' && e.position.distanceTo(bot.entity.position) <= safeRadius && e.username !== bot.username ); if (mob) { context.setCombatLock(true); bot.chat(`Engaging ${mob.name}.`); bot.attack(mob); bot.once('death', () => { context.setCombatLock(false); bot.chat("I died. Lock released."); }); // Monitor mobs and release combat lock when clear const checkClear = setInterval(() => { const nearbyMobs = Object.values(bot.entities).filter(e => e.type === 'mob'); if (nearbyMobs.length === 0) { context.setCombatLock(false); clearInterval(checkClear); bot.chat("Area is clear."); } }, 3000); } else { const dx = Math.floor(Math.random() * safeRadius * 2 - safeRadius); const dz = Math.floor(Math.random() * safeRadius * 2 - safeRadius); const pos = bot.entity.position.offset(dx, 0, dz); bot.pathfinder.setGoal(new GoalNear(pos.x, pos.y, pos.z, 1)); } } setTimeout(() => { getStateMachine().transition('Observe'); resolve(); }, 10000 ); }); }); };