211 lines
8.8 KiB
Java
211 lines
8.8 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* org.jspecify.annotations.Nullable
|
|
*/
|
|
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.util.RandomSource;
|
|
import net.minecraft.world.DifficultyInstance;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntitySpawnReason;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.EquipmentSlot;
|
|
import net.minecraft.world.entity.Mob;
|
|
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.monster.Monster;
|
|
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.level.storage.ValueInput;
|
|
import net.minecraft.world.level.storage.ValueOutput;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.jspecify.annotations.Nullable;
|
|
|
|
public abstract class PatrollingMonster
|
|
extends Monster {
|
|
private static final boolean DEFAULT_PATROL_LEADER = false;
|
|
private static final boolean DEFAULT_PATROLLING = false;
|
|
private @Nullable BlockPos patrolTarget;
|
|
private boolean patrolLeader = false;
|
|
private boolean patrolling = false;
|
|
|
|
protected PatrollingMonster(EntityType<? extends PatrollingMonster> type, Level level) {
|
|
super((EntityType<? extends Monster>)type, level);
|
|
}
|
|
|
|
@Override
|
|
protected void registerGoals() {
|
|
super.registerGoals();
|
|
this.goalSelector.addGoal(4, new LongDistancePatrolGoal<PatrollingMonster>(this, 0.7, 0.595));
|
|
}
|
|
|
|
@Override
|
|
protected void addAdditionalSaveData(ValueOutput output) {
|
|
super.addAdditionalSaveData(output);
|
|
output.storeNullable("patrol_target", BlockPos.CODEC, this.patrolTarget);
|
|
output.putBoolean("PatrolLeader", this.patrolLeader);
|
|
output.putBoolean("Patrolling", this.patrolling);
|
|
}
|
|
|
|
@Override
|
|
protected void readAdditionalSaveData(ValueInput input) {
|
|
super.readAdditionalSaveData(input);
|
|
this.patrolTarget = input.read("patrol_target", BlockPos.CODEC).orElse(null);
|
|
this.patrolLeader = input.getBooleanOr("PatrolLeader", false);
|
|
this.patrolling = input.getBooleanOr("Patrolling", false);
|
|
}
|
|
|
|
public boolean canBeLeader() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public @Nullable SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData groupData) {
|
|
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, groupData);
|
|
}
|
|
|
|
public static boolean checkPatrollingMonsterSpawnRules(EntityType<? extends PatrollingMonster> type, LevelAccessor level, EntitySpawnReason spawnReason, BlockPos pos, RandomSource random) {
|
|
if (level.getBrightness(LightLayer.BLOCK, pos) > 8) {
|
|
return false;
|
|
}
|
|
return PatrollingMonster.checkAnyLightMonsterSpawnRules(type, level, spawnReason, pos, random);
|
|
}
|
|
|
|
@Override
|
|
public boolean removeWhenFarAway(double distSqr) {
|
|
return !this.patrolling || distSqr > 16384.0;
|
|
}
|
|
|
|
public void setPatrolTarget(BlockPos target) {
|
|
this.patrolTarget = target;
|
|
this.patrolling = true;
|
|
}
|
|
|
|
public @Nullable BlockPos getPatrolTarget() {
|
|
return this.patrolTarget;
|
|
}
|
|
|
|
public boolean hasPatrolTarget() {
|
|
return this.patrolTarget != null;
|
|
}
|
|
|
|
public void setPatrolLeader(boolean isLeader) {
|
|
this.patrolLeader = isLeader;
|
|
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 value) {
|
|
this.patrolling = value;
|
|
}
|
|
|
|
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 isOnCooldown = ((Entity)this.mob).level().getGameTime() < this.cooldownUntil;
|
|
return ((PatrollingMonster)this.mob).isPatrolling() && ((Mob)this.mob).getTarget() == null && !((Entity)this.mob).hasControllingPassenger() && ((PatrollingMonster)this.mob).hasPatrolTarget() && !isOnCooldown;
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
boolean patrolLeader = ((PatrollingMonster)this.mob).isPatrolLeader();
|
|
PathNavigation navigation = ((Mob)this.mob).getNavigation();
|
|
if (navigation.isDone()) {
|
|
List<PatrollingMonster> companions = this.findPatrolCompanions();
|
|
if (((PatrollingMonster)this.mob).isPatrolling() && companions.isEmpty()) {
|
|
((PatrollingMonster)this.mob).setPatrolling(false);
|
|
} else if (!patrolLeader || !((PatrollingMonster)this.mob).getPatrolTarget().closerToCenterThan(((Entity)this.mob).position(), 10.0)) {
|
|
Vec3 longDistanceTarget = Vec3.atBottomCenterOf(((PatrollingMonster)this.mob).getPatrolTarget());
|
|
Vec3 selfVector = ((Entity)this.mob).position();
|
|
Vec3 distance = selfVector.subtract(longDistanceTarget);
|
|
longDistanceTarget = distance.yRot(90.0f).scale(0.4).add(longDistanceTarget);
|
|
Vec3 moveTarget = longDistanceTarget.subtract(selfVector).normalize().scale(10.0).add(selfVector);
|
|
BlockPos pathTarget = BlockPos.containing(moveTarget);
|
|
pathTarget = ((Entity)this.mob).level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, pathTarget);
|
|
if (!navigation.moveTo(pathTarget.getX(), pathTarget.getY(), pathTarget.getZ(), patrolLeader ? this.leaderSpeedModifier : this.speedModifier)) {
|
|
this.moveRandomly();
|
|
this.cooldownUntil = ((Entity)this.mob).level().getGameTime() + 200L;
|
|
} else if (patrolLeader) {
|
|
for (PatrollingMonster companion : companions) {
|
|
companion.setPatrolTarget(pathTarget);
|
|
}
|
|
}
|
|
} else {
|
|
((PatrollingMonster)this.mob).findPatrolTarget();
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<PatrollingMonster> findPatrolCompanions() {
|
|
return ((Entity)this.mob).level().getEntitiesOfClass(PatrollingMonster.class, ((Entity)this.mob).getBoundingBox().inflate(16.0), mob -> mob.canJoinPatrol() && !mob.is((Entity)this.mob));
|
|
}
|
|
|
|
private boolean moveRandomly() {
|
|
RandomSource random = ((Entity)this.mob).getRandom();
|
|
BlockPos pathTarget = ((Entity)this.mob).level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, ((Entity)this.mob).blockPosition().offset(-8 + random.nextInt(16), 0, -8 + random.nextInt(16)));
|
|
return ((Mob)this.mob).getNavigation().moveTo(pathTarget.getX(), pathTarget.getY(), pathTarget.getZ(), this.speedModifier);
|
|
}
|
|
}
|
|
}
|
|
|