minecraft-src/net/minecraft/world/entity/monster/PatrollingMonster.java
2025-07-04 03:45:38 +03:00

218 lines
7.2 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.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.goal.Goal.Flag;
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 {
private static final boolean DEFAULT_PATROL_LEADER = false;
private static final boolean DEFAULT_PATROLLING = false;
@Nullable
private BlockPos patrolTarget;
private boolean patrolLeader = false;
private boolean patrolling = false;
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 tag) {
super.addAdditionalSaveData(tag);
tag.storeNullable("patrol_target", BlockPos.CODEC, this.patrolTarget);
tag.putBoolean("PatrolLeader", this.patrolLeader);
tag.putBoolean("Patrolling", this.patrolling);
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
this.patrolTarget = (BlockPos)tag.read("patrol_target", BlockPos.CODEC).orElse(null);
this.patrolLeader = tag.getBooleanOr("PatrolLeader", false);
this.patrolling = tag.getBooleanOr("Patrolling", false);
}
public boolean canBeLeader() {
return true;
}
@Nullable
@Override
public SpawnGroupData finalizeSpawn(
ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData
) {
if (spawnReason != EntitySpawnReason.PATROL
&& spawnReason != EntitySpawnReason.EVENT
&& spawnReason != EntitySpawnReason.STRUCTURE
&& level.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 (spawnReason == EntitySpawnReason.PATROL) {
this.patrolling = true;
}
return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData);
}
public static boolean checkPatrollingMonsterSpawnRules(
EntityType<? extends PatrollingMonster> entityType, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource randomSource
) {
return level.getBrightness(LightLayer.BLOCK, pos) > 8 ? false : checkAnyLightMonsterSpawnRules(entityType, level, spawnReason, pos, 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(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);
}
}
}