Skip to content
This repository has been archived by the owner on Jun 28, 2024. It is now read-only.

Death hole blocker #94

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/main/java/adris/altoclef/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ public class Settings {
*/
private boolean avoidDrowning = true;

/**
* If enabled, the bot will "fill in" the tops of holes it enters
* if there are hostile mobs nearby, to prevent the mobs from dropping
* down and killing the bot slowly.
*/
private boolean deathHoleFill = true;

/**
* If true, eat when we're hungry or in danger.
*/
Expand Down Expand Up @@ -448,6 +455,10 @@ public boolean isDodgeProjectiles() {
return dodgeProjectiles;
}

public boolean shouldDeathHoleFill() {
return deathHoleFill;
}

public boolean isAutoEat() {
return autoEat;
}
Expand Down
112 changes: 110 additions & 2 deletions src/main/java/adris/altoclef/tasksystem/chains/WorldSurvivalChain.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,29 @@

import adris.altoclef.AltoClef;
import adris.altoclef.tasks.EscapeFromLavaTask;
import adris.altoclef.tasks.InteractWithBlockTask;
import adris.altoclef.tasks.SafeRandomShimmyTask;
import adris.altoclef.tasks.misc.TimeoutWanderTask;
import adris.altoclef.tasksystem.TaskRunner;
import adris.altoclef.util.ItemTarget;
import adris.altoclef.util.WorldUtil;
import adris.altoclef.util.csharpisbetter.TimerGame;
import adris.altoclef.util.csharpisbetter.Util;
import baritone.api.utils.input.Input;
import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.item.Item;
import net.minecraft.item.Items;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3i;

import java.util.HashSet;
import java.util.Objects;
import java.util.stream.Collectors;

public class WorldSurvivalChain extends SingleTaskChain {

Expand All @@ -20,6 +33,11 @@ public class WorldSurvivalChain extends SingleTaskChain {
private boolean _wasStuckInPortal;
private int _portalStuckTimer;

private final HashSet<BlockPos> _dontQuickPlaceHere = new HashSet<>();
private final TimerGame _dontQuickPlaceResetTimer = new TimerGame(30);

private static final Item[] DEATH_HOLE_FILL_ITEMS = new Item[]{Items.DIRT, Items.COBBLESTONE, Items.NETHERRACK};

public WorldSurvivalChain(TaskRunner runner) {
super(runner);
}
Expand Down Expand Up @@ -55,13 +73,101 @@ public float getPriority(AltoClef mod) {
// We're stuck inside a portal, so get out.
// Don't allow breaking while we're inside the portal.
setTask(new SafeRandomShimmyTask());
return 60;
return 55;
}
_wasStuckInPortal = false;

if (mod.getModSettings().shouldDeathHoleFill()) {
// Extra patch:
// If we're breaking a block above us, don't quick place there!
if (_dontQuickPlaceResetTimer.elapsed()) {
_dontQuickPlaceResetTimer.reset();
_dontQuickPlaceHere.clear();
}
if (mod.getControllerExtras().isBreakingBlock()) {
BlockPos breaking = mod.getControllerExtras().getBreakingBlockPos();
if (breaking.getY() > mod.getPlayer().getBlockPos().getY() + 1) {
_dontQuickPlaceHere.add(breaking);
}
}

// "Death hole" prevention
if (shouldDeathHoleProtect(mod)) {
PlaceTarget prevent = getDeathHolePreventionSpot(mod);
if (prevent != null) {
setTask(new InteractWithBlockTask(new ItemTarget(DEATH_HOLE_FILL_ITEMS, 1), prevent.direction, prevent.place, false));
return 60;
}
}
}

return Float.NEGATIVE_INFINITY;
}

private boolean shouldDeathHoleProtect(AltoClef mod) {

// TODO: `hasStructureBlocks` command, taking into account protected items.
if (!mod.getInventoryTracker().hasItem(DEATH_HOLE_FILL_ITEMS)) {
return false;
}

// the "Death Hole" scenario is when we're digging down and mobs dive down to trap and kill us.
// Not fun.
Entity player = mod.getPlayer();
// Mobs above us and < 5 blocks away horizontally
Entity closest = Util.minItem(
mod.getEntityTracker().getHostiles().stream().filter(
entity -> entity.getPos().y > player.getPos().y + 2
&& Math.abs(entity.getPos().x - player.getPos().x) < 5
&& Math.abs(entity.getPos().z - player.getPos().z) < 5).collect(Collectors.toList()),
entity -> entity.squaredDistanceTo(player));
boolean mobsClose = closest != null;
if (mobsClose) {
// Check if we're in a hole
// Holes exist if
// - there's a bunch of air above us and mobs above us.
for (int dy = 0; dy < 10; ++dy) {
BlockPos check = player.getBlockPos().add(0, dy, 0);
if (check.getY() >= closest.getBlockPos().getY()) break;
if (WorldUtil.isSolid(mod, check)) {
return false;
}
}
return true;
}
return false;
}

private static class PlaceTarget {
public PlaceTarget(BlockPos place, Vec3i direction) {
this.place = place;
this.direction = Objects.requireNonNull(Direction.fromVector(direction.getX(), direction.getY(), direction.getZ())).getOpposite();
}

public BlockPos place;
public Direction direction;
}
private PlaceTarget getDeathHolePreventionSpot(AltoClef mod) {
final Vec3i[] offsToCheck = new Vec3i[]{
new Vec3i(-1, 0, 0),
new Vec3i(1, 0, 0),
new Vec3i(0, 0, -1),
new Vec3i(0, 0, 1)
};
for (BlockPos check = mod.getPlayer().getBlockPos().add(0, 2, 0); check.getY() < mod.getPlayer().getBlockPos().getY() + 7; check = check.up()) {
if (_dontQuickPlaceHere.contains(check)) continue;
for (Vec3i offs : offsToCheck) {
BlockPos placeAgainst = check.add(offs);
if (WorldUtil.isSolid(mod, placeAgainst)) {
return new PlaceTarget(
placeAgainst,
offs);
}
}
}
return null;
}

private void handleDrowning(AltoClef mod) {
// Swim
boolean avoidedDrowning = false;
Expand Down Expand Up @@ -96,6 +202,8 @@ private boolean isStuckInNetherPortal(AltoClef mod) {
// We're stuck if we're inside a portal, are breaking it and can ONLY look at the portal.
boolean inPortal = mod.getBlockTracker().blockIsValid(mod.getPlayer().getBlockPos(), Blocks.NETHER_PORTAL);
boolean breakingPortal = mod.getControllerExtras().isBreakingBlock() && mod.getBlockTracker().blockIsValid(mod.getControllerExtras().getBreakingBlockPos(), Blocks.NETHER_PORTAL);
// If we're looking at he nether portal
assert MinecraftClient.getInstance().crosshairTarget != null;
if (MinecraftClient.getInstance().crosshairTarget.getType() == HitResult.Type.BLOCK) {
BlockHitResult currentLook = (BlockHitResult) MinecraftClient.getInstance().crosshairTarget;
boolean collidingWithportal = (currentLook != null && mod.getBlockTracker().blockIsValid(currentLook.getBlockPos(), Blocks.NETHER_PORTAL));
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/adris/altoclef/trackers/EntityTracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ protected synchronized void updateState() {

if (entity instanceof HostileEntity || entity instanceof HoglinEntity || entity instanceof ZoglinEntity) {

if (isHostileToPlayer(_mod, entity)) {
if (isAngryAtPlayer(entity)) {

// Check if the mob is facing us or is close enough
boolean closeEnough = entity.isInRange(_mod.getPlayer(), 26);
Expand Down