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