fix various issues
This commit is contained in:
@@ -85,19 +85,19 @@ public final class AdaptiveGoalInjector {
|
||||
}
|
||||
|
||||
public static void inject(Mob mob, ServerLevel level, int initialTier) {
|
||||
if (!AMConfig.AI_ENABLED.get() || initialTier <= 0 || isBoss(mob)) {
|
||||
if (isBoss(mob)) {
|
||||
return;
|
||||
}
|
||||
if (!INJECTED.add(mob)) {
|
||||
return;
|
||||
}
|
||||
PathfinderMob pathfinder = mob instanceof PathfinderMob pf ? pf : null;
|
||||
IntSupplier tier = () -> AdaptiveDifficultyManager.getMobTier(level, mob.getType());
|
||||
IntSupplier tier = () -> isAIEnabledFor(mob)
|
||||
? AdaptiveDifficultyManager.getMobTier(level, mob.getType()) : 0;
|
||||
IntSupplier advancedTier = () -> AMConfig.ENABLE_ADVANCED_AI.get() ? tier.getAsInt() : 0;
|
||||
mob.targetSelector.addGoal(0, new AdaptiveTargetSanitizerGoal(mob));
|
||||
|
||||
if (tier.getAsInt() >= 4) {
|
||||
mob.targetSelector.addGoal(2, new AdaptiveSharedTargetGoal(mob, tier, 4, 12.0D));
|
||||
}
|
||||
mob.targetSelector.addGoal(2, new AdaptiveSharedTargetGoal(mob, tier, 4, 12.0D));
|
||||
|
||||
if (pathfinder != null && mob instanceof AbstractSkeleton && AMConfig.AI_SKELETON.get()) {
|
||||
mob.goalSelector.addGoal(3, new AdaptiveSkeletonShieldGoal(pathfinder, tier));
|
||||
@@ -118,10 +118,10 @@ public final class AdaptiveGoalInjector {
|
||||
mob.goalSelector.addGoal(4, new AdaptiveFlankGoal(pathfinder, tier, 2, 7.0D, 1.0D, false));
|
||||
}
|
||||
|
||||
if (AMConfig.ENABLE_ADVANCED_AI.get()) {
|
||||
injectAdvanced(mob, pathfinder, tier);
|
||||
AdaptiveAIGoalUtils.debug(mob, tier, "injected adaptive goals");
|
||||
}
|
||||
// Install advanced goals even while disabled. Their tier supplier returns zero until the
|
||||
// switches are enabled, allowing config reloads to affect already-loaded mobs both ways.
|
||||
injectAdvanced(mob, pathfinder, advancedTier);
|
||||
AdaptiveAIGoalUtils.debug(mob, tier, "injected adaptive goals");
|
||||
}
|
||||
|
||||
private static void injectAdvanced(Mob mob, PathfinderMob pathfinder, IntSupplier tier) {
|
||||
@@ -310,6 +310,32 @@ public final class AdaptiveGoalInjector {
|
||||
return 1.05D;
|
||||
}
|
||||
|
||||
public static boolean isAIEnabledFor(Mob mob) {
|
||||
if (!AMConfig.enabled || !AMConfig.AI_ENABLED.get() || !AMConfig.isMobEnabled(mob.getType())) {
|
||||
return false;
|
||||
}
|
||||
if (mob instanceof AbstractSkeleton) return AMConfig.AI_SKELETON.get();
|
||||
if (mob instanceof Zombie) return AMConfig.AI_ZOMBIE.get();
|
||||
if (mob instanceof Creeper) return AMConfig.AI_CREEPER.get();
|
||||
if (mob instanceof Spider) return AMConfig.AI_SPIDER.get();
|
||||
if (mob instanceof Witch) return AMConfig.AI_WITCH.get();
|
||||
if (mob instanceof Pillager) return AMConfig.AI_PILLAGER.get();
|
||||
if (mob instanceof EnderMan) return AMConfig.AI_ENDERMAN.get();
|
||||
if (mob instanceof Ghast) return AMConfig.AI_GHAST.get();
|
||||
if (mob instanceof Blaze) return AMConfig.AI_BLAZE.get();
|
||||
if (mob instanceof Guardian) return AMConfig.AI_GUARDIAN.get();
|
||||
if (mob instanceof Phantom) return AMConfig.AI_PHANTOM.get();
|
||||
if (mob instanceof Ravager) return AMConfig.AI_RAVAGER.get();
|
||||
if (mob instanceof Evoker) return AMConfig.AI_EVOKER.get();
|
||||
if (mob instanceof Vex) return AMConfig.AI_VEX.get();
|
||||
if (mob instanceof Piglin || mob instanceof Hoglin || mob instanceof Zoglin) return AMConfig.AI_PIGLIN_FAMILY.get();
|
||||
if (mob instanceof Silverfish || mob instanceof Endermite) return AMConfig.AI_SMALL_SWARM.get();
|
||||
if (mob instanceof Shulker) return AMConfig.AI_SHULKER.get();
|
||||
if (mob instanceof Warden) return AMConfig.AI_WARDEN.get();
|
||||
if (mob instanceof Slime) return AMConfig.AI_SLIME.get();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int creeperFuseTicks(int tier) {
|
||||
return Math.max(18, 30 - Math.max(0, tier - 1) * 3);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class AdaptiveSpecialAbilities {
|
||||
|
||||
@@ -68,7 +69,7 @@ public final class AdaptiveSpecialAbilities {
|
||||
}
|
||||
|
||||
public static void onProjectileJoin(EntityJoinLevelEvent event) {
|
||||
if (!AMConfig.enabled || !(event.getLevel() instanceof ServerLevel level)) {
|
||||
if (!adaptiveAbilitiesEnabled() || !(event.getLevel() instanceof ServerLevel level)) {
|
||||
return;
|
||||
}
|
||||
Entity entity = event.getEntity();
|
||||
@@ -112,7 +113,7 @@ public final class AdaptiveSpecialAbilities {
|
||||
}
|
||||
|
||||
public static void onLivingHurt(LivingHurtEvent event) {
|
||||
if (!AMConfig.enabled || !(event.getEntity().level() instanceof ServerLevel level)) {
|
||||
if (!adaptiveAbilitiesEnabled() || !(event.getEntity().level() instanceof ServerLevel level)) {
|
||||
return;
|
||||
}
|
||||
LivingEntity victim = event.getEntity();
|
||||
@@ -138,7 +139,7 @@ public final class AdaptiveSpecialAbilities {
|
||||
if (now < block.expireTick) {
|
||||
continue;
|
||||
}
|
||||
ServerLevel level = event.getServer().getLevel(block.dimension);
|
||||
ServerLevel level = block.server == event.getServer() ? event.getServer().getLevel(block.dimension) : null;
|
||||
if (level != null && level.getBlockState(block.pos).is(Blocks.COBWEB)) {
|
||||
level.removeBlock(block.pos, false);
|
||||
}
|
||||
@@ -151,15 +152,22 @@ public final class AdaptiveSpecialAbilities {
|
||||
if (now < pending.tick) {
|
||||
continue;
|
||||
}
|
||||
ServerLevel level = event.getServer().getLevel(pending.dimension);
|
||||
if (level != null && pending.mob.isAlive() && AdaptiveAIGoalUtils.isValidAdaptiveTarget(pending.target)
|
||||
&& pending.mob.canAttack(pending.target)) {
|
||||
Vec3 side = AdaptivePositioningUtils.sidePosition(pending.mob, pending.target, 1.6D,
|
||||
pending.mob.getRandom().nextBoolean() ? 1.0D : -1.0D, 0.0D);
|
||||
pending.mob.moveTo(side.x, side.y, side.z, pending.mob.getYRot(), pending.mob.getXRot());
|
||||
pending.mob.removeEffect(MobEffects.INVISIBILITY);
|
||||
pending.mob.setTarget(pending.target);
|
||||
AdaptiveAIGoalUtils.debug(pending.mob, () -> pending.tier, "silverfish burrow burst");
|
||||
ServerLevel level = pending.server == event.getServer() ? event.getServer().getLevel(pending.dimension) : null;
|
||||
Entity mobEntity = level == null ? null : level.getEntity(pending.mobId);
|
||||
Entity targetEntity = level == null ? null : level.getEntity(pending.targetId);
|
||||
if (mobEntity instanceof Silverfish mob && targetEntity instanceof LivingEntity target
|
||||
&& mob.isAlive() && AdaptiveAIGoalUtils.isValidAdaptiveTarget(target) && mob.canAttack(target)) {
|
||||
Vec3 side = AdaptivePositioningUtils.sidePosition(mob, target, 1.6D,
|
||||
mob.getRandom().nextBoolean() ? 1.0D : -1.0D, 0.0D);
|
||||
BlockPos destination = BlockPos.containing(side);
|
||||
Vec3 delta = side.subtract(mob.position());
|
||||
if (level.hasChunkAt(destination) && level.getWorldBorder().isWithinBounds(destination)
|
||||
&& level.noCollision(mob, mob.getBoundingBox().move(delta))) {
|
||||
mob.moveTo(side.x, side.y, side.z, mob.getYRot(), mob.getXRot());
|
||||
mob.setTarget(target);
|
||||
AdaptiveAIGoalUtils.debug(mob, () -> pending.tier, "silverfish burrow burst");
|
||||
}
|
||||
mob.removeEffect(MobEffects.INVISIBILITY);
|
||||
}
|
||||
reappearIterator.remove();
|
||||
}
|
||||
@@ -193,7 +201,8 @@ public final class AdaptiveSpecialAbilities {
|
||||
&& ready(mob, "burrow_burst", 220)) {
|
||||
silverfish.addEffect(new MobEffectInstance(MobEffects.INVISIBILITY, 35, 0));
|
||||
silverfish.getNavigation().stop();
|
||||
DELAYED_REAPPEARS.add(new DelayedReappear(level.dimension(), level.getGameTime() + 24, silverfish, attacker, tier));
|
||||
DELAYED_REAPPEARS.add(new DelayedReappear(level.getServer(), level.dimension(), level.getGameTime() + 24,
|
||||
silverfish.getUUID(), attacker.getUUID(), tier));
|
||||
AdaptiveAIGoalUtils.debug(mob, () -> tier, "silverfish burrow vanish");
|
||||
}
|
||||
if (tier >= 4 && mob instanceof Evoker evoker && ready(mob, "vex_relay", 120)) {
|
||||
@@ -293,7 +302,7 @@ public final class AdaptiveSpecialAbilities {
|
||||
}
|
||||
|
||||
private static int tier(ServerLevel level, Mob mob) {
|
||||
if (!AMConfig.isMobEnabled(mob.getType())) {
|
||||
if (!adaptiveAbilitiesEnabled() || !AdaptiveGoalInjector.isAIEnabledFor(mob)) {
|
||||
return 0;
|
||||
}
|
||||
return AdaptiveDifficultyManager.getMobTier(level, mob.getType());
|
||||
@@ -328,7 +337,7 @@ public final class AdaptiveSpecialAbilities {
|
||||
return;
|
||||
}
|
||||
level.setBlock(pos, Blocks.COBWEB.defaultBlockState(), 3);
|
||||
TEMPORARY_BLOCKS.add(new TemporaryBlock(level.dimension(), pos.immutable(), expireTick));
|
||||
TEMPORARY_BLOCKS.add(new TemporaryBlock(level.getServer(), level.dimension(), pos.immutable(), expireTick));
|
||||
level.playSound(null, pos, SoundEvents.SPIDER_AMBIENT, SoundSource.HOSTILE, 0.6F, 1.25F);
|
||||
}
|
||||
|
||||
@@ -367,11 +376,32 @@ public final class AdaptiveSpecialAbilities {
|
||||
level.addFreshEntity(split);
|
||||
}
|
||||
|
||||
private record TemporaryBlock(net.minecraft.resources.ResourceKey<net.minecraft.world.level.Level> dimension,
|
||||
public static void invalidateTemporaryBlock(ServerLevel level, BlockPos pos) {
|
||||
TEMPORARY_BLOCKS.removeIf(block -> block.server == level.getServer()
|
||||
&& block.dimension == level.dimension() && block.pos.equals(pos));
|
||||
}
|
||||
|
||||
public static void clear(ServerLevel level) {
|
||||
TEMPORARY_BLOCKS.removeIf(block -> block.server == level.getServer() && block.dimension == level.dimension());
|
||||
DELAYED_REAPPEARS.removeIf(pending -> pending.server == level.getServer() && pending.dimension == level.dimension());
|
||||
}
|
||||
|
||||
public static void clear(net.minecraft.server.MinecraftServer server) {
|
||||
TEMPORARY_BLOCKS.removeIf(block -> block.server == server);
|
||||
DELAYED_REAPPEARS.removeIf(pending -> pending.server == server);
|
||||
}
|
||||
|
||||
private static boolean adaptiveAbilitiesEnabled() {
|
||||
return AMConfig.enabled && AMConfig.AI_ENABLED.get() && AMConfig.ENABLE_ADVANCED_AI.get();
|
||||
}
|
||||
|
||||
private record TemporaryBlock(net.minecraft.server.MinecraftServer server,
|
||||
net.minecraft.resources.ResourceKey<net.minecraft.world.level.Level> dimension,
|
||||
BlockPos pos, long expireTick) {
|
||||
}
|
||||
|
||||
private record DelayedReappear(net.minecraft.resources.ResourceKey<net.minecraft.world.level.Level> dimension,
|
||||
long tick, Silverfish mob, LivingEntity target, int tier) {
|
||||
private record DelayedReappear(net.minecraft.server.MinecraftServer server,
|
||||
net.minecraft.resources.ResourceKey<net.minecraft.world.level.Level> dimension,
|
||||
long tick, UUID mobId, UUID targetId, int tier) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,25 +49,17 @@ public class AdaptiveAntiCheeseGoal extends Goal {
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
if (!AMConfig.ENABLE_ANTI_CHEESE_AI.get()) {
|
||||
if (!AMConfig.AI_ENABLED.get() || !AMConfig.ENABLE_ADVANCED_AI.get()
|
||||
|| !AMConfig.ENABLE_ANTI_CHEESE_AI.get() || tierSupplier.getAsInt() < 3) {
|
||||
return false;
|
||||
}
|
||||
LivingEntity target = mob.getTarget();
|
||||
if (isInTrapVehicle() || isTooCloseToTrapVehicle() || nearestTrapBlock() != null) {
|
||||
return true;
|
||||
}
|
||||
if (trappedAllyVehicle() != null) {
|
||||
return true;
|
||||
}
|
||||
if (mob instanceof Zombie && mob.isPassenger() && shouldUnstack(target)) {
|
||||
return true;
|
||||
}
|
||||
if (tierSupplier.getAsInt() < 3) {
|
||||
return false;
|
||||
}
|
||||
if (!AdaptiveAIGoalUtils.isValidAdaptiveTarget(target)) {
|
||||
return false;
|
||||
}
|
||||
if (isInTrapVehicle() || nearestTrapBlock() != null || trappedAllyVehicle() != null) {
|
||||
return true;
|
||||
}
|
||||
if (mob instanceof Zombie && isVerticalObstacleCheese(target)) {
|
||||
return true;
|
||||
}
|
||||
@@ -141,15 +133,10 @@ public class AdaptiveAntiCheeseGoal extends Goal {
|
||||
Entity vehicle = mob.getVehicle();
|
||||
if (vehicle instanceof Boat || vehicle instanceof AbstractMinecart) {
|
||||
mob.getNavigation().stop();
|
||||
tryDestroyTrapVehicle(vehicle);
|
||||
mob.stopRiding();
|
||||
moveAwayFrom(vehicle.position(), 1.15D, "trap vehicle escape");
|
||||
return true;
|
||||
}
|
||||
Entity trapVehicle = nearestTrapVehicle();
|
||||
if (trapVehicle != null && mob.distanceToSqr(trapVehicle) < TRAP_VEHICLE_BREAK_DISTANCE_SQR) {
|
||||
tryDestroyTrapVehicle(trapVehicle);
|
||||
return true;
|
||||
}
|
||||
BlockPos trapBlock = nearestTrapBlock();
|
||||
if (trapBlock != null) {
|
||||
tryDestroyTrapBlock(trapBlock);
|
||||
@@ -164,26 +151,6 @@ public class AdaptiveAntiCheeseGoal extends Goal {
|
||||
return vehicle instanceof Boat || vehicle instanceof AbstractMinecart;
|
||||
}
|
||||
|
||||
private boolean isTooCloseToTrapVehicle() {
|
||||
Entity trapVehicle = nearestTrapVehicle();
|
||||
return trapVehicle != null && mob.distanceToSqr(trapVehicle) < TRAP_VEHICLE_BREAK_DISTANCE_SQR;
|
||||
}
|
||||
|
||||
private Entity nearestTrapVehicle() {
|
||||
List<Entity> vehicles = mob.level().getEntitiesOfClass(Entity.class, mob.getBoundingBox().inflate(3.5D),
|
||||
entity -> entity instanceof Boat || entity instanceof AbstractMinecart);
|
||||
Entity nearest = null;
|
||||
double best = Double.MAX_VALUE;
|
||||
for (Entity entity : vehicles) {
|
||||
double dist = mob.distanceToSqr(entity);
|
||||
if (dist < best) {
|
||||
best = dist;
|
||||
nearest = entity;
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
private Entity trappedAllyVehicle() {
|
||||
List<Entity> vehicles = mob.level().getEntitiesOfClass(Entity.class, mob.getBoundingBox().inflate(8.0D),
|
||||
entity -> isTrapVehicle(entity) && hasTrappedHostilePassenger(entity));
|
||||
@@ -245,42 +212,15 @@ public class AdaptiveAntiCheeseGoal extends Goal {
|
||||
AdaptiveAIGoalUtils.debug(mob, tierSupplier, "moving to break ally trap vehicle");
|
||||
return true;
|
||||
}
|
||||
mob.swing(mob.getUsedItemHand());
|
||||
dropTrapVehicleItem(vehicle);
|
||||
vehicle.ejectPassengers();
|
||||
vehicle.discard();
|
||||
AdaptiveAIGoalUtils.debug(mob, tierSupplier, "breaking ally trap vehicle");
|
||||
for (Entity passenger : List.copyOf(vehicle.getPassengers())) {
|
||||
if (passenger instanceof Mob trapped && trapped instanceof Enemy) {
|
||||
trapped.stopRiding();
|
||||
}
|
||||
}
|
||||
AdaptiveAIGoalUtils.debug(mob, tierSupplier, "freeing ally from trap vehicle");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void dropTrapVehicleItem(Entity vehicle) {
|
||||
Item drop = null;
|
||||
if (vehicle instanceof Boat boat) {
|
||||
drop = boatDrop(boat);
|
||||
} else if (vehicle instanceof AbstractMinecart) {
|
||||
drop = Items.MINECART;
|
||||
}
|
||||
if (drop != null) {
|
||||
vehicle.spawnAtLocation(drop);
|
||||
}
|
||||
}
|
||||
|
||||
private Item boatDrop(Boat boat) {
|
||||
boolean chest = boat instanceof ChestBoat;
|
||||
return switch (boat.getVariant()) {
|
||||
case OAK -> chest ? Items.OAK_CHEST_BOAT : Items.OAK_BOAT;
|
||||
case SPRUCE -> chest ? Items.SPRUCE_CHEST_BOAT : Items.SPRUCE_BOAT;
|
||||
case BIRCH -> chest ? Items.BIRCH_CHEST_BOAT : Items.BIRCH_BOAT;
|
||||
case JUNGLE -> chest ? Items.JUNGLE_CHEST_BOAT : Items.JUNGLE_BOAT;
|
||||
case ACACIA -> chest ? Items.ACACIA_CHEST_BOAT : Items.ACACIA_BOAT;
|
||||
case DARK_OAK -> chest ? Items.DARK_OAK_CHEST_BOAT : Items.DARK_OAK_BOAT;
|
||||
case MANGROVE -> chest ? Items.MANGROVE_CHEST_BOAT : Items.MANGROVE_BOAT;
|
||||
case CHERRY -> chest ? Items.CHERRY_CHEST_BOAT : Items.CHERRY_BOAT;
|
||||
case BAMBOO -> chest ? Items.BAMBOO_CHEST_RAFT : Items.BAMBOO_RAFT;
|
||||
default -> chest ? Items.OAK_CHEST_BOAT : Items.OAK_BOAT;
|
||||
};
|
||||
}
|
||||
|
||||
private BlockPos nearestTrapBlock() {
|
||||
BlockPos nearest = null;
|
||||
double best = Double.MAX_VALUE;
|
||||
@@ -317,6 +257,9 @@ public class AdaptiveAntiCheeseGoal extends Goal {
|
||||
if (mob instanceof Spider && state.is(Blocks.COBWEB)) {
|
||||
return;
|
||||
}
|
||||
if (mob instanceof Zombie && !AMConfig.ALLOW_ZOMBIE_BLOCK_BREAK.get()) {
|
||||
return;
|
||||
}
|
||||
if (state.is(Blocks.COBWEB)) {
|
||||
Block.popResource(mob.level(), pos, new ItemStack(Items.STRING));
|
||||
} else if (state.is(Blocks.SWEET_BERRY_BUSH)) {
|
||||
|
||||
@@ -11,11 +11,11 @@ import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.monster.EnderMan;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.function.IntSupplier;
|
||||
@@ -24,23 +24,6 @@ public class AdaptiveEndermanTacticsGoal extends Goal {
|
||||
|
||||
private static final int ANTI_PILLAR_BUILD_COOLDOWN_MIN = 4;
|
||||
private static final int ANTI_PILLAR_BUILD_COOLDOWN_RANDOM = 4;
|
||||
private static final BlockState[] OVERWORLD_SCAFFOLD_BLOCKS = {
|
||||
Blocks.DIRT.defaultBlockState(),
|
||||
Blocks.GRASS_BLOCK.defaultBlockState(),
|
||||
Blocks.COARSE_DIRT.defaultBlockState(),
|
||||
Blocks.ROOTED_DIRT.defaultBlockState(),
|
||||
Blocks.GRAVEL.defaultBlockState(),
|
||||
Blocks.SAND.defaultBlockState(),
|
||||
Blocks.RED_SAND.defaultBlockState(),
|
||||
Blocks.COBBLESTONE.defaultBlockState(),
|
||||
Blocks.STONE.defaultBlockState()
|
||||
};
|
||||
private static final BlockState[] END_SCAFFOLD_BLOCKS = {
|
||||
Blocks.END_STONE.defaultBlockState(),
|
||||
Blocks.OBSIDIAN.defaultBlockState(),
|
||||
Blocks.COBBLESTONE.defaultBlockState()
|
||||
};
|
||||
|
||||
private final EnderMan enderman;
|
||||
private final IntSupplier tierSupplier;
|
||||
private int cooldown;
|
||||
@@ -57,7 +40,8 @@ public class AdaptiveEndermanTacticsGoal extends Goal {
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return AMConfig.ENABLE_ENDERMAN_AI.get()
|
||||
return AMConfig.AI_ENABLED.get() && AMConfig.ENABLE_ADVANCED_AI.get()
|
||||
&& AMConfig.AI_ENDERMAN.get() && AMConfig.ENABLE_ENDERMAN_AI.get()
|
||||
&& tierSupplier.getAsInt() >= 2
|
||||
&& AdaptiveAIGoalUtils.isValidAdaptiveTarget(enderman.getTarget())
|
||||
&& --cooldown <= 0;
|
||||
@@ -118,8 +102,13 @@ public class AdaptiveEndermanTacticsGoal extends Goal {
|
||||
if (!(enderman.level() instanceof ServerLevel level)) {
|
||||
return false;
|
||||
}
|
||||
BlockState scaffoldState = randomScaffoldState(inEnd);
|
||||
prepareScaffoldBlock(level, scaffoldState);
|
||||
if (!ForgeEventFactory.getMobGriefingEvent(level, enderman)) {
|
||||
return false;
|
||||
}
|
||||
BlockState scaffoldState = enderman.getCarriedBlock();
|
||||
if (!isUsefulScaffoldBlock(scaffoldState)) {
|
||||
return false;
|
||||
}
|
||||
BlockPos targetFeet = BlockPos.containing(target.getX(), target.getY(), target.getZ());
|
||||
if (!targetFeet.equals(rampTargetFeet)) {
|
||||
rampTargetFeet = targetFeet;
|
||||
@@ -131,8 +120,7 @@ public class AdaptiveEndermanTacticsGoal extends Goal {
|
||||
int distance = Math.max(1, rampRise - step);
|
||||
BlockPos pos = new BlockPos(targetFeet.getX(), rampBaseY + step, targetFeet.getZ()).relative(rampApproach, distance);
|
||||
if (canPlaceRampStep(level, pos, rampApproach, step == 0)) {
|
||||
BlockState carried = enderman.getCarriedBlock();
|
||||
level.setBlock(pos, isUsefulScaffoldBlock(carried) ? carried : scaffoldState, 3);
|
||||
level.setBlock(pos, scaffoldState, 3);
|
||||
enderman.setCarriedBlock(null);
|
||||
enderman.randomTeleport(pos.getX() + 0.5D, pos.getY() + 1.0D, pos.getZ() + 0.5D, true);
|
||||
AdaptiveAIGoalUtils.debug(enderman, tierSupplier, "enderman placed anti-pillar scaffold");
|
||||
@@ -142,55 +130,11 @@ public class AdaptiveEndermanTacticsGoal extends Goal {
|
||||
return false;
|
||||
}
|
||||
|
||||
private BlockState randomScaffoldState(boolean inEnd) {
|
||||
BlockState[] options = inEnd ? END_SCAFFOLD_BLOCKS : OVERWORLD_SCAFFOLD_BLOCKS;
|
||||
return options[enderman.getRandom().nextInt(options.length)];
|
||||
}
|
||||
|
||||
private void prepareScaffoldBlock(ServerLevel level, BlockState scaffoldState) {
|
||||
BlockState carried = enderman.getCarriedBlock();
|
||||
if (isUsefulScaffoldBlock(carried)) {
|
||||
return;
|
||||
}
|
||||
if (carried != null) {
|
||||
if (!tryPlaceDiscardedBlock(level, carried)) {
|
||||
enderman.spawnAtLocation(new ItemStack(carried.getBlock()));
|
||||
}
|
||||
}
|
||||
enderman.setCarriedBlock(scaffoldState);
|
||||
AdaptiveAIGoalUtils.debug(enderman, tierSupplier, "enderman swapped carried block for scaffold");
|
||||
}
|
||||
|
||||
private boolean isUsefulScaffoldBlock(BlockState state) {
|
||||
return state != null && !state.liquid()
|
||||
&& !state.getCollisionShape(enderman.level(), enderman.blockPosition()).isEmpty();
|
||||
}
|
||||
|
||||
private boolean tryPlaceDiscardedBlock(ServerLevel level, BlockState carried) {
|
||||
BlockPos origin = enderman.blockPosition();
|
||||
BlockPos[] candidates = {
|
||||
origin,
|
||||
origin.relative(Direction.NORTH),
|
||||
origin.relative(Direction.EAST),
|
||||
origin.relative(Direction.SOUTH),
|
||||
origin.relative(Direction.WEST)
|
||||
};
|
||||
for (BlockPos pos : candidates) {
|
||||
if (canPlaceDiscardedBlock(level, carried, pos)) {
|
||||
level.setBlock(pos, carried, 3);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean canPlaceDiscardedBlock(ServerLevel level, BlockState carried, BlockPos pos) {
|
||||
return level.getWorldBorder().isWithinBounds(pos)
|
||||
&& level.getBlockState(pos).isAir()
|
||||
&& !level.getBlockState(pos.below()).getCollisionShape(level, pos.below()).isEmpty()
|
||||
&& carried.canSurvive(level, pos);
|
||||
}
|
||||
|
||||
private Direction sharedRampDirection(BlockPos targetFeet) {
|
||||
Direction[] directions = {Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
|
||||
int index = Math.floorMod(targetFeet.getX() * 31 + targetFeet.getZ() * 17, directions.length);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.fretux.adaptivemobs.ai.goals;
|
||||
|
||||
import net.fretux.adaptivemobs.ai.AdaptiveAIGoalUtils;
|
||||
import net.fretux.adaptivemobs.ai.AdaptiveGoalInjector;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
@@ -20,6 +21,9 @@ public class AdaptiveTargetSanitizerGoal extends Goal {
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
if (!AdaptiveGoalInjector.isAIEnabledFor(mob)) {
|
||||
return false;
|
||||
}
|
||||
LivingEntity target = mob.getTarget();
|
||||
return (target != null && !AdaptiveAIGoalUtils.isValidAdaptiveTarget(target)) || shouldReleaseZombieStack(target);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ import net.minecraftforge.event.entity.living.MobSpawnEvent;
|
||||
import net.minecraftforge.event.level.BlockEvent;
|
||||
import net.minecraftforge.event.level.ExplosionEvent;
|
||||
import net.minecraftforge.event.level.LevelEvent;
|
||||
import net.minecraftforge.event.level.BlockEvent.EntityPlaceEvent;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.server.ServerStoppingEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
public class AdaptiveMobsEvents {
|
||||
@@ -97,6 +99,9 @@ public class AdaptiveMobsEvents {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onBlockBreak(BlockEvent.BreakEvent event) {
|
||||
if (event.getLevel() instanceof ServerLevel level) {
|
||||
AdaptiveSpecialAbilities.invalidateTemporaryBlock(level, event.getPos());
|
||||
}
|
||||
if (AMConfig.enabled && AMConfig.ENABLE_SOUND_INVESTIGATION_AI.get()
|
||||
&& event.getLevel() instanceof ServerLevel level) {
|
||||
if (event.getPlayer() instanceof ServerPlayer player
|
||||
@@ -107,6 +112,13 @@ public class AdaptiveMobsEvents {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onBlockPlace(EntityPlaceEvent event) {
|
||||
if (event.getLevel() instanceof ServerLevel level) {
|
||||
AdaptiveSpecialAbilities.invalidateTemporaryBlock(level, event.getPos());
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onItemUseFinish(LivingEntityUseItemEvent.Finish event) {
|
||||
if (AMConfig.enabled && AMConfig.ENABLE_SOUND_INVESTIGATION_AI.get()
|
||||
@@ -155,6 +167,7 @@ public class AdaptiveMobsEvents {
|
||||
public void onLevelUnload(LevelEvent.Unload event) {
|
||||
if (event.getLevel() instanceof ServerLevel level) {
|
||||
AdaptiveMemory.clear(level);
|
||||
AdaptiveSpecialAbilities.clear(level);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +184,11 @@ public class AdaptiveMobsEvents {
|
||||
AdaptiveSpecialAbilities.onServerTick(event);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onServerStopping(ServerStoppingEvent event) {
|
||||
AdaptiveSpecialAbilities.clear(event.getServer());
|
||||
}
|
||||
|
||||
private static void clearFriendlyFireTolerance(Mob mob) {
|
||||
mob.getPersistentData().remove(FRIENDLY_FIRE_SOURCE_KEY);
|
||||
mob.getPersistentData().remove(FRIENDLY_FIRE_COUNT_KEY);
|
||||
|
||||
@@ -115,6 +115,7 @@ public final class GearScaler {
|
||||
if (tier >= 5 && random.nextDouble() < 0.22D) {
|
||||
bow.enchant(Enchantments.FLAMING_ARROWS, 1);
|
||||
}
|
||||
skeleton.setDropChance(EquipmentSlot.MAINHAND, 0.0F);
|
||||
}
|
||||
|
||||
private static void upgradeCrossbow(Pillager pillager, int tier, RandomSource random) {
|
||||
@@ -130,6 +131,7 @@ public final class GearScaler {
|
||||
} else if (tier >= 5 && random.nextDouble() < 0.35D) {
|
||||
crossbow.enchant(Enchantments.PIERCING, 2);
|
||||
}
|
||||
pillager.setDropChance(EquipmentSlot.MAINHAND, 0.0F);
|
||||
}
|
||||
|
||||
// --- Helpers -----------------------------------------------------------
|
||||
|
||||
@@ -10,7 +10,6 @@ import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
|
||||
/**
|
||||
@@ -66,8 +65,8 @@ public final class SpawnPressureHelper {
|
||||
for (int attempt = 0; attempt < 6; attempt++) {
|
||||
double dx = (random.nextDouble() - 0.5) * radius;
|
||||
double dz = (random.nextDouble() - 0.5) * radius;
|
||||
BlockPos spawnPos = surfaceSpawnPos(level, original.getX() + dx, original.getZ() + dz);
|
||||
if (!level.getWorldBorder().isWithinBounds(spawnPos)) {
|
||||
BlockPos spawnPos = nearbySpawnPos(original, original.getX() + dx, original.getZ() + dz, random);
|
||||
if (!level.hasChunkAt(spawnPos) || !level.getWorldBorder().isWithinBounds(spawnPos)) {
|
||||
continue;
|
||||
}
|
||||
extra.moveTo(spawnPos.getX() + 0.5D, spawnPos.getY(), spawnPos.getZ() + 0.5D,
|
||||
@@ -93,10 +92,12 @@ public final class SpawnPressureHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockPos surfaceSpawnPos(ServerLevel level, double x, double z) {
|
||||
private static BlockPos nearbySpawnPos(Mob original, double x, double z, RandomSource random) {
|
||||
int blockX = BlockPos.containing(x, 0.0D, z).getX();
|
||||
int blockZ = BlockPos.containing(x, 0.0D, z).getZ();
|
||||
int y = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, blockX, blockZ);
|
||||
// Stay in the original mob's vertical habitat. Spawn rules and collision checks below
|
||||
// decide whether a candidate suits ground, cave, aquatic, or airborne mobs.
|
||||
int y = original.blockPosition().getY() + random.nextInt(9) - 4;
|
||||
return new BlockPos(blockX, y, blockZ);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user