/** * combat_bot.js — Full combat-capable bot using mineflayer plugins. * * Uses: * mineflayer-pvp — combat AI (attack, strafe, shield) * mineflayer-pathfinder — A* navigation * mineflayer-armor-manager — auto-equip best armor * mineflayer-auto-eat — auto-eat when hungry/hurt * * The bot autonomously fights, eats, equips armor, and pathfinds. * AI frames via stdin set high-level goals. */ const mineflayer = require('mineflayer'); const pathfinder = require('mineflayer-pathfinder'); const pvp = require('mineflayer-pvp').plugin; const armorManager = require('mineflayer-armor-manager'); const autoEat = require('mineflayer-auto-eat').loader; const { GoalNear, GoalFollow, GoalBlock } = require('mineflayer-pathfinder').goals; const readline = require('readline'); const host = process.argv[2] || '192.168.0.244'; const port = parseInt(process.argv[3] || '25567', 10); const bot = mineflayer.createBot({ host, port, username: 'ClaudeBot', auth: 'offline', version: '1.21.11', }); // Load plugins bot.loadPlugin(pathfinder.pathfinder); bot.loadPlugin(pvp); bot.loadPlugin(armorManager); bot.loadPlugin(autoEat); let deaths = 0; let kills = 0; let alive = false; const HOSTILE = new Set([ 'zombie', 'husk', 'skeleton', 'creeper', 'spider', 'cave_spider', 'witch', 'enderman', 'pillager', 'vindicator', 'phantom', 'drowned', 'stray', 'blaze', 'ghast', 'slime', 'magma_cube', 'parched', 'camel_husk', ]); // ── Spawn setup ── bot.once('spawn', () => { alive = true; deaths++; // Configure pathfinder const mcData = require('minecraft-data')(bot.version); const movements = new pathfinder.Movements(bot, mcData); movements.allowSprinting = true; movements.canDig = false; // Don't destroy blocks bot.pathfinder.setMovements(movements); // Configure auto-eat bot.autoEat.options = { priority: 'foodPoints', startAt: 16, bannedFood: [], }; out({ event: 'ready', msg: 'Plugins loaded. Combat-ready.' }); setTimeout(() => out(observe()), 3000); }); bot.on('spawn', () => { if (deaths > 1) { alive = true; out({ event: 'respawn', deaths }); setTimeout(() => out(observe()), 3000); } }); // ── Combat events ── bot.on('entityHurt', (entity) => { if (entity === bot.entity) { // I'm being hit — find attacker and fight back const attacker = nearestHostile(8); if (attacker && !bot.pvp.target) { out({ event: 'counter_attack', target: attacker.name || attacker.type, dist: Math.round(attacker.position.distanceTo(bot.entity.position)) }); bot.pvp.attack(attacker); } } }); bot.on('playerCollect', (collector, collected) => { if (collector === bot.entity) { out({ event: 'pickup', item: collected.name || 'item' }); } }); bot.on('death', () => { alive = false; deaths++; out({ event: 'death', deaths, kills }); setTimeout(() => { try { bot.respawn(); } catch (e) {} }, 2000); }); bot.on('chat', (username, message) => { if (username === bot.username) return; out({ event: 'chat', from: username, msg: message }); }); bot.on('kicked', (reason) => { out({ event: 'kicked', reason: typeof reason === 'object' ? JSON.stringify(reason) : String(reason) }); process.exit(1); }); // Track kills bot.on('entityDead', (entity) => { const name = (entity.name || entity.type || '').toLowerCase(); if (HOSTILE.has(name)) { kills++; out({ event: 'kill', target: name, totalKills: kills }); } }); // ── Autonomous combat loop ── setInterval(() => { if (!alive || !bot.entity) return; const hp = bot.health; const nearest = nearestHostile(16); // If low HP and being attacked, flee if (hp < 6 && nearest && nearest.position.distanceTo(bot.entity.position) < 6) { bot.pvp.stop(); fleeSmart(nearest); return; } // If hostile nearby and not already fighting, engage if (nearest && !bot.pvp.target) { const dist = nearest.position.distanceTo(bot.entity.position); if (dist < 10) { bot.pvp.attack(nearest); } } }, 1000); // ── Periodic status report ── setInterval(() => { if (!alive) return; out(observe()); }, 6000); // ── Helper functions ── function nearestHostile(maxDist) { if (!bot.entity) return null; let nearest = null; let nearestDist = maxDist || 16; for (const e of Object.values(bot.entities)) { if (e === bot.entity) continue; const name = (e.name || e.type || '').toLowerCase(); if (!HOSTILE.has(name)) continue; const d = e.position.distanceTo(bot.entity.position); if (d < nearestDist) { nearestDist = d; nearest = e; } } return nearest; } function fleeSmart(from) { if (!from || !bot.entity) return; const pos = bot.entity.position; const dx = pos.x - from.position.x; const dz = pos.z - from.position.z; const len = Math.sqrt(dx * dx + dz * dz) || 1; const fleeX = pos.x + (dx / len) * 30; const fleeZ = pos.z + (dz / len) * 30; try { bot.pathfinder.setGoal(new GoalNear(fleeX, pos.y, fleeZ, 2)); } catch (e) { // Fallback: just sprint bot.setControlState('forward', true); bot.setControlState('sprint', true); setTimeout(() => { bot.setControlState('forward', false); bot.setControlState('sprint', false); }, 3000); } } function observe() { if (!bot.entity) return { alive: false }; const pos = bot.entity.position; const mobs = []; for (const e of Object.values(bot.entities)) { if (e === bot.entity) continue; const d = Math.round(e.position.distanceTo(pos)); if (d > 24) continue; const name = e.name || e.type; const hostile = HOSTILE.has((name || '').toLowerCase()); if (e.type === 'player') { mobs.push({ type: 'player', name: e.username, dist: d }); } else if (hostile || d < 10) { mobs.push({ type: name, dist: d, hostile }); } } return { pos: { x: Math.floor(pos.x), y: Math.floor(pos.y), z: Math.floor(pos.z) }, hp: Math.round(bot.health * 10) / 10, food: bot.food, day: (bot.time?.timeOfDay || 0) < 13000, inv: bot.inventory.items().map(i => `${i.name}x${i.count}`).join(', ') || 'empty', armor: [5, 6, 7, 8].map(s => bot.inventory.slots[s]?.name).filter(Boolean).join(', ') || 'none', weapon: bot.heldItem?.name || 'fist', fighting: bot.pvp.target ? (bot.pvp.target.name || bot.pvp.target.type) : null, mobs: mobs.sort((a, b) => a.dist - b.dist).slice(0, 8), kills, deaths, }; } function out(obj) { console.log(JSON.stringify(obj)); } // ── AI Frame commands ── const rl = readline.createInterface({ input: process.stdin }); rl.on('line', (line) => { const cmd = line.trim(); if (cmd === 'look') out(observe()); else if (cmd === 'hunt') { // Actively seek and kill nearest hostile const target = nearestHostile(30); if (target) { bot.pvp.attack(target); out({ action: 'hunting', target: target.name || target.type, dist: Math.round(target.position.distanceTo(bot.entity.position)) }); } else { out({ action: 'hunt', msg: 'no hostiles nearby' }); } } else if (cmd === 'stop') { bot.pvp.stop(); bot.pathfinder.stop(); out({ action: 'stopped' }); } else if (cmd.startsWith('goto ')) { const [, x, y, z] = cmd.split(' ').map(Number); bot.pathfinder.setGoal(new GoalNear(x, y, z, 2)); out({ action: 'navigating', target: { x, y, z } }); } else if (cmd.startsWith('follow ')) { const name = cmd.slice(7); const player = bot.players[name]; if (player?.entity) { bot.pathfinder.setGoal(new GoalFollow(player.entity, 3), true); out({ action: 'following', target: name }); } } else if (cmd.startsWith('pray ')) { bot.chat(cmd); out({ action: 'pray' }); } else if (cmd.startsWith('sudo ')) { bot.chat(cmd); out({ action: 'sudo' }); } else if (cmd.startsWith('say ')) { bot.chat(cmd.slice(4)); } else if (cmd === 'quit') { bot.end(); process.exit(0); } else { out({ commands: ['look', 'hunt', 'stop', 'goto X Y Z', 'follow PLAYER', 'pray MSG', 'sudo CMD', 'say MSG', 'quit'] }); } });