125 lines
3.3 KiB
JavaScript
125 lines
3.3 KiB
JavaScript
/**
|
|
* FarmingState - Automated farming (plant, harvest, replant)
|
|
* Works with wheat, carrots, potatoes, beetroot, etc.
|
|
*/
|
|
import { BaseState } from './BaseState.js';
|
|
|
|
const CROP_SEEDS = {
|
|
'minecraft:wheat': 'minecraft:wheat_seeds',
|
|
'minecraft:carrots': 'minecraft:carrot',
|
|
'minecraft:potatoes': 'minecraft:potato',
|
|
'minecraft:beetroots': 'minecraft:beetroot_seeds',
|
|
};
|
|
|
|
export class FarmingState extends BaseState {
|
|
constructor(turtle, data = {}) {
|
|
super(turtle, data);
|
|
this.area = data.area || null; // {minX, minZ, maxX, maxZ, y}
|
|
this.harvestCount = 0;
|
|
}
|
|
|
|
get name() {
|
|
return 'farming';
|
|
}
|
|
|
|
get description() {
|
|
return `Farming - ${this.harvestCount} harvested`;
|
|
}
|
|
|
|
async *act() {
|
|
console.log(`[${this.turtle.id}] Starting farming operation`);
|
|
|
|
if (!this.area) {
|
|
console.log(`[${this.turtle.id}] No farming area defined`);
|
|
this.turtle.setState('idle');
|
|
return;
|
|
}
|
|
|
|
// Farm in a zigzag pattern over the area
|
|
const { minX, minZ, maxX, maxZ, y } = this.area;
|
|
let direction = 1; // 1 = positive Z, -1 = negative Z
|
|
|
|
for (let x = minX; x <= maxX; x++) {
|
|
const zStart = direction === 1 ? minZ : maxZ;
|
|
const zEnd = direction === 1 ? maxZ : minZ;
|
|
const zStep = direction;
|
|
|
|
for (let z = zStart; direction === 1 ? z <= zEnd : z >= zEnd; z += zStep) {
|
|
if (this.cancelled) return;
|
|
|
|
// Navigate to position above the crop
|
|
const target = { x, y: y + 1, z };
|
|
const navGen = this.navigateTo(target);
|
|
for await (const _ of navGen) {
|
|
if (this.cancelled) return;
|
|
yield;
|
|
}
|
|
|
|
// Check the block below
|
|
const blockBelow = await this.exec(`
|
|
local hasBlock, data = turtle.inspectDown()
|
|
if hasBlock then
|
|
return {name = data.name, state = data.state}
|
|
end
|
|
return nil
|
|
`);
|
|
|
|
if (blockBelow) {
|
|
// Check if crop is mature (age = 7 for most crops)
|
|
const isMature = blockBelow.state && blockBelow.state.age === 7;
|
|
|
|
if (isMature) {
|
|
// Harvest
|
|
await this.exec('turtle.digDown()');
|
|
this.harvestCount++;
|
|
|
|
// Replant
|
|
const seedName = CROP_SEEDS[blockBelow.name];
|
|
if (seedName) {
|
|
await this.exec(`
|
|
for slot = 1, 16 do
|
|
local item = turtle.getItemDetail(slot)
|
|
if item and item.name == "${seedName}" then
|
|
turtle.select(slot)
|
|
turtle.placeDown()
|
|
turtle.select(1)
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
`);
|
|
}
|
|
}
|
|
}
|
|
|
|
yield;
|
|
}
|
|
|
|
direction *= -1; // Zigzag
|
|
}
|
|
|
|
// Done farming one pass - check inventory
|
|
const isFull = await this.isInventoryFull();
|
|
if (isFull) {
|
|
this.turtle.setState('goHome', {
|
|
reason: 'inventory_full',
|
|
returnState: 'farming',
|
|
returnData: this.data
|
|
});
|
|
} else {
|
|
// Start another pass
|
|
console.log(`[${this.turtle.id}] Farm pass complete, ${this.harvestCount} harvested`);
|
|
yield* this.act(); // Loop
|
|
}
|
|
}
|
|
|
|
getRecoveryData() {
|
|
return {
|
|
...super.getRecoveryData(),
|
|
data: {
|
|
area: this.area,
|
|
},
|
|
};
|
|
}
|
|
}
|