'use strict'; const test = require('node:test'); const assert = require('node:assert'); const { Vec3 } = require('vec3'); const { FakeBot } = require('./helpers/fakeBot'); const { isAttackTarget, findTarget, eyeToEntityDistance, findLowDurabilitySlot, wearsGoldArmor } = require('../src/bot/autoCombat'); const { defaultConfig } = require('../src/config/expansionConfig'); /** 构造一个假实体 */ function makeEntity(name, kind, position, extra) { return Object.assign({ id: Math.floor(Math.random() * 100000), name, kind, type: 'mob', position: new Vec3(position[0], position[1], position[2]), width: 0.6, height: 1.8, isValid: true }, extra || {}); } test('敌对生物可被攻击', () => { const bot = new FakeBot(); const config = defaultConfig(); assert.strictEqual(isAttackTarget(makeEntity('zombie', 'Hostile mobs', [0, 64, 0]), bot, config), true); assert.strictEqual(isAttackTarget(makeEntity('skeleton', 'Hostile mobs', [0, 64, 0]), bot, config), true); assert.strictEqual(isAttackTarget(makeEntity('creeper', 'Hostile mobs', [0, 64, 0]), bot, config), true); }); test('绝不攻击玩家与中立生物', () => { const bot = new FakeBot(); const config = defaultConfig(); // 玩家 assert.strictEqual(isAttackTarget(makeEntity('Steve', 'Passive mobs', [0, 64, 0], { type: 'player' }), bot, config), false); // 铁傀儡、僵尸猪灵(原版中立,绝不主动攻击) assert.strictEqual(isAttackTarget(makeEntity('iron_golem', 'Passive mobs', [0, 64, 0]), bot, config), false); assert.strictEqual(isAttackTarget(makeEntity('zombified_piglin', 'Hostile mobs', [0, 64, 0]), bot, config), false); // 宠物与常见中立生物 for (const name of ['wolf', 'cat', 'llama', 'bee', 'fox', 'polar_bear', 'villager']) { assert.strictEqual(isAttackTarget(makeEntity(name, 'Passive mobs', [0, 64, 0]), bot, config), false, name + ' 不应被攻击'); } }); test('条件敌对生物默认不打,可在配置里放开', () => { const bot = new FakeBot(); const config = defaultConfig(); assert.strictEqual(isAttackTarget(makeEntity('enderman', 'Hostile mobs', [0, 64, 0]), bot, config), false); assert.strictEqual(isAttackTarget(makeEntity('spider', 'Hostile mobs', [0, 64, 0]), bot, config), false); config.combat.attackEnderman = true; config.combat.attackSpiders = true; assert.strictEqual(isAttackTarget(makeEntity('enderman', 'Hostile mobs', [0, 64, 0]), bot, config), true); assert.strictEqual(isAttackTarget(makeEntity('spider', 'Hostile mobs', [0, 64, 0]), bot, config), true); }); test('猪灵:未穿金甲才攻击', () => { const config = defaultConfig(); const bareBot = new FakeBot(); assert.strictEqual(isAttackTarget(makeEntity('piglin', 'Hostile mobs', [0, 64, 0]), bareBot, config), true); const armoredBot = new FakeBot({ slots: [] }); armoredBot.inventory.slots[5] = { name: 'golden_helmet' }; assert.strictEqual(wearsGoldArmor(armoredBot), true); assert.strictEqual(isAttackTarget(makeEntity('piglin', 'Hostile mobs', [0, 64, 0]), armoredBot, config), false); }); test('高危生物默认跳过', () => { const bot = new FakeBot(); const config = defaultConfig(); for (const name of ['warden', 'ender_dragon', 'wither']) { assert.strictEqual(isAttackTarget(makeEntity(name, 'Hostile mobs', [0, 64, 0]), bot, config), false, name + ' 应被跳过'); } config.combat.avoidDangerousMobs = false; assert.strictEqual(isAttackTarget(makeEntity('wither', 'Hostile mobs', [0, 64, 0]), bot, config), true); }); test('无效 / 已消失 / 非生物实体不被攻击', () => { const bot = new FakeBot(); const config = defaultConfig(); assert.strictEqual(isAttackTarget(null, bot, config), false); assert.strictEqual(isAttackTarget(makeEntity('zombie', 'Hostile mobs', [0, 64, 0], { isValid: false }), bot, config), false); assert.strictEqual(isAttackTarget(makeEntity('zombie', 'Hostile mobs', [0, 64, 0], { type: 'object' }), bot, config), false); assert.strictEqual(isAttackTarget(bot.entity, bot, config), false); }); test('findTarget 在索敌半径内挑最近的合法目标', () => { const bot = new FakeBot({ x: 0, y: 64, z: 0 }); const config = defaultConfig(); const near = makeEntity('zombie', 'Hostile mobs', [1, 64, 0]); const far = makeEntity('skeleton', 'Hostile mobs', [4, 64, 0]); const neutral = makeEntity('iron_golem', 'Passive mobs', [0.5, 64, 0]); const outside = makeEntity('creeper', 'Hostile mobs', [20, 64, 0]); bot.entities = { 1: near, 2: far, 3: neutral, 4: outside }; assert.strictEqual(findTarget(bot, 4.5, config), near); }); test('findTarget 半径内没有敌对生物时返回 null', () => { const bot = new FakeBot({ x: 0, y: 64, z: 0 }); const config = defaultConfig(); bot.entities = { 1: makeEntity('cow', 'Passive mobs', [1, 64, 0]) }; assert.strictEqual(findTarget(bot, 4.5, config), null); }); test('eyeToEntityDistance 计算到碰撞箱最近点的距离', () => { const bot = new FakeBot({ x: 0, y: 64, z: 0 }); const entity = makeEntity('zombie', 'Hostile mobs', [2, 64, 0]); const distance = eyeToEntityDistance(bot, entity); assert.ok(Math.abs(distance - 1.7) < 1e-6, `期望 1.7,实际 ${distance}`); }); test('耐久保护:识别剩余耐久低于阈值的装备', () => { const bot = new FakeBot({ slots: [] }); // 主手:钻石剑,最大 1561,已用 1400 → 剩余 10.3% bot.heldItem = { name: 'diamond_sword', maxDurability: 1561, durabilityUsed: 1400 }; const low = findLowDurabilitySlot(bot, 15); assert.ok(low); assert.strictEqual(low.name, '主手'); assert.strictEqual(low.percent, 10); assert.strictEqual(low.itemName, 'diamond_sword'); }); test('耐久保护:装备充足时返回 null', () => { const bot = new FakeBot({ slots: [] }); bot.heldItem = { name: 'diamond_sword', maxDurability: 1561, durabilityUsed: 100 }; bot.inventory.slots[5] = { name: 'diamond_helmet', maxDurability: 363, durabilityUsed: 10 }; assert.strictEqual(findLowDurabilitySlot(bot, 15), null); }); test('耐久保护:检查四件盔甲与副手', () => { const bot = new FakeBot({ slots: [] }); bot.inventory.slots[7] = { name: 'diamond_leggings', maxDurability: 495, durabilityUsed: 480 }; const low = findLowDurabilitySlot(bot, 15); assert.ok(low); assert.strictEqual(low.name, '护腿'); }); test('耐久保护:不可损坏物品被忽略', () => { const bot = new FakeBot({ slots: [] }); bot.heldItem = { name: 'stone' }; bot.inventory.slots[5] = { name: 'turtle_helmet', maxDurability: 0 }; assert.strictEqual(findLowDurabilitySlot(bot, 90), null); });