Initial commit

This commit is contained in:
2026-06-29 15:59:48 +02:00
commit 7fe5ab4e24
67 changed files with 7661 additions and 0 deletions
@@ -0,0 +1,255 @@
package net.fretux.adaptivemobs.ai.goals;
import net.fretux.adaptivemobs.ai.AdaptiveAIGoalUtils;
import net.fretux.adaptivemobs.ai.AdaptivePositioningUtils;
import net.fretux.adaptivemobs.config.AMConfig;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.monster.AbstractSkeleton;
import net.minecraft.world.entity.monster.Creeper;
import net.minecraft.world.entity.monster.EnderMan;
import net.minecraft.world.entity.monster.Pillager;
import net.minecraft.world.entity.monster.Zombie;
import net.minecraft.world.entity.vehicle.AbstractMinecart;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import java.util.EnumSet;
import java.util.List;
import java.util.function.IntSupplier;
public class AdaptiveAntiCheeseGoal extends Goal {
private final PathfinderMob mob;
private final IntSupplier tierSupplier;
private int cooldown;
public AdaptiveAntiCheeseGoal(PathfinderMob mob, IntSupplier tierSupplier) {
this.mob = mob;
this.tierSupplier = tierSupplier;
setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
}
@Override
public boolean canUse() {
return AMConfig.ENABLE_ANTI_CHEESE_AI.get()
&& tierSupplier.getAsInt() >= 3
&& mob.getTarget() != null
&& mob.getTarget().isAlive()
&& --cooldown <= 0;
}
@Override
public void start() {
cooldown = 55 + mob.getRandom().nextInt(80);
LivingEntity target = mob.getTarget();
if (target == null) {
return;
}
mob.getLookControl().setLookAt(target, 30.0F, 30.0F);
if (avoidTrap()) {
return;
}
if (mob instanceof Zombie && isVerticalObstacleCheese(target)) {
if (tryCorpseLadderSurge(target)) {
return;
}
if (tryZombieStack(target)) {
return;
}
Vec3 dest = AdaptivePositioningUtils.darkerSidePosition(mob, target, 2.5D, 0.0D);
moveIfReasonable(dest, 1.0D, "zombie pillar gather");
return;
}
if ((mob instanceof AbstractSkeleton || mob instanceof Pillager) && !mob.hasLineOfSight(target)) {
Vec3 dest = AdaptivePositioningUtils.darkerSidePosition(mob, target, 7.0D, 0.0D);
moveIfReasonable(dest, 1.0D, "ranged tiny-window reposition");
return;
}
if (mob instanceof Creeper creeper && target.hasLineOfSight(creeper)
&& AdaptivePositioningUtils.isOpenToSky(mob)
&& Math.sqrt(mob.distanceToSqr(target)) > 4.0D) {
creeper.setSwellDir(-1);
Vec3 dest = AdaptivePositioningUtils.darkerSidePosition(mob, target, 5.0D, 0.0D);
moveIfReasonable(dest, 1.0D, "creeper open-ground hesitation");
return;
}
if (mob instanceof EnderMan && (mob.level().getFluidState(mob.blockPosition()).is(FluidTags.WATER)
|| target.level().getFluidState(target.blockPosition()).is(FluidTags.WATER))) {
Vec3 dest = AdaptivePositioningUtils.retreatPosition(mob, target, 7.0D);
moveIfReasonable(dest, 1.0D, "enderman water disengage");
}
}
private boolean avoidTrap() {
Entity vehicle = mob.getVehicle();
if (vehicle instanceof Boat || vehicle instanceof AbstractMinecart) {
mob.stopRiding();
moveAwayFrom(vehicle.position(), 1.15D, "trap vehicle escape");
return true;
}
Entity trapVehicle = nearestTrapVehicle();
if (trapVehicle != null && mob.distanceToSqr(trapVehicle) < 2.9D) {
moveAwayFrom(trapVehicle.position(), 1.05D, "trap vehicle avoidance");
return true;
}
Vec3 trapBlock = nearestTrapBlock();
if (trapBlock != null) {
moveAwayFrom(trapBlock, 1.05D, "trap block avoidance");
return true;
}
return false;
}
private Entity nearestTrapVehicle() {
List<Entity> vehicles = mob.level().getEntitiesOfClass(Entity.class, mob.getBoundingBox().inflate(2.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 Vec3 nearestTrapBlock() {
Vec3 nearest = null;
double best = Double.MAX_VALUE;
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
for (int z = -1; z <= 1; z++) {
BlockState state = mob.level().getBlockState(mob.blockPosition().offset(x, y, z));
if (!isTrapBlock(state)) {
continue;
}
Vec3 pos = Vec3.atCenterOf(mob.blockPosition().offset(x, y, z));
double dist = mob.position().distanceToSqr(pos);
if (dist < best) {
best = dist;
nearest = pos;
}
}
}
}
return nearest;
}
private boolean isTrapBlock(BlockState state) {
return state.is(Blocks.COBWEB)
|| state.is(Blocks.POWDER_SNOW)
|| state.is(Blocks.SWEET_BERRY_BUSH);
}
private void moveAwayFrom(Vec3 threat, double speed, String debug) {
Vec3 away = mob.position().subtract(threat);
double len = Math.sqrt(away.x * away.x + away.z * away.z);
if (len < 1.0E-3D) {
away = new Vec3(mob.getRandom().nextBoolean() ? 1.0D : -1.0D, 0.0D, 0.0D);
len = 1.0D;
}
Vec3 dest = mob.position().add(away.x / len * 4.0D, 0.0D, away.z / len * 4.0D);
moveIfReasonable(dest, speed, debug);
}
private boolean tryZombieStack(LivingEntity target) {
if (mob.isPassenger() || mob.isVehicle()) {
return false;
}
List<Zombie> carriers = mob.level().getEntitiesOfClass(Zombie.class, mob.getBoundingBox().inflate(2.0D),
zombie -> zombie != mob
&& zombie.isAlive()
&& !zombie.isVehicle()
&& zombie.getTarget() == target
&& Math.abs(zombie.getY() - mob.getY()) < 1.25D);
Zombie bestCarrier = null;
double best = Double.MAX_VALUE;
for (Zombie carrier : carriers) {
double dist = mob.distanceToSqr(carrier);
if (dist < best) {
best = dist;
bestCarrier = carrier;
}
}
if (bestCarrier == null) {
return false;
}
boolean mounted = mob.startRiding(bestCarrier, true);
if (mounted) {
AdaptiveAIGoalUtils.debug(mob, tierSupplier, "zombie stacking over obstacle");
}
return mounted;
}
private boolean tryCorpseLadderSurge(LivingEntity target) {
if (tierSupplier.getAsInt() < 5 || !mob.isPassenger()) {
return false;
}
double vertical = target.getY() - mob.getY();
double horizontal = Math.sqrt((target.getX() - mob.getX()) * (target.getX() - mob.getX())
+ (target.getZ() - mob.getZ()) * (target.getZ() - mob.getZ()));
if (vertical < 0.75D || vertical > 2.25D || horizontal > 2.0D) {
return false;
}
List<Zombie> nearby = mob.level().getEntitiesOfClass(Zombie.class, mob.getBoundingBox().inflate(2.5D),
zombie -> zombie.isAlive() && zombie.getTarget() == target);
if (nearby.size() < 3) {
return false;
}
mob.setDeltaMovement(mob.getDeltaMovement().add(0.0D, 0.45D, 0.0D));
mob.hurtMarked = true;
AdaptiveAIGoalUtils.debug(mob, tierSupplier, "zombie corpse ladder surge");
return true;
}
private boolean isVerticalObstacleCheese(LivingEntity target) {
return isPillaring(target) || isSimpleWallBlocked(target);
}
private boolean isPillaring(LivingEntity target) {
double vertical = target.getY() - mob.getY();
double horizontal = Math.sqrt((target.getX() - mob.getX()) * (target.getX() - mob.getX())
+ (target.getZ() - mob.getZ()) * (target.getZ() - mob.getZ()));
return vertical > 2.5D && horizontal < 5.5D;
}
private boolean isSimpleWallBlocked(LivingEntity target) {
double vertical = Math.abs(target.getY() - mob.getY());
double horizontal = Math.sqrt((target.getX() - mob.getX()) * (target.getX() - mob.getX())
+ (target.getZ() - mob.getZ()) * (target.getZ() - mob.getZ()));
if (vertical > 2.0D || horizontal > 5.5D || mob.hasLineOfSight(target)) {
return false;
}
Vec3 direction = target.position().subtract(mob.position());
int steps = Math.max(2, (int) Math.ceil(horizontal));
for (int i = 1; i < steps; i++) {
double scale = i / (double) steps;
BlockPos base = BlockPos.containing(mob.getX() + direction.x * scale, mob.getY(), mob.getZ() + direction.z * scale);
if (isSolidObstacle(base.above()) || isSolidObstacle(base.above(2))) {
return true;
}
}
return false;
}
private boolean isSolidObstacle(BlockPos pos) {
return !mob.level().getBlockState(pos).getCollisionShape(mob.level(), pos).isEmpty();
}
private void moveIfReasonable(Vec3 dest, double speed, String debug) {
if (AdaptivePositioningUtils.isReasonableGround(mob, dest)) {
mob.getNavigation().moveTo(dest.x, dest.y, dest.z, speed);
AdaptiveAIGoalUtils.debug(mob, tierSupplier, debug);
}
}
}