Загрузка данных


package releon.ru.api.module.impl.combat.aura.target;

import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import releon.ru.api.module.impl.combat.AntiBot;
import releon.ru.api.module.impl.combat.AuraModule;
import releon.ru.api.module.impl.combat.aura.AngleConnection;
import releon.ru.api.module.impl.combat.aura.MathAngle;
import releon.ru.api.module.impl.combat.aura.impl.LinearConstructor;
import releon.ru.utils.repository.friend.FriendUtils;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;

public class TargetFinder {
    private final MultiPoint pointFinder = new MultiPoint();
    private LivingEntity currentTarget;
    private List<LivingEntity> potentialTargets = List.of();

    public LivingEntity getCurrentTarget() {
        return currentTarget;
    }

    public void releaseTarget() {
        currentTarget = null;
    }

    public void validateTarget(Predicate<LivingEntity> predicate) {
        if (currentTarget != null && predicate.test(currentTarget) && potentialTargets.contains(currentTarget)) {
            return;
        }
        LivingEntity nextTarget = findFirstMatch(predicate).orElse(null);
        if (nextTarget == null) {
            if (currentTarget != null && !predicate.test(currentTarget)) {
                releaseTarget();
            }
            return;
        }
        currentTarget = nextTarget;
    }

    public void searchTargets(Iterable<Entity> entities, float maxDistance, float maxFov, boolean ignoreWalls) {
        searchTargets(entities, maxDistance, maxFov, ignoreWalls, entity -> true);
    }

    public void searchTargets(Iterable<Entity> entities, float maxDistance, float maxFov, boolean ignoreWalls,
                              Predicate<LivingEntity> predicate) {
        SortMode sortMode = resolveSortMode();
        boolean needsFov = maxFov < 360.0F || sortMode == SortMode.FOV || sortMode == SortMode.COMBINED;
        if (currentTarget != null
                && (!predicate.test(currentTarget)
                || !pointFinder.hasValidPoint(currentTarget, maxDistance, ignoreWalls)
                || (needsFov && getFov(currentTarget, maxDistance, ignoreWalls) > maxFov))) {
            releaseTarget();
        }
        potentialTargets = createTargets(entities, maxDistance, maxFov, ignoreWalls, predicate, sortMode, needsFov);
    }

    private double getFov(LivingEntity entity, float maxDistance, boolean ignoreWalls) {
        Vec3 attackVector = pointFinder.computeVector(entity, maxDistance, AngleConnection.INSTANCE.getRotation(), new LinearConstructor().randomValue(), ignoreWalls).getA();
        return RaycastAngle.rayTrace(maxDistance, entity.getBoundingBox())
                ? 0
                : AngleConnection.computeRotationDifference(MathAngle.cameraAngle(), MathAngle.calculateAngle(attackVector));
    }

    private List<LivingEntity> createTargets(Iterable<Entity> entities, float maxDistance, float maxFov,
                                             boolean ignoreWalls, Predicate<LivingEntity> predicate,
                                             SortMode sortMode, boolean needsFov) {
        List<TargetCandidate> candidates = new ArrayList<>();
        Minecraft client = Minecraft.getInstance();
        if (client.player == null) {
            return List.of();
        }
        double maxDistanceSqr = maxDistance * maxDistance;
        for (Entity entity : entities) {
            if (!(entity instanceof LivingEntity living)) {
                continue;
            }
            if (!predicate.test(living) || living.distanceToSqr(client.player) > maxDistanceSqr) {
                continue;
            }
            if (!pointFinder.hasValidPoint(living, maxDistance, ignoreWalls)) {
                continue;
            }
            double fov = 0.0D;
            if (needsFov) {
                fov = getFov(living, maxDistance, ignoreWalls);
                if (fov >= maxFov) {
                    continue;
                }
            }
            candidates.add(new TargetCandidate(living, living.distanceTo(client.player), living.getHealth() + living.getAbsorptionAmount(), living.getArmorValue(), fov));
        }
        PriorityBounds bounds = PriorityBounds.of(candidates);
        candidates.sort(getComparator(sortMode, bounds));
        List<LivingEntity> result = new ArrayList<>(candidates.size());
        for (TargetCandidate candidate : candidates) {
            result.add(candidate.entity());
        }
        return result;
    }

    private Optional<LivingEntity> findFirstMatch(Predicate<LivingEntity> predicate) {
        return potentialTargets.stream().filter(predicate).findFirst();
    }

    private SortMode resolveSortMode() {
        AuraModule aura = AuraModule.getInstance();
        if (aura == null || aura.getTargetPriority() == null) {
            return SortMode.DISTANCE;
        }
        return switch (aura.getTargetPriority().getValue()) {
            case "Health" -> SortMode.HEALTH;
            case "Armor" -> SortMode.ARMOR;
            case "FOV" -> SortMode.FOV;
            case "Combined" -> SortMode.COMBINED;
            default -> SortMode.DISTANCE;
        };
    }

    private Comparator<TargetCandidate> getComparator(SortMode sortMode, PriorityBounds bounds) {
        return switch (sortMode) {
            case HEALTH -> Comparator.<TargetCandidate>comparingDouble(TargetCandidate::health)
                    .thenComparing(Comparator.comparingInt(TargetCandidate::armor).reversed())
                    .thenComparingDouble(TargetCandidate::distance)
                    .thenComparingDouble(TargetCandidate::fov);
            case ARMOR -> Comparator.<TargetCandidate>comparingInt(TargetCandidate::armor).reversed()
                    .thenComparingDouble(TargetCandidate::health)
                    .thenComparingDouble(TargetCandidate::distance)
                    .thenComparingDouble(TargetCandidate::fov);
            case FOV -> Comparator.<TargetCandidate>comparingDouble(TargetCandidate::fov)
                    .thenComparingDouble(TargetCandidate::distance)
                    .thenComparingDouble(TargetCandidate::health)
                    .thenComparing(Comparator.comparingInt(TargetCandidate::armor).reversed());
            case COMBINED -> Comparator.<TargetCandidate>comparingDouble(bounds::combinedScore)
                    .thenComparingDouble(TargetCandidate::distance)
                    .thenComparingDouble(TargetCandidate::health)
                    .thenComparing(Comparator.comparingInt(TargetCandidate::armor).reversed())
                    .thenComparingDouble(TargetCandidate::fov);
            case DISTANCE -> Comparator.comparingDouble(TargetCandidate::distance);
        };
    }

    public enum SortMode {
        DISTANCE,
        HEALTH,
        ARMOR,
        FOV,
        COMBINED
    }

    private record TargetCandidate(LivingEntity entity, float distance, float health, int armor, double fov) {
    }

    private record PriorityBounds(float minDistance, float maxDistance, float minHealth, float maxHealth, int minArmor, int maxArmor, double minFov, double maxFov) {
        private static PriorityBounds of(List<TargetCandidate> candidates) {
            if (candidates.isEmpty()) {
                return new PriorityBounds(0, 0, 0, 0, 0, 0, 0, 0);
            }
            float minDistance = Float.MAX_VALUE;
            float maxDistance = -Float.MAX_VALUE;
            float minHealth = Float.MAX_VALUE;
            float maxHealth = -Float.MAX_VALUE;
            int minArmor = Integer.MAX_VALUE;
            int maxArmor = Integer.MIN_VALUE;
            double minFov = Double.MAX_VALUE;
            double maxFov = -Double.MAX_VALUE;
            for (TargetCandidate candidate : candidates) {
                minDistance = Math.min(minDistance, candidate.distance());
                maxDistance = Math.max(maxDistance, candidate.distance());
                minHealth = Math.min(minHealth, candidate.health());
                maxHealth = Math.max(maxHealth, candidate.health());
                minArmor = Math.min(minArmor, candidate.armor());
                maxArmor = Math.max(maxArmor, candidate.armor());
                minFov = Math.min(minFov, candidate.fov());
                maxFov = Math.max(maxFov, candidate.fov());
            }
            return new PriorityBounds(minDistance, maxDistance, minHealth, maxHealth, minArmor, maxArmor, minFov, maxFov);
        }

        private double combinedScore(TargetCandidate candidate) {
            return normalize(candidate.distance(), minDistance, maxDistance)
                    + normalize(candidate.health(), minHealth, maxHealth)
                    + 1.0 - normalize(candidate.armor(), minArmor, maxArmor)
                    + normalize(candidate.fov(), minFov, maxFov);
        }

        private double normalize(double value, double min, double max) {
            return Double.compare(max, min) == 0 ? 0.0 : (value - min) / (max - min);
        }
    }

    public static class EntityFilter {
        private final java.util.Set<String> targetSettings;

        public EntityFilter(java.util.Set<String> targetSettings) {
            this.targetSettings = targetSettings;
        }

        public boolean isValid(LivingEntity entity) {
            Minecraft client = Minecraft.getInstance();
            if (client.player == null || entity == client.player || !entity.isAlive() || entity.getHealth() <= 0.0F) {
                return false;
            }
            if (entity.isInvisible() && !targetSettings.contains("Invisible")) {
                return false;
            }
            if (entity instanceof Player player) {
                if (AntiBot.shouldIgnore(player)) {
                    return false;
                }
                return FriendUtils.isFriend(player) ? targetSettings.contains("Friends") : targetSettings.contains("Players");
            }
            if (entity instanceof Animal) {
                return targetSettings.contains("Animals");
            }
            if (entity instanceof Mob) {
                return targetSettings.contains("Mobs");
            }
            if (entity instanceof ArmorStand) {
                return targetSettings.contains("Armor Stands");
            }
            return false;
        }
    }
}