225 lines
7.3 KiB
Java
225 lines
7.3 KiB
Java
package net.minecraft.world.entity.monster;
|
|
|
|
import java.util.EnumSet;
|
|
import java.util.List;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.registries.Registries;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.nbt.NbtUtils;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.world.DifficultyInstance;
|
|
import net.minecraft.world.entity.EntitySpawnReason;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.EquipmentSlot;
|
|
import net.minecraft.world.entity.SpawnGroupData;
|
|
import net.minecraft.world.entity.ai.goal.Goal;
|
|
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
|
import net.minecraft.world.entity.raid.Raid;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.LevelAccessor;
|
|
import net.minecraft.world.level.LightLayer;
|
|
import net.minecraft.world.level.ServerLevelAccessor;
|
|
import net.minecraft.world.level.levelgen.Heightmap;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public abstract class PatrollingMonster extends Monster {
|
|
@Nullable
|
|
private BlockPos patrolTarget;
|
|
private boolean patrolLeader;
|
|
private boolean patrolling;
|
|
|
|
protected PatrollingMonster(EntityType<? extends PatrollingMonster> entityType, Level level) {
|
|
super(entityType, level);
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(4, new PatrollingMonster.LongDistancePatrolGoal<>(this, 0.7, 0.595));
|
|
}
|
|
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag compound) {
|
|
super.addAdditionalSaveData(compound);
|
|
if (this.patrolTarget != null) {
|
|
compound.put("patrol_target", NbtUtils.writeBlockPos(this.patrolTarget));
|
|
}
|
|
|
|
compound.putBoolean("PatrolLeader", this.patrolLeader);
|
|
compound.putBoolean("Patrolling", this.patrolling);
|
|
}
|
|
|
|
@Override
|
|
public void readAdditionalSaveData(CompoundTag compound) {
|
|
super.readAdditionalSaveData(compound);
|
|
NbtUtils.readBlockPos(compound, "patrol_target").ifPresent(blockPos -> this.patrolTarget = blockPos);
|
|
this.patrolLeader = compound.getBoolean("PatrolLeader");
|
|
this.patrolling = compound.getBoolean("Patrolling");
|
|
}
|
|
|
|
public boolean canBeLeader() {
|
|
return true;
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public SpawnGroupData finalizeSpawn(
|
|
ServerLevelAccessor serverLevelAccessor, DifficultyInstance difficultyInstance, EntitySpawnReason entitySpawnReason, @Nullable SpawnGroupData spawnGroupData
|
|
) {
|
|
if (entitySpawnReason != EntitySpawnReason.PATROL
|
|
&& entitySpawnReason != EntitySpawnReason.EVENT
|
|
&& entitySpawnReason != EntitySpawnReason.STRUCTURE
|
|
&& serverLevelAccessor.getRandom().nextFloat() < 0.06F
|
|
&& this.canBeLeader()) {
|
|
this.patrolLeader = true;
|
|
}
|
|
|
|
if (this.isPatrolLeader()) {
|
|
this.setItemSlot(EquipmentSlot.HEAD, Raid.getOminousBannerInstance(this.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN)));
|
|
this.setDropChance(EquipmentSlot.HEAD, 2.0F);
|
|
}
|
|
|
|
if (entitySpawnReason == EntitySpawnReason.PATROL) {
|
|
this.patrolling = true;
|
|
}
|
|
|
|
return super.finalizeSpawn(serverLevelAccessor, difficultyInstance, entitySpawnReason, spawnGroupData);
|
|
}
|
|
|
|
public static boolean checkPatrollingMonsterSpawnRules(
|
|
EntityType<? extends PatrollingMonster> entityType,
|
|
LevelAccessor levelAccessor,
|
|
EntitySpawnReason entitySpawnReason,
|
|
BlockPos blockPos,
|
|
RandomSource randomSource
|
|
) {
|
|
return levelAccessor.getBrightness(LightLayer.BLOCK, blockPos) > 8
|
|
? false
|
|
: checkAnyLightMonsterSpawnRules(entityType, levelAccessor, entitySpawnReason, blockPos, randomSource);
|
|
}
|
|
|
|
@Override
|
|
public boolean removeWhenFarAway(double distanceToClosestPlayer) {
|
|
return !this.patrolling || distanceToClosestPlayer > 16384.0;
|
|
}
|
|
|
|
public void setPatrolTarget(BlockPos patrolTarget) {
|
|
this.patrolTarget = patrolTarget;
|
|
this.patrolling = true;
|
|
}
|
|
|
|
public BlockPos getPatrolTarget() {
|
|
return this.patrolTarget;
|
|
}
|
|
|
|
public boolean hasPatrolTarget() {
|
|
return this.patrolTarget != null;
|
|
}
|
|
|
|
public void setPatrolLeader(boolean patrolLeader) {
|
|
this.patrolLeader = patrolLeader;
|
|
this.patrolling = true;
|
|
}
|
|
|
|
public boolean isPatrolLeader() {
|
|
return this.patrolLeader;
|
|
}
|
|
|
|
public boolean canJoinPatrol() {
|
|
return true;
|
|
}
|
|
|
|
public void findPatrolTarget() {
|
|
this.patrolTarget = this.blockPosition().offset(-500 + this.random.nextInt(1000), 0, -500 + this.random.nextInt(1000));
|
|
this.patrolling = true;
|
|
}
|
|
|
|
protected boolean isPatrolling() {
|
|
return this.patrolling;
|
|
}
|
|
|
|
protected void setPatrolling(boolean patrolling) {
|
|
this.patrolling = patrolling;
|
|
}
|
|
|
|
public static class LongDistancePatrolGoal<T extends PatrollingMonster> extends Goal {
|
|
private static final int NAVIGATION_FAILED_COOLDOWN = 200;
|
|
private final T mob;
|
|
private final double speedModifier;
|
|
private final double leaderSpeedModifier;
|
|
private long cooldownUntil;
|
|
|
|
public LongDistancePatrolGoal(T mob, double speedModifier, double leaderSpeedModifier) {
|
|
this.mob = mob;
|
|
this.speedModifier = speedModifier;
|
|
this.leaderSpeedModifier = leaderSpeedModifier;
|
|
this.cooldownUntil = -1L;
|
|
this.setFlags(EnumSet.of(Goal.Flag.MOVE));
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
boolean bl = this.mob.level().getGameTime() < this.cooldownUntil;
|
|
return this.mob.isPatrolling() && this.mob.getTarget() == null && !this.mob.hasControllingPassenger() && this.mob.hasPatrolTarget() && !bl;
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
boolean bl = this.mob.isPatrolLeader();
|
|
PathNavigation pathNavigation = this.mob.getNavigation();
|
|
if (pathNavigation.isDone()) {
|
|
List<PatrollingMonster> list = this.findPatrolCompanions();
|
|
if (this.mob.isPatrolling() && list.isEmpty()) {
|
|
this.mob.setPatrolling(false);
|
|
} else if (bl && this.mob.getPatrolTarget().closerToCenterThan(this.mob.position(), 10.0)) {
|
|
this.mob.findPatrolTarget();
|
|
} else {
|
|
Vec3 vec3 = Vec3.atBottomCenterOf(this.mob.getPatrolTarget());
|
|
Vec3 vec32 = this.mob.position();
|
|
Vec3 vec33 = vec32.subtract(vec3);
|
|
vec3 = vec33.yRot(90.0F).scale(0.4).add(vec3);
|
|
Vec3 vec34 = vec3.subtract(vec32).normalize().scale(10.0).add(vec32);
|
|
BlockPos blockPos = BlockPos.containing(vec34);
|
|
blockPos = this.mob.level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, blockPos);
|
|
if (!pathNavigation.moveTo(blockPos.getX(), blockPos.getY(), blockPos.getZ(), bl ? this.leaderSpeedModifier : this.speedModifier)) {
|
|
this.moveRandomly();
|
|
this.cooldownUntil = this.mob.level().getGameTime() + 200L;
|
|
} else if (bl) {
|
|
for (PatrollingMonster patrollingMonster : list) {
|
|
patrollingMonster.setPatrolTarget(blockPos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<PatrollingMonster> findPatrolCompanions() {
|
|
return this.mob
|
|
.level()
|
|
.getEntitiesOfClass(
|
|
PatrollingMonster.class,
|
|
this.mob.getBoundingBox().inflate(16.0),
|
|
patrollingMonster -> patrollingMonster.canJoinPatrol() && !patrollingMonster.is(this.mob)
|
|
);
|
|
}
|
|
|
|
private boolean moveRandomly() {
|
|
RandomSource randomSource = this.mob.getRandom();
|
|
BlockPos blockPos = this.mob
|
|
.level()
|
|
.getHeightmapPos(
|
|
Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, this.mob.blockPosition().offset(-8 + randomSource.nextInt(16), 0, -8 + randomSource.nextInt(16))
|
|
);
|
|
return this.mob.getNavigation().moveTo(blockPos.getX(), blockPos.getY(), blockPos.getZ(), this.speedModifier);
|
|
}
|
|
}
|
|
}
|