fix various issues

This commit is contained in:
2026-07-20 01:23:59 +02:00
parent 85a96a6678
commit 36bb227fd3
7 changed files with 125 additions and 85 deletions
@@ -75,6 +75,7 @@ import net.minecraft.world.entity.monster.Zoglin;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.BooleanSupplier;
import java.util.function.IntSupplier;
public final class AdaptiveGoalInjector {
@@ -99,22 +100,21 @@ public final class AdaptiveGoalInjector {
mob.targetSelector.addGoal(2, new AdaptiveSharedTargetGoal(mob, tier, 4, 12.0D));
if (pathfinder != null && mob instanceof AbstractSkeleton && AMConfig.AI_SKELETON.get()) {
if (pathfinder != null && mob instanceof AbstractSkeleton) {
mob.goalSelector.addGoal(3, new AdaptiveSkeletonShieldGoal(pathfinder, tier));
mob.goalSelector.addGoal(4, new AdaptiveFlankGoal(pathfinder, tier, 2, 8.0D, 1.0D, true));
} else if (pathfinder != null && mob instanceof Zombie && AMConfig.AI_ZOMBIE.get()) {
} else if (pathfinder != null && mob instanceof Zombie) {
mob.goalSelector.addGoal(3, new AdaptiveFlankGoal(pathfinder, tier, 2, 2.5D, 1.05D, true));
// Priority 1: must be able to interrupt the vanilla melee attack goal (priority 2),
// otherwise it can never take over while the zombie is already fighting.
mob.goalSelector.addGoal(1, new AdaptiveZombiePackTacticsGoal((Zombie) mob, tier));
} else if (pathfinder != null && mob instanceof Creeper creeper && AMConfig.AI_CREEPER.get()) {
} else if (pathfinder != null && mob instanceof Creeper creeper) {
creeper.maxSwell = creeperFuseTicks(tier.getAsInt());
mob.goalSelector.addGoal(2, new AdaptiveCreeperPressureGoal(creeper, tier));
mob.goalSelector.addGoal(4, new AdaptiveFlankGoal(pathfinder, tier, 2, 3.0D, 1.0D, false));
} else if (pathfinder != null && mob instanceof Spider && AMConfig.AI_SPIDER.get()) {
} else if (pathfinder != null && mob instanceof Spider) {
mob.goalSelector.addGoal(4, new AdaptiveFlankGoal(pathfinder, tier, 2, 2.0D, 1.15D, false));
} else if (pathfinder != null && ((mob instanceof Witch && AMConfig.AI_WITCH.get())
|| (mob instanceof Pillager && AMConfig.AI_PILLAGER.get()))) {
} else if (pathfinder != null && (mob instanceof Witch || mob instanceof Pillager)) {
mob.goalSelector.addGoal(4, new AdaptiveFlankGoal(pathfinder, tier, 2, 7.0D, 1.0D, false));
}
@@ -125,100 +125,111 @@ public final class AdaptiveGoalInjector {
}
private static void injectAdvanced(Mob mob, PathfinderMob pathfinder, IntSupplier tier) {
if (AMConfig.ENABLE_TARGET_PRIORITY_AI.get()) {
mob.targetSelector.addGoal(3, new AdaptiveTargetPriorityGoal(mob, tier));
mob.targetSelector.addGoal(3, new AdaptiveTargetPriorityGoal(mob,
gated(tier, () -> AMConfig.ENABLE_TARGET_PRIORITY_AI.get())));
if (pathfinder != null && !(mob instanceof Warden)) {
mob.goalSelector.addGoal(8, new AdaptiveMoraleGoal(pathfinder,
gated(tier, () -> AMConfig.ENABLE_MORALE_AI.get())));
mob.goalSelector.addGoal(8, new AdaptiveSupportGoal(pathfinder,
gated(tier, () -> AMConfig.ENABLE_SUPPORT_AI.get())));
}
if (pathfinder != null && AMConfig.ENABLE_MORALE_AI.get() && !(mob instanceof Warden)) {
mob.goalSelector.addGoal(8, new AdaptiveMoraleGoal(pathfinder, tier));
if (pathfinder != null) {
mob.goalSelector.addGoal(2, new AdaptiveThreatMemoryGoal(pathfinder,
gated(tier, () -> AMConfig.ENABLE_THREAT_MEMORY_AI.get())));
mob.goalSelector.addGoal(9, new AdaptiveSoundInvestigationGoal(pathfinder,
gated(tier, () -> AMConfig.ENABLE_SOUND_INVESTIGATION_AI.get())));
mob.goalSelector.addGoal(8, new AdaptiveBiomeTacticsGoal(pathfinder,
gated(tier, () -> AMConfig.ENABLE_BIOME_TACTICS_AI.get())));
mob.goalSelector.addGoal(0, new AdaptiveAntiCheeseGoal(pathfinder,
gated(tier, () -> AMConfig.ENABLE_ANTI_CHEESE_AI.get())));
}
if (pathfinder != null && AMConfig.ENABLE_THREAT_MEMORY_AI.get()) {
mob.goalSelector.addGoal(2, new AdaptiveThreatMemoryGoal(pathfinder, tier));
if (pathfinder != null && (mob instanceof Zombie || mob instanceof Spider)) {
mob.goalSelector.addGoal(6, new AdaptiveEscapeDenialGoal(pathfinder,
gated(tier, () -> AMConfig.ENABLE_ESCAPE_DENIAL_AI.get())));
}
if (pathfinder != null && AMConfig.ENABLE_SOUND_INVESTIGATION_AI.get()) {
mob.goalSelector.addGoal(9, new AdaptiveSoundInvestigationGoal(pathfinder, tier));
}
if (pathfinder != null && AMConfig.ENABLE_SUPPORT_AI.get() && !(mob instanceof Warden)) {
mob.goalSelector.addGoal(8, new AdaptiveSupportGoal(pathfinder, tier));
}
if (pathfinder != null && AMConfig.ENABLE_ESCAPE_DENIAL_AI.get()
&& (mob instanceof Zombie || mob instanceof Spider)) {
mob.goalSelector.addGoal(6, new AdaptiveEscapeDenialGoal(pathfinder, tier));
}
if (pathfinder != null && AMConfig.ENABLE_BIOME_TACTICS_AI.get()) {
mob.goalSelector.addGoal(8, new AdaptiveBiomeTacticsGoal(pathfinder, tier));
}
if (pathfinder != null && AMConfig.ENABLE_ANTI_CHEESE_AI.get()) {
mob.goalSelector.addGoal(0, new AdaptiveAntiCheeseGoal(pathfinder, tier));
}
if (pathfinder != null && isRangedMob(mob) && rangedEnabled(mob)) {
mob.goalSelector.addGoal(3, new AdaptiveRangedAttackGoal(pathfinder, tier, rangedOptimalRange(mob), 1.0D));
if (AMConfig.ENABLE_RANGED_COVER_AI.get()
&& (mob instanceof AbstractSkeleton || mob instanceof Pillager || mob instanceof Witch || mob instanceof Evoker)) {
mob.goalSelector.addGoal(6, new AdaptiveRangedCoverGoal(pathfinder, tier));
if (pathfinder != null && isRangedMob(mob)) {
mob.goalSelector.addGoal(3, new AdaptiveRangedAttackGoal(pathfinder,
gated(tier, () -> rangedEnabled(mob)), rangedOptimalRange(mob), 1.0D));
if (mob instanceof AbstractSkeleton || mob instanceof Pillager || mob instanceof Witch || mob instanceof Evoker) {
mob.goalSelector.addGoal(6, new AdaptiveRangedCoverGoal(pathfinder,
gated(tier, () -> rangedEnabled(mob) && AMConfig.ENABLE_RANGED_COVER_AI.get())));
}
}
if (pathfinder != null && isMeleeMob(mob) && meleeEnabled(mob)) {
mob.goalSelector.addGoal(5, new AdaptiveMeleePositioningGoal(pathfinder, tier, meleeSpeed(mob), conservativeMelee(mob)));
if (pathfinder != null && isMeleeMob(mob)) {
mob.goalSelector.addGoal(5, new AdaptiveMeleePositioningGoal(pathfinder,
gated(tier, () -> meleeEnabled(mob)), meleeSpeed(mob), conservativeMelee(mob)));
}
if (mob instanceof Creeper creeper && AMConfig.ENABLE_CREEPER_AI.get() && AMConfig.AI_CREEPER.get()) {
mob.goalSelector.addGoal(3, new AdaptiveCreeperTacticsGoal(creeper, tier));
if (mob instanceof Creeper creeper) {
mob.goalSelector.addGoal(3, new AdaptiveCreeperTacticsGoal(creeper,
gated(tier, () -> AMConfig.ENABLE_CREEPER_AI.get() && AMConfig.AI_CREEPER.get())));
}
if (pathfinder != null && mob instanceof Spider && AMConfig.ENABLE_SPIDER_AI.get() && AMConfig.AI_SPIDER.get()) {
if (pathfinder != null && mob instanceof Spider) {
IntSupplier spiderTier = gated(tier, () -> AMConfig.ENABLE_SPIDER_AI.get() && AMConfig.AI_SPIDER.get());
// Priority 2: below disengage (1, flee always wins) but still above the vanilla
// attack goal (3) so it can actually interrupt combat instead of being starved by it.
mob.goalSelector.addGoal(2, new AdaptiveSpiderTacticsGoal(pathfinder, tier, mob instanceof CaveSpider));
mob.goalSelector.addGoal(1, new AdaptiveSpiderDisengageGoal(pathfinder, tier, mob instanceof CaveSpider ? 3 : 4, 0.14D, 1.3D));
mob.goalSelector.addGoal(5, new AdaptiveLowHealthRetreatGoal(pathfinder, tier, 4, 0.38D, 1.15D));
mob.goalSelector.addGoal(2, new AdaptiveSpiderTacticsGoal(pathfinder, spiderTier, mob instanceof CaveSpider));
mob.goalSelector.addGoal(1, new AdaptiveSpiderDisengageGoal(pathfinder, spiderTier, mob instanceof CaveSpider ? 3 : 4, 0.14D, 1.3D));
mob.goalSelector.addGoal(5, new AdaptiveLowHealthRetreatGoal(pathfinder, spiderTier, 4, 0.38D, 1.15D));
}
if (mob instanceof Witch witch && AMConfig.ENABLE_WITCH_AI.get() && AMConfig.AI_WITCH.get()) {
mob.goalSelector.addGoal(3, new AdaptiveWitchTacticsGoal(witch, tier));
mob.goalSelector.addGoal(7, new AdaptiveLowHealthRetreatGoal(witch, tier, 3, 0.35D, 1.0D));
if (mob instanceof Witch witch) {
IntSupplier witchTier = gated(tier, () -> AMConfig.ENABLE_WITCH_AI.get() && AMConfig.AI_WITCH.get());
mob.goalSelector.addGoal(3, new AdaptiveWitchTacticsGoal(witch, witchTier));
mob.goalSelector.addGoal(7, new AdaptiveLowHealthRetreatGoal(witch, witchTier, 3, 0.35D, 1.0D));
}
if (mob instanceof EnderMan enderman && AMConfig.ENABLE_ENDERMAN_AI.get() && AMConfig.AI_ENDERMAN.get()) {
mob.goalSelector.addGoal(2, new AdaptiveEndermanTacticsGoal(enderman, tier));
if (mob instanceof EnderMan enderman) {
mob.goalSelector.addGoal(2, new AdaptiveEndermanTacticsGoal(enderman,
gated(tier, () -> AMConfig.ENABLE_ENDERMAN_AI.get() && AMConfig.AI_ENDERMAN.get())));
}
if (pathfinder != null && mob instanceof Endermite && AMConfig.ENABLE_ENDERMITE_AI.get() && AMConfig.AI_SMALL_SWARM.get()) {
mob.goalSelector.addGoal(4, new AdaptiveEndermiteTacticsGoal(pathfinder, tier));
if (pathfinder != null && mob instanceof Endermite) {
mob.goalSelector.addGoal(4, new AdaptiveEndermiteTacticsGoal(pathfinder,
gated(tier, () -> AMConfig.ENABLE_ENDERMITE_AI.get() && AMConfig.AI_SMALL_SWARM.get())));
}
if (mob instanceof Evoker evoker && AMConfig.ENABLE_RAID_MOB_AI.get()
&& AMConfig.ENABLE_EVOKER_AI.get() && AMConfig.AI_EVOKER.get()) {
mob.goalSelector.addGoal(3, new AdaptiveEvokerTacticsGoal(evoker, tier));
if (mob instanceof Evoker evoker) {
mob.goalSelector.addGoal(3, new AdaptiveEvokerTacticsGoal(evoker,
gated(tier, () -> AMConfig.ENABLE_RAID_MOB_AI.get()
&& AMConfig.ENABLE_EVOKER_AI.get() && AMConfig.AI_EVOKER.get())));
}
if (mob instanceof Guardian guardian && AMConfig.ENABLE_AQUATIC_HOSTILE_AI.get()
&& AMConfig.ENABLE_GUARDIAN_ADVANCED_AI.get() && AMConfig.AI_GUARDIAN.get()) {
mob.goalSelector.addGoal(4, new AdaptiveGuardianTacticsGoal(guardian, tier));
if (mob instanceof Guardian guardian) {
mob.goalSelector.addGoal(4, new AdaptiveGuardianTacticsGoal(guardian,
gated(tier, () -> AMConfig.ENABLE_AQUATIC_HOSTILE_AI.get()
&& AMConfig.ENABLE_GUARDIAN_ADVANCED_AI.get() && AMConfig.AI_GUARDIAN.get())));
}
if (mob instanceof Shulker shulker && AMConfig.ENABLE_END_MOB_AI.get()
&& AMConfig.ENABLE_SHULKER_ADVANCED_AI.get() && AMConfig.AI_SHULKER.get()) {
mob.goalSelector.addGoal(4, new AdaptiveShulkerTacticsGoal(shulker, tier));
if (mob instanceof Shulker shulker) {
mob.goalSelector.addGoal(4, new AdaptiveShulkerTacticsGoal(shulker,
gated(tier, () -> AMConfig.ENABLE_END_MOB_AI.get()
&& AMConfig.ENABLE_SHULKER_ADVANCED_AI.get() && AMConfig.AI_SHULKER.get())));
}
if (mob instanceof Slime slime && AMConfig.AI_SLIME.get()
if (mob instanceof Slime slime) {
mob.goalSelector.addGoal(4, new AdaptiveSlimeTacticsGoal(slime,
gated(tier, () -> AMConfig.AI_SLIME.get()
&& ((mob instanceof MagmaCube && AMConfig.ENABLE_MAGMA_CUBE_ADVANCED_AI.get())
|| (!(mob instanceof MagmaCube) && AMConfig.ENABLE_SLIME_ADVANCED_AI.get()))) {
mob.goalSelector.addGoal(4, new AdaptiveSlimeTacticsGoal(slime, tier));
|| (!(mob instanceof MagmaCube) && AMConfig.ENABLE_SLIME_ADVANCED_AI.get())))));
}
if (mob instanceof Ravager ravager && AMConfig.ENABLE_RAID_MOB_AI.get()
&& AMConfig.ENABLE_RAVAGER_ADVANCED_AI.get() && AMConfig.AI_RAVAGER.get()) {
mob.goalSelector.addGoal(4, new AdaptiveRavagerTacticsGoal(ravager, tier));
if (mob instanceof Ravager ravager) {
mob.goalSelector.addGoal(4, new AdaptiveRavagerTacticsGoal(ravager,
gated(tier, () -> AMConfig.ENABLE_RAID_MOB_AI.get()
&& AMConfig.ENABLE_RAVAGER_ADVANCED_AI.get() && AMConfig.AI_RAVAGER.get())));
}
if (isAirborneMob(mob) && airborneEnabled(mob)) {
mob.goalSelector.addGoal(4, new AdaptiveAirbornePositioningGoal(mob, tier, airborneRange(mob), airborneVerticalOffset(mob)));
if (isAirborneMob(mob)) {
mob.goalSelector.addGoal(4, new AdaptiveAirbornePositioningGoal(mob,
gated(tier, () -> airborneEnabled(mob)), airborneRange(mob), airborneVerticalOffset(mob)));
}
if (pathfinder != null && mob instanceof Vex && AMConfig.ENABLE_RAID_MOB_AI.get() && AMConfig.AI_VEX.get()) {
mob.goalSelector.addGoal(4, new AdaptiveRetreatAndReengageGoal(pathfinder, tier, 5, 0.10D, 1.2D));
mob.goalSelector.addGoal(7, new AdaptiveLowHealthRetreatGoal(pathfinder, tier, 4, 0.30D, 1.2D));
if (pathfinder != null && mob instanceof Vex) {
IntSupplier vexTier = gated(tier, () -> AMConfig.ENABLE_RAID_MOB_AI.get() && AMConfig.AI_VEX.get());
mob.goalSelector.addGoal(4, new AdaptiveRetreatAndReengageGoal(pathfinder, vexTier, 5, 0.10D, 1.2D));
mob.goalSelector.addGoal(7, new AdaptiveLowHealthRetreatGoal(pathfinder, vexTier, 4, 0.30D, 1.2D));
}
if (pathfinder != null && mob instanceof AbstractSkeleton && AMConfig.AI_SKELETON.get()) {
if (pathfinder != null && mob instanceof AbstractSkeleton) {
mob.goalSelector.addGoal(7, new AdaptiveLowHealthRetreatGoal(pathfinder, tier, 4, 0.25D, 1.0D));
}
if (pathfinder != null && mob instanceof Pillager && AMConfig.AI_PILLAGER.get()) {
if (pathfinder != null && mob instanceof Pillager) {
mob.goalSelector.addGoal(7, new AdaptiveLowHealthRetreatGoal(pathfinder, tier, 4, 0.28D, 1.0D));
}
if (pathfinder != null && mob instanceof Warden && AMConfig.ENABLE_WARDEN_AI.get() && AMConfig.AI_WARDEN.get()) {
mob.goalSelector.addGoal(7, new AdaptiveMeleePositioningGoal(pathfinder, tier, 0.9D, true));
if (AMConfig.ENABLE_WARDEN_ADVANCED_AI.get()) {
mob.goalSelector.addGoal(8, new AdaptiveWardenTacticsGoal((Warden) mob, tier));
}
if (pathfinder != null && mob instanceof Warden warden) {
IntSupplier wardenTier = gated(tier, () -> AMConfig.ENABLE_WARDEN_AI.get() && AMConfig.AI_WARDEN.get());
mob.goalSelector.addGoal(7, new AdaptiveMeleePositioningGoal(pathfinder, wardenTier, 0.9D, true));
mob.goalSelector.addGoal(8, new AdaptiveWardenTacticsGoal(warden,
gated(wardenTier, () -> AMConfig.ENABLE_WARDEN_ADVANCED_AI.get())));
}
}
@@ -310,6 +321,10 @@ public final class AdaptiveGoalInjector {
return 1.05D;
}
private static IntSupplier gated(IntSupplier tier, BooleanSupplier enabled) {
return () -> enabled.getAsBoolean() ? tier.getAsInt() : 0;
}
public static boolean isAIEnabledFor(Mob mob) {
if (!AMConfig.enabled || !AMConfig.AI_ENABLED.get() || !AMConfig.isMobEnabled(mob.getType())) {
return false;
@@ -44,9 +44,12 @@ public final class AdaptiveMemory {
return Optional.empty();
}
ServerPlayer player = level.getServer().getPlayerList().getPlayer(entry.playerId);
return AdaptiveAIGoalUtils.isValidAdaptiveTarget(player) && mob.canAttack(player)
? Optional.of(player)
: Optional.empty();
if (player == null || player.level() != level
|| !AdaptiveAIGoalUtils.isValidAdaptiveTarget(player) || !mob.canAttack(player)) {
THREATS.remove(mob);
return Optional.empty();
}
return Optional.of(player);
}
public static void rememberSound(ServerLevel level, BlockPos pos, SoundKind kind, int ticks) {
@@ -382,12 +382,29 @@ public final class AdaptiveSpecialAbilities {
}
public static void clear(ServerLevel level) {
TEMPORARY_BLOCKS.removeIf(block -> block.server == level.getServer() && block.dimension == level.dimension());
TEMPORARY_BLOCKS.removeIf(block -> {
if (block.server != level.getServer() || block.dimension != level.dimension()) {
return false;
}
if (level.getBlockState(block.pos).is(Blocks.COBWEB)) {
level.removeBlock(block.pos, false);
}
return true;
});
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);
TEMPORARY_BLOCKS.removeIf(block -> {
if (block.server != server) {
return false;
}
ServerLevel level = server.getLevel(block.dimension);
if (level != null && level.getBlockState(block.pos).is(Blocks.COBWEB)) {
level.removeBlock(block.pos, false);
}
return true;
});
DELAYED_REAPPEARS.removeIf(pending -> pending.server == server);
}
@@ -35,7 +35,8 @@ public class AdaptiveThreatMemoryGoal extends Goal {
return;
}
Player player = remembered.get();
if (!AdaptiveAIGoalUtils.isValidAdaptiveTarget(player) || !mob.canAttack(player)) {
if (player.level() != mob.level()
|| !AdaptiveAIGoalUtils.isValidAdaptiveTarget(player) || !mob.canAttack(player)) {
return;
}
if (mob.getTarget() == null || !mob.getTarget().isAlive()) {
@@ -153,7 +153,9 @@ public class AdaptiveMobsEvents {
return;
}
Entity entity = event.getEntity();
if (AMConfig.enabled && entity instanceof Mob mob && mob instanceof Enemy && AMConfig.isMobEnabled(mob.getType())) {
// Install dormant, config-gated goals on every supported hostile so a later config reload
// can enable them without requiring the entity to unload and join again.
if (entity instanceof Mob mob && mob instanceof Enemy) {
AdaptiveGoalInjector.inject(mob, level, AdaptiveDifficultyManager.getMobTier(level, mob.getType()));
}
if (AMConfig.enabled && AMConfig.ENABLE_SOUND_INVESTIGATION_AI.get()
@@ -115,7 +115,6 @@ 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) {
@@ -131,7 +130,6 @@ public final class GearScaler {
} else if (tier >= 5 && random.nextDouble() < 0.35D) {
crossbow.enchant(Enchantments.PIERCING, 2);
}
pillager.setDropChance(EquipmentSlot.MAINHAND, 0.0F);
}
// --- Helpers -----------------------------------------------------------
@@ -2,6 +2,8 @@ package net.fretux.adaptivemobs.spawn;
import net.fretux.adaptivemobs.AdaptiveMobs;
import net.fretux.adaptivemobs.config.AMConfig;
import net.fretux.adaptivemobs.gear.GearScaler;
import net.fretux.adaptivemobs.scaling.MobStatScaler;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
@@ -77,6 +79,8 @@ public final class SpawnPressureHelper {
&& extra.checkSpawnObstruction(level)) {
extra.finalizeSpawn(level, level.getCurrentDifficultyAt(extra.blockPosition()),
MobSpawnType.NATURAL, null, null);
MobStatScaler.applyScaling(extra, tier);
GearScaler.applyGear(extra, tier);
level.addFreshEntity(extra);
if (AMConfig.debug) {
AdaptiveMobs.LOGGER.info("[AdaptiveMobs] Extra spawn ({}) at tier {} near {}",