feat: Implement FarmingState class for turtle state machine to automate farming operations
This commit is contained in:
124
server/states/FarmingState.js
Normal file
124
server/states/FarmingState.js
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* 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,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user