Volver al índice
src/main/java/ar/com/companeros/Brain.java
package ar.com.companeros;
import java.util.EnumSet;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
/** Prioridades del bot: seguridad → comida → herramientas → casa → explorar. */
public final class Brain extends Goal {
private final Companion bot;
private String wanted = "wood";
private int lastSearch = -100;
private int approachTicks;
public Brain(Companion bot) { this.bot = bot; setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); }
@Override public boolean canUse() { return bot.home != null && bot.humanPresent() && !bot.mode.equals("quieto"); }
@Override public boolean requiresUpdateEveryTick() { return true; }
@Override public void stop() { bot.getNavigation().stop(); bot.miningProgress = 0; }
@Override public void tick() {
bot.workTicks++;
if (!bot.serverLevel().getEntitiesOfClass(net.minecraft.world.entity.monster.Creeper.class,
bot.getBoundingBox().inflate(6)).isEmpty()) return;
if (bot.mode.equals("seguir")) {
var player = bot.owner == null ? null : bot.serverLevel().getPlayerByUUID(bot.owner);
if (player != null && bot.distanceToSqr(player) > 9) bot.getNavigation().moveTo(player, 1.1);
else bot.getNavigation().stop();
bot.activity = "Acompañando";
return;
}
if (bot.getHealth() <= 7 && hasFood() || bot.serverLevel().isNight()) {
bot.activity = "Descansando";
if (bot.distanceToSqr(net.minecraft.world.phys.Vec3.atCenterOf(bot.home)) > 4)
bot.getNavigation().moveTo(bot.home.getX() + .5, bot.home.getY(), bot.home.getZ() + .5, 1.15);
else bot.getNavigation().stop();
return;
}
// Comer tiene prioridad sobre construir o fabricar.
if (bot.workTicks % 20 == 0 && bot.count(Items.WHEAT) >= 3) Crafting.make(bot, Items.BREAD);
String next = bot.hunger <= 16 && !hasFood() ? "food"
: bot.count(ItemTags.LOGS) + bot.count(ItemTags.PLANKS) < 4 ? "wood"
: bot.count(Items.COBBLESTONE) < 8 && bot.count(ItemTags.PICKAXES) > 0 ? "stone" : "wood";
if (!next.equals(wanted)) { wanted = next; bot.resource = null; bot.miningProgress = 0; }
if (bot.workTicks % 20 == 0 && !wanted.equals("food")) bootstrap();
if (!wanted.equals("food") && bot.building && !Shelter.complete(bot)
&& (bot.count(ItemTags.PLANKS) > 0 || bot.shelterStep == Shelter.blueprint(bot.home).size() - 1
&& bot.count(ItemTags.WOODEN_DOORS) > 0)) {
bot.activity = "Construyendo";
bot.resource = null; bot.miningProgress = 0;
if (bot.workTicks % 10 == 0) Shelter.step(bot);
return;
}
// Tras la casa sigue acumulando una reserva moderada, sin talar sin límite.
if (!wanted.equals("food") && bot.count(ItemTags.PLANKS) >= 64 && bot.count(ItemTags.LOGS) >= 8) {
bot.activity = "Explorando";
if (bot.workTicks % 100 == 0) {
var destination = net.minecraft.world.entity.ai.util.DefaultRandomPos.getPos(bot, 6, 3);
if (destination != null && bot.inArea(net.minecraft.core.BlockPos.containing(destination)))
bot.getNavigation().moveTo(destination.x, destination.y, destination.z, .8);
}
return;
}
if (bot.resource == null && bot.workTicks - lastSearch >= 40) {
lastSearch = bot.workTicks;
bot.resource = WorldActions.find(bot, wanted);
approachTicks = 0;
if (bot.resource == null && wanted.equals("stone")) {
wanted = "wood";
bot.resource = WorldActions.find(bot, wanted);
}
if (bot.resource == null && wanted.equals("food")) hunt();
}
if (bot.resource == null) { bot.activity = wanted.equals("food") ? "Buscando comida" : "Buscando recursos"; return; }
var pos = bot.resource;
bot.activity = wanted.equals("food") ? "Recolectando comida" : wanted.equals("stone") ? "Picando piedra" : "Recolectando madera";
if (bot.position().distanceToSqr(net.minecraft.world.phys.Vec3.atCenterOf(pos)) <= 16) {
bot.getNavigation().stop();
if (WorldActions.mine(bot, pos, wanted)) bot.resource = null;
} else {
approachTicks++;
if (approachTicks % 40 == 1) {
var path = bot.getNavigation().createPath(pos, 2);
if (path == null || !path.canReach()) { WorldActions.block(bot, pos); bot.resource = null; return; }
bot.getNavigation().moveTo(path, 1);
}
if (approachTicks > 300) { WorldActions.block(bot, pos); bot.resource = null; }
}
}
private boolean hasFood() {
for (int i = 0; i < bot.bag.getContainerSize(); i++) {
ItemStack stack = bot.bag.getItem(i);
if (stack.isEdible() && !stack.is(Items.ROTTEN_FLESH) && !stack.is(Items.POISONOUS_POTATO)
&& !stack.is(Items.SPIDER_EYE) && !stack.is(Items.PUFFERFISH)) return true;
}
return false;
}
private void hunt() {
bot.serverLevel().getEntitiesOfClass(net.minecraft.world.entity.animal.Animal.class,
bot.getBoundingBox().inflate(bot.radius), animal -> bot.inArea(animal.blockPosition())
&& !animal.hasCustomName() && !animal.isBaby() && animal.blockPosition().distSqr(bot.home) > 64
&& (animal instanceof net.minecraft.world.entity.animal.Cow
|| animal instanceof net.minecraft.world.entity.animal.Pig
|| animal instanceof net.minecraft.world.entity.animal.Sheep)
&& unfenced(animal.blockPosition())).stream()
.min(java.util.Comparator.comparingDouble(bot::distanceToSqr)).ifPresent(animal -> {
bot.equipBest(ItemTags.SWORDS); bot.setTarget(animal); bot.activity = "Buscando alimento";
});
}
private boolean unfenced(net.minecraft.core.BlockPos center) {
for (var pos : net.minecraft.core.BlockPos.betweenClosed(center.offset(-3, -1, -3), center.offset(3, 2, 3))) {
if (bot.serverLevel().hasChunkAt(pos) && (WorkArea.get(bot.serverLevel()).contains(pos)
|| bot.level().getBlockState(pos).is(net.minecraft.tags.BlockTags.FENCES))) return false;
}
return true;
}
// Las recetas se consultan en el servidor, incluyendo los cambios hechos por el pack.
private void bootstrap() {
if (bot.count(ItemTags.PLANKS) < 16 && bot.count(ItemTags.LOGS) > 0) {
for (var item : net.minecraftforge.registries.ForgeRegistries.ITEMS.tags().getTag(ItemTags.PLANKS)) {
if (Crafting.make(bot, item)) break;
}
}
if (bot.count(Items.STICK) < 4) Crafting.make(bot, Items.STICK);
var table = bot.home.offset(1, 0, 0);
if (!bot.level().getBlockState(table).is(net.minecraft.world.level.block.Blocks.CRAFTING_TABLE)) {
if (bot.count(Items.CRAFTING_TABLE) == 0) Crafting.make(bot, Items.CRAFTING_TABLE);
for (int i = 0; i < bot.bag.getContainerSize(); i++) if (bot.bag.getItem(i).is(Items.CRAFTING_TABLE)) {
WorldActions.place(bot, table, bot.bag.getItem(i)); break;
}
}
if (bot.count(ItemTags.PICKAXES) == 0) Crafting.make(bot, Items.WOODEN_PICKAXE);
if (bot.count(ItemTags.AXES) == 0) Crafting.make(bot, Items.WOODEN_AXE);
if (bot.count(ItemTags.SWORDS) == 0) Crafting.make(bot, Items.WOODEN_SWORD);
if (bot.count(Items.STONE_PICKAXE) == 0) Crafting.make(bot, Items.STONE_PICKAXE);
if (bot.count(Items.STONE_AXE) == 0) Crafting.make(bot, Items.STONE_AXE);
if (bot.count(Items.STONE_SWORD) == 0) Crafting.make(bot, Items.STONE_SWORD);
if (bot.count(Items.WHEAT) >= 3) Crafting.make(bot, Items.BREAD);
if (bot.building && bot.count(ItemTags.WOODEN_DOORS) == 0 && bot.count(ItemTags.PLANKS) >= 6) {
for (var item : net.minecraftforge.registries.ForgeRegistries.ITEMS.tags().getTag(ItemTags.WOODEN_DOORS)) {
if (Crafting.make(bot, item)) break;
}
}
}
}