package net.minecraft.world.entity.ai.sensing; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiPredicate; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.memory.MemoryModuleType; import net.minecraft.world.entity.ai.targeting.TargetingConditions; public abstract class Sensor { private static final RandomSource RANDOM = RandomSource.createThreadSafe(); private static final int DEFAULT_SCAN_RATE = 20; private static final int DEFAULT_TARGETING_RANGE = 16; private static final TargetingConditions TARGET_CONDITIONS = TargetingConditions.forNonCombat().range(16.0); private static final TargetingConditions TARGET_CONDITIONS_IGNORE_INVISIBILITY_TESTING = TargetingConditions.forNonCombat() .range(16.0) .ignoreInvisibilityTesting(); private static final TargetingConditions ATTACK_TARGET_CONDITIONS = TargetingConditions.forCombat().range(16.0); private static final TargetingConditions ATTACK_TARGET_CONDITIONS_IGNORE_INVISIBILITY_TESTING = TargetingConditions.forCombat() .range(16.0) .ignoreInvisibilityTesting(); private static final TargetingConditions ATTACK_TARGET_CONDITIONS_IGNORE_LINE_OF_SIGHT = TargetingConditions.forCombat().range(16.0).ignoreLineOfSight(); private static final TargetingConditions ATTACK_TARGET_CONDITIONS_IGNORE_INVISIBILITY_AND_LINE_OF_SIGHT = TargetingConditions.forCombat() .range(16.0) .ignoreLineOfSight() .ignoreInvisibilityTesting(); private final int scanRate; private long timeToTick; public Sensor(int scanRate) { this.scanRate = scanRate; this.timeToTick = RANDOM.nextInt(scanRate); } public Sensor() { this(20); } public final void tick(ServerLevel level, E entity) { if (--this.timeToTick <= 0L) { this.timeToTick = this.scanRate; this.updateTargetingConditionRanges(entity); this.doTick(level, entity); } } private void updateTargetingConditionRanges(E entity) { double d = entity.getAttributeValue(Attributes.FOLLOW_RANGE); TARGET_CONDITIONS.range(d); TARGET_CONDITIONS_IGNORE_INVISIBILITY_TESTING.range(d); ATTACK_TARGET_CONDITIONS.range(d); ATTACK_TARGET_CONDITIONS_IGNORE_INVISIBILITY_TESTING.range(d); ATTACK_TARGET_CONDITIONS_IGNORE_LINE_OF_SIGHT.range(d); ATTACK_TARGET_CONDITIONS_IGNORE_INVISIBILITY_AND_LINE_OF_SIGHT.range(d); } protected abstract void doTick(ServerLevel level, E entity); public abstract Set> requires(); public static boolean isEntityTargetable(ServerLevel level, LivingEntity entity, LivingEntity target) { return entity.getBrain().isMemoryValue(MemoryModuleType.ATTACK_TARGET, target) ? TARGET_CONDITIONS_IGNORE_INVISIBILITY_TESTING.test(level, entity, target) : TARGET_CONDITIONS.test(level, entity, target); } public static boolean isEntityAttackable(ServerLevel level, LivingEntity entity, LivingEntity target) { return entity.getBrain().isMemoryValue(MemoryModuleType.ATTACK_TARGET, target) ? ATTACK_TARGET_CONDITIONS_IGNORE_INVISIBILITY_TESTING.test(level, entity, target) : ATTACK_TARGET_CONDITIONS.test(level, entity, target); } public static BiPredicate wasEntityAttackableLastNTicks(LivingEntity entity, int ticks) { return rememberPositives(ticks, (serverLevel, livingEntity2) -> isEntityAttackable(serverLevel, entity, livingEntity2)); } public static boolean isEntityAttackableIgnoringLineOfSight(ServerLevel level, LivingEntity entity, LivingEntity target) { return entity.getBrain().isMemoryValue(MemoryModuleType.ATTACK_TARGET, target) ? ATTACK_TARGET_CONDITIONS_IGNORE_INVISIBILITY_AND_LINE_OF_SIGHT.test(level, entity, target) : ATTACK_TARGET_CONDITIONS_IGNORE_LINE_OF_SIGHT.test(level, entity, target); } static BiPredicate rememberPositives(int ticks, BiPredicate predicate) { AtomicInteger atomicInteger = new AtomicInteger(0); return (object, object2) -> { if (predicate.test(object, object2)) { atomicInteger.set(ticks); return true; } else { return atomicInteger.decrementAndGet() >= 0; } }; } }