properly implement 2 mechanics

This commit is contained in:
2026-07-19 21:06:04 +02:00
parent dc32cac13c
commit 55bdc63514
3 changed files with 112 additions and 20 deletions
@@ -25,13 +25,12 @@ import net.minecraft.world.entity.monster.EnderMan;
import net.minecraft.world.entity.monster.Endermite;
import net.minecraft.world.entity.monster.Evoker;
import net.minecraft.world.entity.monster.Guardian;
import net.minecraft.world.entity.monster.MagmaCube;
import net.minecraft.world.entity.monster.Ghast;
import net.minecraft.world.entity.monster.Phantom;
import net.minecraft.world.entity.monster.Pillager;
import net.minecraft.world.entity.monster.Ravager;
import net.minecraft.world.entity.monster.Shulker;
import net.minecraft.world.entity.monster.Silverfish;
import net.minecraft.world.entity.monster.Slime;
import net.minecraft.world.entity.monster.Spider;
import net.minecraft.world.entity.monster.Stray;
import net.minecraft.world.entity.monster.Vex;
@@ -44,6 +43,7 @@ import net.minecraft.world.entity.monster.piglin.PiglinBrute;
import net.minecraft.world.entity.monster.warden.Warden;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.Arrow;
import net.minecraft.world.entity.projectile.LargeFireball;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.entity.projectile.ShulkerBullet;
import net.minecraft.world.entity.player.Player;
@@ -60,6 +60,7 @@ import java.util.List;
public final class AdaptiveSpecialAbilities {
private static final String COOLDOWN_PREFIX = "am_special_cd_";
private static final String SPLIT_FIREBALL_KEY = "am_split_fireball";
private static final List<TemporaryBlock> TEMPORARY_BLOCKS = new ArrayList<>();
private static final List<DelayedReappear> DELAYED_REAPPEARS = new ArrayList<>();
@@ -75,6 +76,10 @@ public final class AdaptiveSpecialAbilities {
return;
}
int tier = tier(level, mob);
if (tier >= 5 && mob instanceof Ghast ghast && projectile instanceof LargeFireball fireball) {
splitGhastFireball(level, ghast, fireball);
return;
}
if (tier < 4 || !(projectile instanceof Arrow arrow)) {
return;
}
@@ -267,13 +272,6 @@ public final class AdaptiveSpecialAbilities {
player.addEffect(new MobEffectInstance(MobEffects.LEVITATION, 80, 0));
player.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 50, 0));
}
if ((mob instanceof Slime || mob instanceof MagmaCube) && tier >= 5 && mob.getBbWidth() <= 1.5F) {
mob.addEffect(new MobEffectInstance(MobEffects.DAMAGE_BOOST, 80, 0));
mob.addEffect(new MobEffectInstance(MobEffects.JUMP, 80, 1));
if (mob instanceof MagmaCube) {
player.setSecondsOnFire(2);
}
}
if (mob instanceof Phantom && tier >= 5) {
player.addEffect(new MobEffectInstance(MobEffects.HUNGER, 100, 0));
}
@@ -338,6 +336,37 @@ public final class AdaptiveSpecialAbilities {
return directEntity instanceof Projectile;
}
private static void splitGhastFireball(ServerLevel level, Ghast ghast, LargeFireball original) {
if (original.getPersistentData().getBoolean(SPLIT_FIREBALL_KEY)) {
return;
}
Vec3 direction = original.getDeltaMovement();
if (direction.lengthSqr() < 1.0E-4D && AdaptiveAIGoalUtils.isValidAdaptiveTarget(ghast.getTarget())) {
direction = ghast.getTarget().getEyePosition().subtract(original.position());
}
if (direction.lengthSqr() < 1.0E-4D) {
return;
}
direction = direction.normalize();
Vec3 side = new Vec3(-direction.z, 0.0D, direction.x);
if (side.lengthSqr() < 1.0E-4D) {
side = new Vec3(1.0D, 0.0D, 0.0D);
} else {
side = side.normalize();
}
spawnSplitFireball(level, ghast, original, direction.add(side.scale(0.18D)).normalize());
spawnSplitFireball(level, ghast, original, direction.subtract(side.scale(0.18D)).normalize());
AdaptiveAIGoalUtils.debug(ghast, () -> 5, "ghast splitting fireball");
}
private static void spawnSplitFireball(ServerLevel level, Ghast ghast, LargeFireball original, Vec3 direction) {
LargeFireball split = new LargeFireball(level, ghast, direction.x, direction.y, direction.z,
ghast.getExplosionPower());
split.getPersistentData().putBoolean(SPLIT_FIREBALL_KEY, true);
split.setPos(original.getX(), original.getY(), original.getZ());
level.addFreshEntity(split);
}
private record TemporaryBlock(net.minecraft.resources.ResourceKey<net.minecraft.world.level.Level> dimension,
BlockPos pos, long expireTick) {
}
@@ -9,39 +9,63 @@ import net.minecraft.world.entity.monster.MagmaCube;
import net.minecraft.world.entity.monster.Slime;
import net.minecraft.world.phys.Vec3;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.function.IntSupplier;
public class AdaptiveSlimeTacticsGoal extends Goal {
private static final String RECOMBINE_READY_AT_KEY = "am_recombine_ready_at";
private static final int MAX_RECOMBINE_SIZE = 4;
private static final double RECOMBINE_SEARCH_RADIUS = 12.0D;
private static final double RECOMBINE_DISTANCE_SQR = 3.24D;
private final Slime slime;
private final IntSupplier tierSupplier;
private int cooldown;
private int recombineCheckCooldown;
private Slime recombinePartner;
public AdaptiveSlimeTacticsGoal(Slime slime, IntSupplier tierSupplier) {
this.slime = slime;
this.tierSupplier = tierSupplier;
setFlags(EnumSet.of(Flag.MOVE, Flag.JUMP, Flag.LOOK));
scheduleRecombine(200 + slime.getRandom().nextInt(101));
}
@Override
public boolean canUse() {
boolean magma = slime instanceof MagmaCube;
return AMConfig.ENABLE_MELEE_AI.get()
&& (magma ? AMConfig.ENABLE_MAGMA_CUBE_ADVANCED_AI.get() : AMConfig.ENABLE_SLIME_ADVANCED_AI.get())
&& AMConfig.AI_SLIME.get()
&& tierSupplier.getAsInt() >= 2
&& slime.getTarget() != null
&& slime.getTarget().isAlive()
&& --cooldown <= 0;
if (!AMConfig.ENABLE_MELEE_AI.get()
|| !(magma ? AMConfig.ENABLE_MAGMA_CUBE_ADVANCED_AI.get() : AMConfig.ENABLE_SLIME_ADVANCED_AI.get())
|| !AMConfig.AI_SLIME.get() || tierSupplier.getAsInt() < 2) {
return false;
}
recombinePartner = null;
if (tierSupplier.getAsInt() >= 5 && slime.getSize() < MAX_RECOMBINE_SIZE
&& slime.level().getGameTime() >= slime.getPersistentData().getLong(RECOMBINE_READY_AT_KEY)
&& --recombineCheckCooldown <= 0) {
recombineCheckCooldown = 10;
recombinePartner = findRecombinePartner();
if (recombinePartner != null) {
return true;
}
}
return AdaptiveAIGoalUtils.isValidAdaptiveTarget(slime.getTarget()) && --cooldown <= 0;
}
@Override
public void start() {
int tier = tierSupplier.getAsInt();
if (recombinePartner != null && recombinePartner.isAlive()) {
tryRecombine(recombinePartner);
recombinePartner = null;
return;
}
cooldown = Math.max(16, 52 - tier * 5) + slime.getRandom().nextInt(25);
LivingEntity target = slime.getTarget();
if (target == null) {
if (!AdaptiveAIGoalUtils.isValidAdaptiveTarget(target)) {
return;
}
double distance = Math.sqrt(slime.distanceToSqr(target));
@@ -59,4 +83,40 @@ public class AdaptiveSlimeTacticsGoal extends Goal {
}
AdaptiveAIGoalUtils.debug(slime, tierSupplier, "slime angled jump pressure");
}
private Slime findRecombinePartner() {
boolean magma = slime instanceof MagmaCube;
List<Slime> candidates = slime.level().getEntitiesOfClass(Slime.class,
slime.getBoundingBox().inflate(RECOMBINE_SEARCH_RADIUS),
other -> other != slime && other.isAlive() && other.getSize() == slime.getSize()
&& (other instanceof MagmaCube) == magma
&& other.level().getGameTime() >= other.getPersistentData().getLong(RECOMBINE_READY_AT_KEY));
return candidates.stream().min(Comparator.comparingDouble(slime::distanceToSqr)).orElse(null);
}
private void tryRecombine(Slime partner) {
if (slime.distanceToSqr(partner) > RECOMBINE_DISTANCE_SQR) {
slime.getLookControl().setLookAt(partner, 30.0F, 30.0F);
slime.getMoveControl().setWantedPosition(partner.getX(), partner.getY(), partner.getZ(), 1.2D);
return;
}
LivingEntity inheritedTarget = AdaptiveAIGoalUtils.isValidAdaptiveTarget(slime.getTarget())
? slime.getTarget() : partner.getTarget();
partner.discard();
slime.setSize(Math.min(MAX_RECOMBINE_SIZE, slime.getSize() * 2), true);
if (AdaptiveAIGoalUtils.isValidAdaptiveTarget(inheritedTarget)) {
slime.setTarget(inheritedTarget);
}
scheduleRecombine(200 + slime.getRandom().nextInt(101));
AdaptiveAIGoalUtils.debug(slime, tierSupplier,
slime instanceof MagmaCube ? "magma cube recombined" : "slime recombined");
}
private void scheduleRecombine(int delayTicks) {
if (slime.getPersistentData().getLong(RECOMBINE_READY_AT_KEY) == 0L) {
slime.getPersistentData().putLong(RECOMBINE_READY_AT_KEY, slime.level().getGameTime() + delayTicks);
} else if (slime.getSize() > 1) {
slime.getPersistentData().putLong(RECOMBINE_READY_AT_KEY, slime.level().getGameTime() + delayTicks);
}
}
}