206 lines
8 KiB
Java
206 lines
8 KiB
Java
package net.minecraft.world.entity.ai.behavior;
|
|
|
|
import com.google.common.collect.ImmutableMap;
|
|
import com.google.common.collect.Lists;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.function.BiPredicate;
|
|
import java.util.function.Function;
|
|
import java.util.stream.Collectors;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.random.WeightedRandom;
|
|
import net.minecraft.util.valueproviders.UniformInt;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
|
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
|
|
import net.minecraft.world.entity.ai.memory.MemoryStatus;
|
|
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.pathfinder.Path;
|
|
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class LongJumpToRandomPos<E extends Mob> extends Behavior<E> {
|
|
protected static final int FIND_JUMP_TRIES = 20;
|
|
private static final int PREPARE_JUMP_DURATION = 40;
|
|
protected static final int MIN_PATHFIND_DISTANCE_TO_VALID_JUMP = 8;
|
|
private static final int TIME_OUT_DURATION = 200;
|
|
private static final List<Integer> ALLOWED_ANGLES = Lists.<Integer>newArrayList(65, 70, 75, 80);
|
|
private final UniformInt timeBetweenLongJumps;
|
|
protected final int maxLongJumpHeight;
|
|
protected final int maxLongJumpWidth;
|
|
protected final float maxJumpVelocityMultiplier;
|
|
protected List<LongJumpToRandomPos.PossibleJump> jumpCandidates = Lists.<LongJumpToRandomPos.PossibleJump>newArrayList();
|
|
protected Optional<Vec3> initialPosition = Optional.empty();
|
|
@Nullable
|
|
protected Vec3 chosenJump;
|
|
protected int findJumpTries;
|
|
protected long prepareJumpStart;
|
|
private final Function<E, SoundEvent> getJumpSound;
|
|
private final BiPredicate<E, BlockPos> acceptableLandingSpot;
|
|
|
|
public LongJumpToRandomPos(
|
|
UniformInt timeBetweenLongJumps, int maxLongJumpHeight, int maxLongJumpWidth, float maxJumpVelocity, Function<E, SoundEvent> getJumpSound
|
|
) {
|
|
this(timeBetweenLongJumps, maxLongJumpHeight, maxLongJumpWidth, maxJumpVelocity, getJumpSound, LongJumpToRandomPos::defaultAcceptableLandingSpot);
|
|
}
|
|
|
|
public static <E extends Mob> boolean defaultAcceptableLandingSpot(E mob, BlockPos pos) {
|
|
Level level = mob.level();
|
|
BlockPos blockPos = pos.below();
|
|
return level.getBlockState(blockPos).isSolidRender() && mob.getPathfindingMalus(WalkNodeEvaluator.getPathTypeStatic(mob, pos)) == 0.0F;
|
|
}
|
|
|
|
public LongJumpToRandomPos(
|
|
UniformInt timeBetweenLongJumps,
|
|
int maxLongJumpHeight,
|
|
int maxLongJumpWidth,
|
|
float maxJumpVelocity,
|
|
Function<E, SoundEvent> getJumpSound,
|
|
BiPredicate<E, BlockPos> acceptableLandingSpot
|
|
) {
|
|
super(
|
|
ImmutableMap.of(
|
|
MemoryModuleType.LOOK_TARGET,
|
|
MemoryStatus.REGISTERED,
|
|
MemoryModuleType.LONG_JUMP_COOLDOWN_TICKS,
|
|
MemoryStatus.VALUE_ABSENT,
|
|
MemoryModuleType.LONG_JUMP_MID_JUMP,
|
|
MemoryStatus.VALUE_ABSENT
|
|
),
|
|
200
|
|
);
|
|
this.timeBetweenLongJumps = timeBetweenLongJumps;
|
|
this.maxLongJumpHeight = maxLongJumpHeight;
|
|
this.maxLongJumpWidth = maxLongJumpWidth;
|
|
this.maxJumpVelocityMultiplier = maxJumpVelocity;
|
|
this.getJumpSound = getJumpSound;
|
|
this.acceptableLandingSpot = acceptableLandingSpot;
|
|
}
|
|
|
|
protected boolean checkExtraStartConditions(ServerLevel serverLevel, Mob mob) {
|
|
boolean bl = mob.onGround() && !mob.isInWater() && !mob.isInLava() && !serverLevel.getBlockState(mob.blockPosition()).is(Blocks.HONEY_BLOCK);
|
|
if (!bl) {
|
|
mob.getBrain().setMemory(MemoryModuleType.LONG_JUMP_COOLDOWN_TICKS, this.timeBetweenLongJumps.sample(serverLevel.random) / 2);
|
|
}
|
|
|
|
return bl;
|
|
}
|
|
|
|
protected boolean canStillUse(ServerLevel serverLevel, Mob mob, long l) {
|
|
boolean bl = this.initialPosition.isPresent()
|
|
&& ((Vec3)this.initialPosition.get()).equals(mob.position())
|
|
&& this.findJumpTries > 0
|
|
&& !mob.isInWater()
|
|
&& (this.chosenJump != null || !this.jumpCandidates.isEmpty());
|
|
if (!bl && mob.getBrain().getMemory(MemoryModuleType.LONG_JUMP_MID_JUMP).isEmpty()) {
|
|
mob.getBrain().setMemory(MemoryModuleType.LONG_JUMP_COOLDOWN_TICKS, this.timeBetweenLongJumps.sample(serverLevel.random) / 2);
|
|
mob.getBrain().eraseMemory(MemoryModuleType.LOOK_TARGET);
|
|
}
|
|
|
|
return bl;
|
|
}
|
|
|
|
protected void start(ServerLevel serverLevel, E mob, long l) {
|
|
this.chosenJump = null;
|
|
this.findJumpTries = 20;
|
|
this.initialPosition = Optional.of(mob.position());
|
|
BlockPos blockPos = mob.blockPosition();
|
|
int i = blockPos.getX();
|
|
int j = blockPos.getY();
|
|
int k = blockPos.getZ();
|
|
this.jumpCandidates = (List<LongJumpToRandomPos.PossibleJump>)BlockPos.betweenClosedStream(
|
|
i - this.maxLongJumpWidth,
|
|
j - this.maxLongJumpHeight,
|
|
k - this.maxLongJumpWidth,
|
|
i + this.maxLongJumpWidth,
|
|
j + this.maxLongJumpHeight,
|
|
k + this.maxLongJumpWidth
|
|
)
|
|
.filter(blockPos2 -> !blockPos2.equals(blockPos))
|
|
.map(blockPos2 -> new LongJumpToRandomPos.PossibleJump(blockPos2.immutable(), Mth.ceil(blockPos.distSqr(blockPos2))))
|
|
.collect(Collectors.toCollection(Lists::newArrayList));
|
|
}
|
|
|
|
protected void tick(ServerLevel serverLevel, E mob, long l) {
|
|
if (this.chosenJump != null) {
|
|
if (l - this.prepareJumpStart >= 40L) {
|
|
mob.setYRot(mob.yBodyRot);
|
|
mob.setDiscardFriction(true);
|
|
double d = this.chosenJump.length();
|
|
double e = d + mob.getJumpBoostPower();
|
|
mob.setDeltaMovement(this.chosenJump.scale(e / d));
|
|
mob.getBrain().setMemory(MemoryModuleType.LONG_JUMP_MID_JUMP, true);
|
|
serverLevel.playSound(null, mob, (SoundEvent)this.getJumpSound.apply(mob), SoundSource.NEUTRAL, 1.0F, 1.0F);
|
|
}
|
|
} else {
|
|
this.findJumpTries--;
|
|
this.pickCandidate(serverLevel, mob, l);
|
|
}
|
|
}
|
|
|
|
protected void pickCandidate(ServerLevel level, E entity, long prepareJumpStart) {
|
|
while (!this.jumpCandidates.isEmpty()) {
|
|
Optional<LongJumpToRandomPos.PossibleJump> optional = this.getJumpCandidate(level);
|
|
if (!optional.isEmpty()) {
|
|
LongJumpToRandomPos.PossibleJump possibleJump = (LongJumpToRandomPos.PossibleJump)optional.get();
|
|
BlockPos blockPos = possibleJump.targetPos();
|
|
if (this.isAcceptableLandingPosition(level, entity, blockPos)) {
|
|
Vec3 vec3 = Vec3.atCenterOf(blockPos);
|
|
Vec3 vec32 = this.calculateOptimalJumpVector(entity, vec3);
|
|
if (vec32 != null) {
|
|
entity.getBrain().setMemory(MemoryModuleType.LOOK_TARGET, new BlockPosTracker(blockPos));
|
|
PathNavigation pathNavigation = entity.getNavigation();
|
|
Path path = pathNavigation.createPath(blockPos, 0, 8);
|
|
if (path == null || !path.canReach()) {
|
|
this.chosenJump = vec32;
|
|
this.prepareJumpStart = prepareJumpStart;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected Optional<LongJumpToRandomPos.PossibleJump> getJumpCandidate(ServerLevel level) {
|
|
Optional<LongJumpToRandomPos.PossibleJump> optional = WeightedRandom.getRandomItem(
|
|
level.random, this.jumpCandidates, LongJumpToRandomPos.PossibleJump::weight
|
|
);
|
|
optional.ifPresent(this.jumpCandidates::remove);
|
|
return optional;
|
|
}
|
|
|
|
private boolean isAcceptableLandingPosition(ServerLevel level, E entity, BlockPos pos) {
|
|
BlockPos blockPos = entity.blockPosition();
|
|
int i = blockPos.getX();
|
|
int j = blockPos.getZ();
|
|
return i == pos.getX() && j == pos.getZ() ? false : this.acceptableLandingSpot.test(entity, pos);
|
|
}
|
|
|
|
@Nullable
|
|
protected Vec3 calculateOptimalJumpVector(Mob mob, Vec3 target) {
|
|
List<Integer> list = Lists.<Integer>newArrayList(ALLOWED_ANGLES);
|
|
Collections.shuffle(list);
|
|
float f = (float)(mob.getAttributeValue(Attributes.JUMP_STRENGTH) * this.maxJumpVelocityMultiplier);
|
|
|
|
for (int i : list) {
|
|
Optional<Vec3> optional = LongJumpUtil.calculateJumpVectorForAngle(mob, target, f, i, true);
|
|
if (optional.isPresent()) {
|
|
return (Vec3)optional.get();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public record PossibleJump(BlockPos targetPos, int weight) {
|
|
}
|
|
}
|