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; } } } }