minecraft-src/net/minecraft/world/entity/ai/sensing/Sensor.java
2025-07-04 02:49:36 +03:00

97 lines
4.2 KiB
Java

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<E extends LivingEntity> {
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<MemoryModuleType<?>> 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<ServerLevel, LivingEntity> 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 <T, U> BiPredicate<T, U> rememberPositives(int ticks, BiPredicate<T, U> predicate) {
AtomicInteger atomicInteger = new AtomicInteger(0);
return (object, object2) -> {
if (predicate.test(object, object2)) {
atomicInteger.set(ticks);
return true;
} else {
return atomicInteger.decrementAndGet() >= 0;
}
};
}
}