/*
 * Decompiled with CFR 0.152.
 * 
 * Could not load the following classes:
 *  meteordevelopment.orbit.EventHandler
 *  net.minecraft.class_124
 *  net.minecraft.class_1263
 *  net.minecraft.class_1268
 *  net.minecraft.class_1297
 *  net.minecraft.class_1511
 *  net.minecraft.class_1542
 *  net.minecraft.class_1657
 *  net.minecraft.class_1707
 *  net.minecraft.class_1733
 *  net.minecraft.class_1744
 *  net.minecraft.class_1747
 *  net.minecraft.class_1753
 *  net.minecraft.class_1792
 *  net.minecraft.class_1799
 *  net.minecraft.class_1802
 *  net.minecraft.class_1887
 *  net.minecraft.class_1893
 *  net.minecraft.class_1922
 *  net.minecraft.class_2189
 *  net.minecraft.class_2246
 *  net.minecraft.class_2248
 *  net.minecraft.class_2336
 *  net.minecraft.class_2338
 *  net.minecraft.class_2338$class_2339
 *  net.minecraft.class_2346
 *  net.minecraft.class_2350
 *  net.minecraft.class_2374
 *  net.minecraft.class_238
 *  net.minecraft.class_2382
 *  net.minecraft.class_239$class_240
 *  net.minecraft.class_243
 *  net.minecraft.class_2480
 *  net.minecraft.class_2561
 *  net.minecraft.class_2596
 *  net.minecraft.class_2649
 *  net.minecraft.class_265
 *  net.minecraft.class_2680
 *  net.minecraft.class_2682
 *  net.minecraft.class_2846
 *  net.minecraft.class_2846$class_2847
 *  net.minecraft.class_3489
 *  net.minecraft.class_3959
 *  net.minecraft.class_3959$class_242
 *  net.minecraft.class_3959$class_3960
 *  net.minecraft.class_3965
 *  net.minecraft.class_437
 *  net.minecraft.class_476
 *  net.minecraft.class_495
 *  net.minecraft.class_5250
 *  net.minecraft.class_5321
 *  net.minecraft.class_744
 *  net.minecraft.class_9334
 *  org.jetbrains.annotations.NotNull
 *  org.jetbrains.annotations.Range
 *  org.joml.Vector3d
 */
package meteordevelopment.meteorclient.systems.modules.world;

import java.lang.runtime.SwitchBootstraps;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import meteordevelopment.meteorclient.events.game.GameLeftEvent;
import meteordevelopment.meteorclient.events.packets.PacketEvent;
import meteordevelopment.meteorclient.events.render.Render2DEvent;
import meteordevelopment.meteorclient.events.render.Render3DEvent;
import meteordevelopment.meteorclient.events.world.TickEvent;
import meteordevelopment.meteorclient.mixin.ShulkerBoxScreenHandlerAccessor;
import meteordevelopment.meteorclient.mixininterface.IVec3d;
import meteordevelopment.meteorclient.renderer.ShapeMode;
import meteordevelopment.meteorclient.renderer.text.TextRenderer;
import meteordevelopment.meteorclient.settings.BlockListSetting;
import meteordevelopment.meteorclient.settings.BoolSetting;
import meteordevelopment.meteorclient.settings.ColorSetting;
import meteordevelopment.meteorclient.settings.DoubleSetting;
import meteordevelopment.meteorclient.settings.EnumSetting;
import meteordevelopment.meteorclient.settings.IntSetting;
import meteordevelopment.meteorclient.settings.ItemListSetting;
import meteordevelopment.meteorclient.settings.Setting;
import meteordevelopment.meteorclient.settings.SettingGroup;
import meteordevelopment.meteorclient.systems.modules.Categories;
import meteordevelopment.meteorclient.systems.modules.Module;
import meteordevelopment.meteorclient.systems.modules.Modules;
import meteordevelopment.meteorclient.systems.modules.combat.KillAura;
import meteordevelopment.meteorclient.systems.modules.movement.Velocity;
import meteordevelopment.meteorclient.systems.modules.movement.speed.Speed;
import meteordevelopment.meteorclient.systems.modules.player.AutoEat;
import meteordevelopment.meteorclient.systems.modules.player.AutoGap;
import meteordevelopment.meteorclient.systems.modules.player.AutoTool;
import meteordevelopment.meteorclient.systems.modules.player.InstantRebreak;
import meteordevelopment.meteorclient.systems.modules.player.SpeedMine;
import meteordevelopment.meteorclient.systems.modules.world.NoGhostBlocks;
import meteordevelopment.meteorclient.utils.Utils;
import meteordevelopment.meteorclient.utils.entity.SortPriority;
import meteordevelopment.meteorclient.utils.entity.TargetUtils;
import meteordevelopment.meteorclient.utils.misc.HorizontalDirection;
import meteordevelopment.meteorclient.utils.misc.MBlockPos;
import meteordevelopment.meteorclient.utils.player.CustomPlayerInput;
import meteordevelopment.meteorclient.utils.player.EChestMemory;
import meteordevelopment.meteorclient.utils.player.InvUtils;
import meteordevelopment.meteorclient.utils.player.PlayerUtils;
import meteordevelopment.meteorclient.utils.player.Rotations;
import meteordevelopment.meteorclient.utils.render.NametagUtils;
import meteordevelopment.meteorclient.utils.render.RenderUtils;
import meteordevelopment.meteorclient.utils.render.color.Color;
import meteordevelopment.meteorclient.utils.render.color.SettingColor;
import meteordevelopment.meteorclient.utils.world.BlockUtils;
import meteordevelopment.meteorclient.utils.world.Dir;
import meteordevelopment.meteorclient.utils.world.TickRate;
import meteordevelopment.orbit.EventHandler;
import net.minecraft.class_124;
import net.minecraft.class_1263;
import net.minecraft.class_1268;
import net.minecraft.class_1297;
import net.minecraft.class_1511;
import net.minecraft.class_1542;
import net.minecraft.class_1657;
import net.minecraft.class_1707;
import net.minecraft.class_1733;
import net.minecraft.class_1744;
import net.minecraft.class_1747;
import net.minecraft.class_1753;
import net.minecraft.class_1792;
import net.minecraft.class_1799;
import net.minecraft.class_1802;
import net.minecraft.class_1887;
import net.minecraft.class_1893;
import net.minecraft.class_1922;
import net.minecraft.class_2189;
import net.minecraft.class_2246;
import net.minecraft.class_2248;
import net.minecraft.class_2336;
import net.minecraft.class_2338;
import net.minecraft.class_2346;
import net.minecraft.class_2350;
import net.minecraft.class_2374;
import net.minecraft.class_238;
import net.minecraft.class_2382;
import net.minecraft.class_239;
import net.minecraft.class_243;
import net.minecraft.class_2480;
import net.minecraft.class_2561;
import net.minecraft.class_2596;
import net.minecraft.class_2649;
import net.minecraft.class_265;
import net.minecraft.class_2680;
import net.minecraft.class_2682;
import net.minecraft.class_2846;
import net.minecraft.class_3489;
import net.minecraft.class_3959;
import net.minecraft.class_3965;
import net.minecraft.class_437;
import net.minecraft.class_476;
import net.minecraft.class_495;
import net.minecraft.class_5250;
import net.minecraft.class_5321;
import net.minecraft.class_744;
import net.minecraft.class_9334;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;
import org.joml.Vector3d;

public class HighwayBuilder
extends Module {
    private final SettingGroup sgGeneral;
    private final SettingGroup sgDigging;
    private final SettingGroup sgPaving;
    private final SettingGroup sgInventory;
    private final SettingGroup sgRenderDigging;
    private final SettingGroup sgRenderPaving;
    private final Setting<Integer> width;
    private final Setting<Integer> height;
    private final Setting<Floor> floor;
    private final Setting<Boolean> railings;
    private final Setting<Boolean> cornerBlock;
    private final Setting<Boolean> mineAboveRailings;
    private final Setting<Rotation> rotation;
    private final Setting<Boolean> disconnectOnToggle;
    private final Setting<Boolean> pauseOnLag;
    private final Setting<Boolean> destroyCrystalTraps;
    private final Setting<Boolean> doubleMine;
    private final Setting<Boolean> fastBreak;
    private final Setting<Boolean> dontBreakTools;
    private final Setting<Integer> breakDurability;
    private final Setting<Integer> savePickaxes;
    private final Setting<Integer> breakDelay;
    private final Setting<Integer> blocksPerTick;
    public final Setting<List<class_2248>> blocksToPlace;
    private final Setting<Double> placeRange;
    private final Setting<Integer> placeDelay;
    private final Setting<Integer> placementsPerTick;
    private final Setting<List<class_1792>> trashItems;
    private final Setting<Integer> inventoryDelay;
    private final Setting<Boolean> ejectUselessShulkers;
    private final Setting<Boolean> searchEnderChest;
    private final Setting<Boolean> searchShulkers;
    private final Setting<Integer> minEmpty;
    private final Setting<Boolean> mineEnderChests;
    private final Setting<BlockadeType> blockadeType;
    public final Setting<Integer> saveEchests;
    private final Setting<Boolean> rebreakEchests;
    private final Setting<Integer> rebreakTimer;
    private final Setting<Boolean> renderMine;
    private final Setting<ShapeMode> renderMineShape;
    private final Setting<SettingColor> renderMineSideColor;
    private final Setting<SettingColor> renderMineLineColor;
    private final Setting<Boolean> renderPlace;
    private final Setting<ShapeMode> renderPlaceShape;
    private final Setting<SettingColor> renderPlaceSideColor;
    private final Setting<SettingColor> renderPlaceLineColor;
    private HorizontalDirection dir;
    private HorizontalDirection leftDir;
    private HorizontalDirection rightDir;
    private class_744 prevInput;
    private CustomPlayerInput input;
    private State state;
    private State lastState;
    private IBlockPosProvider blockPosProvider;
    public class_243 start;
    public int blocksBroken;
    public int blocksPlaced;
    private final MBlockPos lastBreakingPos;
    private boolean displayInfo;
    private boolean warned;
    private boolean suspended;
    private boolean inventory;
    private int placeTimer;
    private int breakTimer;
    private int count;
    private int syncId;
    private final RestockTask restockTask;
    private final ArrayList<class_1511> ignoreCrystals;
    public boolean drawingBow;
    public DoubleMineBlock normalMining;
    public DoubleMineBlock packetMining;
    private final MBlockPos posRender2;
    private final MBlockPos posRender3;

    public HighwayBuilder() {
        super(Categories.World, "highway-builder", "Automatically builds highways.");
        this.sgGeneral = this.settings.getDefaultGroup();
        this.sgDigging = this.settings.createGroup("Digging");
        this.sgPaving = this.settings.createGroup("Paving");
        this.sgInventory = this.settings.createGroup("Inventory");
        this.sgRenderDigging = this.settings.createGroup("Render Digging");
        this.sgRenderPaving = this.settings.createGroup("Render Paving");
        this.width = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("width")).description("Width of the highway.")).defaultValue(4)).range(1, 5).sliderRange(1, 5).build());
        this.height = this.sgGeneral.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("height")).description("Height of the highway.")).defaultValue(3)).range(2, 5).sliderRange(2, 5).build());
        this.floor = this.sgGeneral.add(((EnumSetting.Builder)((EnumSetting.Builder)((EnumSetting.Builder)new EnumSetting.Builder().name("floor")).description("What floor placement mode to use.")).defaultValue(Floor.Replace)).build());
        this.railings = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("railings")).description("Builds railings next to the highway.")).defaultValue(true)).build());
        this.cornerBlock = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("corner-support-block")).description("Places a support block underneath the railings, to prevent air placing.")).defaultValue(true)).visible(this.railings::get)).build());
        this.mineAboveRailings = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("mine-above-railings")).description("Mines blocks above railings.")).defaultValue(true)).build());
        this.rotation = this.sgGeneral.add(((EnumSetting.Builder)((EnumSetting.Builder)((EnumSetting.Builder)new EnumSetting.Builder().name("rotation")).description("Mode of rotation.")).defaultValue(Rotation.Both)).build());
        this.disconnectOnToggle = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("disconnect-on-toggle")).description("Automatically disconnects when the module is turned off, for example for not having enough blocks.")).defaultValue(false)).build());
        this.pauseOnLag = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("pause-on-lag")).description("Pauses the current process while the server stops responding.")).defaultValue(true)).build());
        this.destroyCrystalTraps = this.sgGeneral.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("destroy-crystal-traps")).description("Use a bow to defuse crystal traps safely from a distance. An infinity bow is recommended.")).defaultValue(true)).build());
        this.doubleMine = this.sgDigging.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("double-mine")).description("Whether to double mine blocks when applicable (normal mine and packet mine simultaneously).")).defaultValue(true)).build());
        this.fastBreak = this.sgDigging.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("fast-break")).description("Whether to finish breaking blocks faster than normal while double mining.")).defaultValue(true)).visible(this.doubleMine::get)).build());
        this.dontBreakTools = this.sgDigging.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("dont-break-tools")).description("Don't break tools.")).defaultValue(false)).build());
        this.breakDurability = this.sgDigging.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("durability-percentage")).description("The durability percentage at which to stop using a tool.")).defaultValue(2)).range(1, 100).sliderRange(1, 100).visible(this.dontBreakTools::get)).build());
        this.savePickaxes = this.sgDigging.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("save-pickaxes")).description("How many pickaxes to ensure are saved. Hitting this number in your inventory will trigger a restock or the module toggling off.")).defaultValue(1)).range(0, 36).sliderRange(0, 36).visible(() -> this.dontBreakTools.get() == false)).build());
        this.breakDelay = this.sgDigging.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("break-delay")).description("The delay between breaking blocks.")).defaultValue(0)).min(0).build());
        this.blocksPerTick = this.sgDigging.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("blocks-per-tick")).description("The maximum amount of blocks that can be mined in a tick. Only applies to blocks instantly breakable.")).defaultValue(1)).range(1, 100).sliderRange(1, 25).build());
        this.blocksToPlace = this.sgPaving.add(((BlockListSetting.Builder)((BlockListSetting.Builder)new BlockListSetting.Builder().name("blocks-to-place")).description("Blocks it is allowed to place.")).defaultValue(class_2246.field_10540).filter(block -> class_2248.method_9614((class_265)block.method_9564().method_26220((class_1922)class_2682.field_12294, class_2338.field_10980))).build());
        this.placeRange = this.sgPaving.add(((DoubleSetting.Builder)((DoubleSetting.Builder)new DoubleSetting.Builder().name("place-range")).description("The maximum distance at which you can place blocks.")).defaultValue(4.5).sliderMax(5.5).build());
        this.placeDelay = this.sgPaving.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("place-delay")).description("The delay between placing blocks.")).defaultValue(0)).min(0).build());
        this.placementsPerTick = this.sgPaving.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("placements-per-tick")).description("The maximum amount of blocks that can be placed in a tick.")).defaultValue(1)).min(1).build());
        this.trashItems = this.sgInventory.add(((ItemListSetting.Builder)((ItemListSetting.Builder)new ItemListSetting.Builder().name("trash-items")).description("Items that are considered trash and can be thrown out.")).defaultValue(class_1802.field_8328, class_1802.field_8155, class_1802.field_8397, class_1802.field_8845, class_1802.field_8601, class_1802.field_8801, class_1802.field_23843, class_1802.field_22000, class_1802.field_8070, class_1802.field_8067, class_1802.field_21999, class_1802.field_8511, class_1802.field_8354).build());
        this.inventoryDelay = this.sgInventory.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("inventory-delay")).description("Delay in ticks on inventory interactions.")).defaultValue(3)).min(0).build());
        this.ejectUselessShulkers = this.sgInventory.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("eject-useless-shulkers")).description("Whether you should eject useless shulkers. Warning - will throw out any shulkers that don't contain blocks to place, pickaxes, or food. Be careful with your kits.")).defaultValue(true)).build());
        this.searchEnderChest = this.sgInventory.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("search-ender-chest")).description("Searches your ender chest to find items to use. Be careful with this one, especially if you let it search through shulkers.")).defaultValue(false)).build());
        this.searchShulkers = this.sgInventory.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("search-shulkers")).description("Searches through shulkers to find items to use.")).defaultValue(true)).build());
        this.minEmpty = this.sgInventory.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("minimum-empty-slots")).description("The minimum amount of empty slots you want left after mining obsidian.")).defaultValue(3)).sliderRange(0, 9).min(0).build());
        this.mineEnderChests = this.sgInventory.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("mine-ender-chests")).description("Mines ender chests for obsidian.")).defaultValue(true)).build());
        this.blockadeType = this.sgInventory.add(((EnumSetting.Builder)((EnumSetting.Builder)((EnumSetting.Builder)((EnumSetting.Builder)new EnumSetting.Builder().name("echest-blockade-type")).description("What blockade type to use (the structure placed when mining echests).")).defaultValue(BlockadeType.Full)).visible(this.mineEnderChests::get)).build());
        this.saveEchests = this.sgInventory.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("save-ender-chests")).description("How many ender chests to ensure are saved. Hitting this number in your inventory will trigger a restock or the module toggling off.")).defaultValue(2)).range(0, 64).sliderRange(0, 64).visible(this.mineEnderChests::get)).build());
        this.rebreakEchests = this.sgInventory.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("instantly-rebreak-echests")).description("Whether or not to use the instant rebreak exploit to break echests.")).defaultValue(false)).visible(this.mineEnderChests::get)).build());
        this.rebreakTimer = this.sgInventory.add(((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)((IntSetting.Builder)new IntSetting.Builder().name("rebreak-delay")).description("Delay between rebreak attempts.")).defaultValue(0)).sliderMax(20).visible(() -> this.mineEnderChests.get() != false && this.rebreakEchests.get() != false)).build());
        this.renderMine = this.sgRenderDigging.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("render-blocks-to-mine")).description("Render blocks to be mined.")).defaultValue(true)).build());
        this.renderMineShape = this.sgRenderDigging.add(((EnumSetting.Builder)((EnumSetting.Builder)((EnumSetting.Builder)new EnumSetting.Builder().name("blocks-to-mine-shape-mode")).description("How the blocks to be mined are rendered.")).defaultValue(ShapeMode.Both)).build());
        this.renderMineSideColor = this.sgRenderDigging.add(((ColorSetting.Builder)((ColorSetting.Builder)new ColorSetting.Builder().name("blocks-to-mine-side-color")).description("Color of blocks to be mined.")).defaultValue(new SettingColor(225, 25, 25, 25)).build());
        this.renderMineLineColor = this.sgRenderDigging.add(((ColorSetting.Builder)((ColorSetting.Builder)new ColorSetting.Builder().name("blocks-to-mine-line-color")).description("Color of blocks to be mined.")).defaultValue(new SettingColor(225, 25, 25)).build());
        this.renderPlace = this.sgRenderPaving.add(((BoolSetting.Builder)((BoolSetting.Builder)((BoolSetting.Builder)new BoolSetting.Builder().name("render-blocks-to-place")).description("Render blocks to be placed.")).defaultValue(true)).build());
        this.renderPlaceShape = this.sgRenderPaving.add(((EnumSetting.Builder)((EnumSetting.Builder)((EnumSetting.Builder)new EnumSetting.Builder().name("blocks-to-place-shape-mode")).description("How the blocks to be placed are rendered.")).defaultValue(ShapeMode.Both)).build());
        this.renderPlaceSideColor = this.sgRenderPaving.add(((ColorSetting.Builder)((ColorSetting.Builder)new ColorSetting.Builder().name("blocks-to-place-side-color")).description("Color of blocks to be placed.")).defaultValue(new SettingColor(25, 25, 225, 25)).build());
        this.renderPlaceLineColor = this.sgRenderPaving.add(((ColorSetting.Builder)((ColorSetting.Builder)new ColorSetting.Builder().name("blocks-to-place-line-color")).description("Color of blocks to be placed.")).defaultValue(new SettingColor(25, 25, 225)).build());
        this.lastBreakingPos = new MBlockPos();
        this.suspended = true;
        this.inventory = true;
        this.restockTask = new RestockTask(this);
        this.ignoreCrystals = new ArrayList();
        this.posRender2 = new MBlockPos();
        this.posRender3 = new MBlockPos();
        this.runInMainMenu = true;
    }

    @Override
    public void onActivate() {
        if (!Utils.canUpdate()) {
            return;
        }
        this.updateVariables();
        this.dir = HorizontalDirection.get(this.mc.field_1724.method_36454());
        this.leftDir = this.dir.rotateLeftSkipOne();
        this.rightDir = this.leftDir.opposite();
        this.blockPosProvider = this.dir.diagonal ? new DiagonalBlockPosProvider() : new StraightBlockPosProvider();
        this.state = State.Forward;
        this.setState(State.Center);
        this.lastBreakingPos.set(0, 0, 0);
        this.start = this.mc.field_1724.method_73189();
        this.blocksPlaced = 0;
        this.blocksBroken = 0;
        this.displayInfo = true;
        this.suspended = false;
        this.restockTask.complete();
        if (this.blocksPerTick.get() > 1 && this.rotation.get().mine) {
            this.warning("With rotations enabled, you can break at most 1 block per tick.", new Object[0]);
        }
        if (this.placementsPerTick.get() > 1 && this.rotation.get().place) {
            this.warning("With rotations enabled, you can place at most 1 block per tick.", new Object[0]);
        }
        if (Modules.get().get(InstantRebreak.class).isActive()) {
            this.warning("It's recommended to disable the Instant Rebreak module and instead use the 'instantly-rebreak-echests' setting to avoid errors.", new Object[0]);
        }
        if (Modules.get().get(Speed.class).isActive() && this.dir.diagonal) {
            this.warning("It's recommended to disable the Speed module to avoid misalignment on diagonals.", new Object[0]);
        }
        if (!Modules.get().get(Velocity.class).isActive()) {
            this.warning("It's recommended to enable the Velocity module to avoid misalignment (entity pushing, liquid movement).", new Object[0]);
        }
        if (!this.warned && Modules.get().get(NoGhostBlocks.class).isActive()) {
            this.info("The No Ghost Blocks module is useful to prevent desyncs on laggy servers. However, it will also slow Highway Builder down, and comes with the risks of incorrect statistics and packet kicks.", new Object[0]);
            this.warned = true;
        }
    }

    @Override
    public void onDeactivate() {
        if (!Utils.canUpdate()) {
            return;
        }
        this.mc.field_1724.field_3913 = this.prevInput;
        this.mc.field_1724.method_36456(this.dir.yaw);
        this.mc.field_1690.field_1904.method_23481(false);
        if (this.displayInfo) {
            this.info("Distance: (highlight)%.0f", PlayerUtils.distanceTo(this.start));
            this.info("Blocks broken: (highlight)%d", this.blocksBroken);
            this.info("Blocks placed: (highlight)%d", this.blocksPlaced);
        }
    }

    @Override
    public void error(String message, Object ... args) {
        super.error(message, args);
        this.toggle();
        if (this.disconnectOnToggle.get().booleanValue()) {
            this.disconnect(message, args);
        }
    }

    private void errorEarly(String message, Object ... args) {
        super.error(message, args);
        this.displayInfo = false;
        this.toggle();
    }

    @EventHandler
    private void onTick(TickEvent.Pre event) {
        if (this.dir == null) {
            this.onActivate();
            return;
        }
        if (this.suspended) {
            if (this.inventory && Utils.canUpdate()) {
                this.updateVariables();
                this.suspended = false;
            } else {
                return;
            }
        }
        if (this.width.get() < 3 && this.dir.diagonal) {
            this.errorEarly("Diagonal highways with width less than 3 are not supported.", new Object[0]);
            return;
        }
        if (Modules.get().get(AutoEat.class).eating || Modules.get().get(AutoGap.class).isEating() || Modules.get().get(KillAura.class).attacking) {
            this.input.stop();
            return;
        }
        if (this.pauseOnLag.get().booleanValue() && TickRate.INSTANCE.getTimeSinceLastTick() >= 1.5f) {
            this.input.stop();
            return;
        }
        this.count = 0;
        if (this.mc.field_1724.method_23318() < this.start.field_1351 - 0.5) {
            this.setState(State.ReLevel);
        }
        this.tickDoubleMine();
        this.state.tick(this);
        if (this.breakTimer > 0) {
            --this.breakTimer;
        }
        if (this.placeTimer > 0) {
            --this.placeTimer;
        }
    }

    @EventHandler
    private void onPacket(PacketEvent.Receive event) {
        class_2596<?> class_25962 = event.packet;
        if (class_25962 instanceof class_2649) {
            class_2649 p = (class_2649)class_25962;
            if (p.comp_3837() == 0 && this.suspended) {
                this.inventory = true;
            } else {
                this.syncId = p.comp_3837();
            }
        }
    }

    @EventHandler
    private void onGameLeave(GameLeftEvent event) {
        this.suspended = true;
        this.inventory = false;
    }

    @EventHandler
    private void onRender2d(Render2DEvent event) {
        if (this.suspended || !this.renderMine.get().booleanValue()) {
            return;
        }
        if (this.normalMining != null) {
            this.normalMining.renderLetter();
        }
        if (this.packetMining != null) {
            this.packetMining.renderLetter();
        }
    }

    @EventHandler
    private void onRender3D(Render3DEvent event) {
        if (this.suspended || this.blockPosProvider == null) {
            return;
        }
        if (this.renderMine.get().booleanValue()) {
            this.render(event, this.blockPosProvider.getFront(), mBlockPos -> this.canMine((MBlockPos)mBlockPos, true), true);
            if (this.floor.get() == Floor.Replace) {
                this.render(event, this.blockPosProvider.getFloor(), mBlockPos -> this.canMine((MBlockPos)mBlockPos, false), true);
            }
            if (this.railings.get().booleanValue()) {
                this.render(event, this.blockPosProvider.getRailings(0), mBlockPos -> this.canMine((MBlockPos)mBlockPos, false), true);
            }
            if (this.mineAboveRailings.get().booleanValue()) {
                this.render(event, this.blockPosProvider.getRailings(1), mBlockPos -> this.canMine((MBlockPos)mBlockPos, true), true);
            }
            if (this.state == State.MineEChestBlockade) {
                this.render(event, this.blockPosProvider.getBlockade(true, this.blockadeType.get()), mBlockPos -> this.canMine((MBlockPos)mBlockPos, true), true);
            }
        }
        if (this.renderPlace.get().booleanValue()) {
            this.render(event, this.blockPosProvider.getLiquids(), mBlockPos -> this.canPlace((MBlockPos)mBlockPos, true), false);
            if (this.railings.get().booleanValue()) {
                this.render(event, this.blockPosProvider.getRailings(0), mBlockPos -> this.canPlace((MBlockPos)mBlockPos, false), false);
                if (this.cornerBlock.get().booleanValue()) {
                    this.render(event, this.blockPosProvider.getRailings(-1), mBlockPos -> {
                        boolean valid = false;
                        for (MBlockPos pos : this.blockPosProvider.getRailings(0)) {
                            if (this.blocksToPlace.get().contains(pos.getState().method_26204()) || !pos.add(0, -1, 0).equals(mBlockPos)) continue;
                            valid = true;
                            break;
                        }
                        return valid && this.canPlace((MBlockPos)mBlockPos, false);
                    }, false);
                }
            }
            this.render(event, this.blockPosProvider.getFloor(), mBlockPos -> this.canPlace((MBlockPos)mBlockPos, false), false);
            if (this.state == State.PlaceEChestBlockade) {
                this.render(event, this.blockPosProvider.getBlockade(false, this.blockadeType.get()), mBlockPos -> this.canPlace((MBlockPos)mBlockPos, false), false);
            }
        }
    }

    private void render(Render3DEvent event, MBPIterator it, Predicate<MBlockPos> predicate, boolean mine) {
        Color sideColor = mine ? (Color)this.renderMineSideColor.get() : (Color)this.renderPlaceSideColor.get();
        Color lineColor = mine ? (Color)this.renderMineLineColor.get() : (Color)this.renderPlaceLineColor.get();
        ShapeMode shapeMode = mine ? this.renderMineShape.get() : this.renderPlaceShape.get();
        for (MBlockPos pos : it) {
            this.posRender2.set(pos);
            if (!predicate.test(this.posRender2)) continue;
            int excludeDir = 0;
            for (class_2350 side : class_2350.values()) {
                this.posRender3.set(this.posRender2).add(side.method_10148(), side.method_10164(), side.method_10165());
                it.save();
                for (MBlockPos p : it) {
                    if (!p.equals(this.posRender3) || !predicate.test(p)) continue;
                    excludeDir |= Dir.get(side);
                }
                it.restore();
            }
            event.renderer.box(this.posRender2.getBlockPos(), sideColor, lineColor, shapeMode, excludeDir);
        }
    }

    private void updateVariables() {
        this.prevInput = this.mc.field_1724.field_3913;
        this.input = new CustomPlayerInput();
        this.mc.field_1724.field_3913 = this.input;
        this.syncId = 0;
        this.count = 0;
        this.breakTimer = 0;
        this.placeTimer = 0;
        this.ignoreCrystals.clear();
        this.normalMining = null;
        this.packetMining = null;
    }

    private void setState(State state) {
        this.setState(state, this.state);
    }

    private void setState(State state, State lastState) {
        this.lastState = lastState;
        this.state = state;
        this.input.stop();
        state.start(this);
    }

    private int getWidthLeft() {
        return switch (this.width.get()) {
            case 4, 5 -> 2;
            case 2, 3 -> 1;
            default -> 0;
        };
    }

    private int getWidthRight() {
        return switch (this.width.get()) {
            case 5 -> 2;
            case 3, 4 -> 1;
            default -> 0;
        };
    }

    private boolean canMine(MBlockPos pos, boolean mineBlocksToPlace) {
        class_2680 state = pos.getState();
        return BlockUtils.canBreak(pos.getBlockPos(), state) && (mineBlocksToPlace || !this.blocksToPlace.get().contains(state.method_26204()));
    }

    private boolean canPlace(MBlockPos pos, boolean liquids) {
        if (pos.getBlockPos().method_19770((class_2374)this.mc.field_1724.method_33571()) > this.placeRange.get() * this.placeRange.get()) {
            return false;
        }
        return liquids ? !pos.getState().method_26227().method_15769() : BlockUtils.canPlace(pos.getBlockPos());
    }

    private void disconnect(String message, Object ... args) {
        class_5250 text = class_2561.method_43470((String)(String.format("%s[%s%s%s] %s", class_124.field_1080, class_124.field_1078, this.title, class_124.field_1080, class_124.field_1061) + String.format(message, args))).method_27693("\n");
        text.method_10852((class_2561)this.getStatsText());
        this.mc.method_1562().method_48296().method_10747((class_2561)text);
    }

    public class_5250 getStatsText() {
        class_5250 text = class_2561.method_43470((String)String.format("%sDistance: %s%.0f\n", class_124.field_1080, class_124.field_1068, this.mc.field_1724 == null ? 0.0 : PlayerUtils.distanceTo(this.start)));
        text.method_27693(String.format("%sBlocks broken: %s%d\n", class_124.field_1080, class_124.field_1068, this.blocksBroken));
        text.method_27693(String.format("%sBlocks placed: %s%d", class_124.field_1080, class_124.field_1068, this.blocksPlaced));
        return text;
    }

    private void tickDoubleMine() {
        if (this.normalMining != null) {
            if (this.normalMining.shouldRemove()) {
                this.mc.method_1562().method_52787((class_2596)new class_2846(class_2846.class_2847.field_12971, this.normalMining.blockPos, this.normalMining.direction));
                this.normalMining = null;
                DoubleMineBlock.rateLimited = true;
            } else if (this.mc.field_1687.method_8320(this.normalMining.blockPos).method_26204() != this.normalMining.block) {
                this.normalMining = null;
                ++this.blocksBroken;
                ++this.count;
                DoubleMineBlock.rateLimited = false;
            } else if (this.normalMining.isReady()) {
                this.normalMining.stopDestroying();
            }
            this.mc.field_1724.method_6104(class_1268.field_5808);
        }
        if (this.packetMining != null) {
            if (this.packetMining.shouldRemove()) {
                this.packetMining = null;
            } else if (this.mc.field_1687.method_8320(this.packetMining.blockPos).method_26204() != this.packetMining.block) {
                this.packetMining = null;
                ++this.blocksBroken;
                ++this.count;
            }
        }
    }

    public static enum Floor {
        Replace,
        PlaceMissing;

    }

    public static enum Rotation {
        None(false, false),
        Mine(true, false),
        Place(false, true),
        Both(true, true);

        public final boolean mine;
        public final boolean place;

        private Rotation(boolean mine, boolean place) {
            this.mine = mine;
            this.place = place;
        }
    }

    public static enum BlockadeType {
        Full(6),
        Partial(4),
        Shulker(3);

        public final int columns;

        private BlockadeType(int columns) {
            this.columns = columns;
        }
    }

    private class RestockTask {
        public boolean materials;
        public boolean pickaxes;
        public boolean food;
        private final HighwayBuilder b;

        public RestockTask(HighwayBuilder b) {
            this.b = b;
        }

        public void setMaterials() {
            this.setTask(0);
        }

        public void setPickaxes() {
            this.setTask(1);
        }

        public void setFood() {
            this.setTask(2);
        }

        private void setTask(@Range(from=0L, to=2L) int value) {
            this.complete();
            switch (value) {
                case 0: {
                    this.materials = true;
                    break;
                }
                case 1: {
                    this.pickaxes = true;
                    break;
                }
                case 2: {
                    this.food = true;
                }
            }
            HighwayBuilder.this.setState(State.Restock);
            this.b.info("Starting new restock task for " + this.item(), new Object[0]);
        }

        public void complete() {
            this.materials = false;
            this.pickaxes = false;
            this.food = false;
        }

        public boolean tasksInactive() {
            return !this.materials && !this.pickaxes && !this.food;
        }

        public String item() {
            if (this.materials) {
                return "building materials";
            }
            if (this.pickaxes) {
                return "pickaxes";
            }
            if (this.food) {
                return "food";
            }
            return "unknown";
        }
    }

    private class DiagonalBlockPosProvider
    implements IBlockPosProvider {
        private final MBlockPos pos = new MBlockPos();
        private final MBlockPos pos2 = new MBlockPos();

        private DiagonalBlockPosProvider() {
        }

        @Override
        public MBPIterator getFront() {
            this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir.rotateLeft()).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft() - 1);
            return new MBPIterator(){
                private int i;
                private int w;
                private int y;
                private int pi;
                private int pw;
                private int py;

                @Override
                public boolean hasNext() {
                    return this.i < 2 && this.w < HighwayBuilder.this.width.get() && this.y < HighwayBuilder.this.height.get();
                }

                @Override
                public MBlockPos next() {
                    DiagonalBlockPosProvider.this.pos2.set(DiagonalBlockPosProvider.this.pos).offset(HighwayBuilder.this.rightDir, this.w).add(0, this.y++, 0);
                    if (this.y >= HighwayBuilder.this.height.get()) {
                        this.y = 0;
                        ++this.w;
                        if (this.w >= (this.i == 0 ? HighwayBuilder.this.width.get() - 1 : HighwayBuilder.this.width.get())) {
                            this.w = 0;
                            ++this.i;
                            DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft());
                        }
                    }
                    return DiagonalBlockPosProvider.this.pos2;
                }

                private void initPos() {
                    if (this.i == 0) {
                        DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir.rotateLeft()).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft() - 1);
                    } else {
                        DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft());
                    }
                }

                @Override
                public void save() {
                    this.pi = this.i;
                    this.pw = this.w;
                    this.py = this.y;
                    this.y = 0;
                    this.w = 0;
                    this.i = 0;
                    this.initPos();
                }

                @Override
                public void restore() {
                    this.i = this.pi;
                    this.w = this.pw;
                    this.y = this.py;
                    this.initPos();
                }
            };
        }

        @Override
        public MBPIterator getFloor() {
            this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).add(0, -1, 0).offset(HighwayBuilder.this.dir.rotateLeft()).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft() - 1);
            return new MBPIterator(){
                private int i;
                private int w;
                private int pi;
                private int pw;

                @Override
                public boolean hasNext() {
                    return this.i < 2 && this.w < HighwayBuilder.this.width.get();
                }

                @Override
                public MBlockPos next() {
                    DiagonalBlockPosProvider.this.pos2.set(DiagonalBlockPosProvider.this.pos).offset(HighwayBuilder.this.rightDir, this.w++);
                    if (this.w >= (this.i == 0 ? HighwayBuilder.this.width.get() - 1 : HighwayBuilder.this.width.get())) {
                        this.w = 0;
                        ++this.i;
                        DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).add(0, -1, 0).offset(HighwayBuilder.this.dir).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft());
                    }
                    return DiagonalBlockPosProvider.this.pos2;
                }

                private void initPos() {
                    if (this.i == 0) {
                        DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).add(0, -1, 0).offset(HighwayBuilder.this.dir.rotateLeft()).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft() - 1);
                    } else {
                        DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).add(0, -1, 0).offset(HighwayBuilder.this.dir).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft());
                    }
                }

                @Override
                public void save() {
                    this.pi = this.i;
                    this.pw = this.w;
                    this.w = 0;
                    this.i = 0;
                    this.initPos();
                }

                @Override
                public void restore() {
                    this.i = this.pi;
                    this.w = this.pw;
                    this.initPos();
                }
            };
        }

        @Override
        public MBPIterator getRailings(final int state) {
            this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir.rotateLeft()).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft());
            return new MBPIterator(){
                private int i;
                private int y;
                private int pi;
                private int py;
                {
                    this.y = state;
                }

                @Override
                public boolean hasNext() {
                    return this.i < 2 && this.y < (state == 1 ? HighwayBuilder.this.height.get() : state + 1);
                }

                @Override
                public MBlockPos next() {
                    DiagonalBlockPosProvider.this.pos2.set(DiagonalBlockPosProvider.this.pos).add(0, this.y++, 0);
                    if (this.y >= (state == 1 ? HighwayBuilder.this.height.get() : state + 1)) {
                        this.y = state;
                        ++this.i;
                        DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir.rotateRight()).offset(HighwayBuilder.this.rightDir, HighwayBuilder.this.getWidthRight());
                    }
                    return DiagonalBlockPosProvider.this.pos2;
                }

                private void initPos() {
                    if (this.i == 0) {
                        DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir.rotateLeft()).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft());
                    } else {
                        DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir.rotateRight()).offset(HighwayBuilder.this.rightDir, HighwayBuilder.this.getWidthRight());
                    }
                }

                @Override
                public void save() {
                    this.pi = this.i;
                    this.py = this.y;
                    this.i = 0;
                    this.y = state;
                    this.initPos();
                }

                @Override
                public void restore() {
                    this.i = this.pi;
                    this.y = this.py;
                    this.initPos();
                }
            };
        }

        @Override
        public MBPIterator getLiquids() {
            final boolean m = HighwayBuilder.this.mineAboveRailings.get();
            this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir).offset(HighwayBuilder.this.dir.rotateLeft()).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft());
            return new MBPIterator(){
                private int i;
                private int w;
                private int y;
                private int pi;
                private int pw;
                private int py;

                private int getWidth() {
                    return HighwayBuilder.this.width.get() + (this.i == 0 ? 1 : 0) + (m && this.i == 1 ? 2 : 0);
                }

                @Override
                public boolean hasNext() {
                    if (m && this.i == 1 && this.y == HighwayBuilder.this.height.get() && this.w == this.getWidth() - 1) {
                        return false;
                    }
                    return this.i < 2 && this.w < this.getWidth() && this.y < HighwayBuilder.this.height.get() + 1;
                }

                private void updateW() {
                    ++this.w;
                    if (this.w >= this.getWidth()) {
                        this.w = 0;
                        ++this.i;
                        DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir, 2).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft() + (m ? 1 : 0));
                    }
                }

                @Override
                public MBlockPos next() {
                    if (this.i == (m ? 1 : 0) && this.y == HighwayBuilder.this.height.get() && (this.w == 0 || this.w == this.getWidth() - 1)) {
                        this.y = 0;
                        this.updateW();
                    }
                    DiagonalBlockPosProvider.this.pos2.set(DiagonalBlockPosProvider.this.pos).offset(HighwayBuilder.this.rightDir, this.w).add(0, this.y++, 0);
                    if (this.y >= HighwayBuilder.this.height.get() + 1) {
                        this.y = 0;
                        this.updateW();
                    }
                    return DiagonalBlockPosProvider.this.pos2;
                }

                private void initPos() {
                    if (this.i == 0) {
                        DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir).offset(HighwayBuilder.this.dir.rotateLeft()).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft());
                    } else {
                        DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir, 2).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft() + (m ? 1 : 0));
                    }
                }

                @Override
                public void save() {
                    this.pi = this.i;
                    this.pw = this.w;
                    this.py = this.y;
                    this.y = 0;
                    this.w = 0;
                    this.i = 0;
                    this.initPos();
                }

                @Override
                public void restore() {
                    this.i = this.pi;
                    this.w = this.pw;
                    this.y = this.py;
                    this.initPos();
                }
            };
        }

        @Override
        public MBPIterator getBlockade(final boolean mine, final BlockadeType blockadeType) {
            return new MBPIterator(){
                private int i;
                private int y;
                private int pi;
                private int py;
                {
                    this.i = mine ? -1 : 0;
                }

                private MBlockPos get(int i) {
                    HorizontalDirection dir2 = HighwayBuilder.this.dir.rotateLeft().rotateLeftSkipOne();
                    DiagonalBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(dir2);
                    return switch (i) {
                        case -1 -> DiagonalBlockPosProvider.this.pos;
                        case 0 -> DiagonalBlockPosProvider.this.pos.offset(dir2);
                        case 1 -> DiagonalBlockPosProvider.this.pos.offset(dir2.rotateLeftSkipOne());
                        case 2 -> DiagonalBlockPosProvider.this.pos.offset(dir2.rotateLeftSkipOne().opposite());
                        case 3 -> DiagonalBlockPosProvider.this.pos.offset(dir2.opposite(), 2);
                        case 4 -> DiagonalBlockPosProvider.this.pos.offset(dir2.opposite()).offset(dir2.rotateLeftSkipOne());
                        case 5 -> DiagonalBlockPosProvider.this.pos.offset(dir2.opposite()).offset(dir2.rotateLeftSkipOne().opposite());
                        default -> throw new IllegalStateException("Unexpected value: " + i);
                    };
                }

                @Override
                public boolean hasNext() {
                    return this.i < blockadeType.columns && this.y < 2;
                }

                @Override
                public MBlockPos next() {
                    MBlockPos pos = this.get(this.i).add(0, this.y, 0);
                    ++this.y;
                    if (this.y > 1) {
                        this.y = 0;
                        ++this.i;
                    }
                    return pos;
                }

                @Override
                public void save() {
                    this.pi = this.i;
                    this.py = this.y;
                    this.y = 0;
                    this.i = 0;
                }

                @Override
                public void restore() {
                    this.i = this.pi;
                    this.y = this.py;
                }

                @Override
                public int placementsPerTick(HighwayBuilder b) {
                    return 1;
                }
            };
        }
    }

    private class StraightBlockPosProvider
    implements IBlockPosProvider {
        private final MBlockPos pos = new MBlockPos();
        private final MBlockPos pos2 = new MBlockPos();

        private StraightBlockPosProvider() {
        }

        @Override
        public MBPIterator getFront() {
            this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft());
            return new MBPIterator(){
                private int w;
                private int y;
                private int pw;
                private int py;

                @Override
                public boolean hasNext() {
                    return this.w < HighwayBuilder.this.width.get() && this.y < HighwayBuilder.this.height.get();
                }

                @Override
                public MBlockPos next() {
                    StraightBlockPosProvider.this.pos2.set(StraightBlockPosProvider.this.pos).offset(HighwayBuilder.this.rightDir, this.w).add(0, this.y, 0);
                    ++this.w;
                    if (this.w >= HighwayBuilder.this.width.get()) {
                        this.w = 0;
                        ++this.y;
                    }
                    return StraightBlockPosProvider.this.pos2;
                }

                @Override
                public void save() {
                    this.pw = this.w;
                    this.py = this.y;
                    this.y = 0;
                    this.w = 0;
                }

                @Override
                public void restore() {
                    this.w = this.pw;
                    this.y = this.py;
                }
            };
        }

        @Override
        public MBPIterator getFloor() {
            this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft()).add(0, -1, 0);
            return new MBPIterator(){
                private int w;
                private int pw;

                @Override
                public boolean hasNext() {
                    return this.w < HighwayBuilder.this.width.get();
                }

                @Override
                public MBlockPos next() {
                    return StraightBlockPosProvider.this.pos2.set(StraightBlockPosProvider.this.pos).offset(HighwayBuilder.this.rightDir, this.w++);
                }

                @Override
                public void save() {
                    this.pw = this.w;
                    this.w = 0;
                }

                @Override
                public void restore() {
                    this.w = this.pw;
                }
            };
        }

        @Override
        public MBPIterator getRailings(final int state) {
            this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir);
            return new MBPIterator(){
                private int i;
                private int y;
                private int pi;
                private int py;
                {
                    this.y = state;
                }

                @Override
                public boolean hasNext() {
                    return this.i < 2 && this.y < (state == 1 ? HighwayBuilder.this.height.get() : state + 1);
                }

                @Override
                public MBlockPos next() {
                    if (this.i == 0) {
                        StraightBlockPosProvider.this.pos2.set(StraightBlockPosProvider.this.pos).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft() + 1).add(0, this.y, 0);
                    } else {
                        StraightBlockPosProvider.this.pos2.set(StraightBlockPosProvider.this.pos).offset(HighwayBuilder.this.rightDir, HighwayBuilder.this.getWidthRight() + 1).add(0, this.y, 0);
                    }
                    ++this.y;
                    if (this.y >= (state == 1 ? HighwayBuilder.this.height.get() : state + 1)) {
                        this.y = state;
                        ++this.i;
                    }
                    return StraightBlockPosProvider.this.pos2;
                }

                @Override
                public void save() {
                    this.pi = this.i;
                    this.py = this.y;
                    this.i = 0;
                    this.y = state;
                }

                @Override
                public void restore() {
                    this.i = this.pi;
                    this.y = this.py;
                }
            };
        }

        @Override
        public MBPIterator getLiquids() {
            this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir, 2).offset(HighwayBuilder.this.leftDir, HighwayBuilder.this.getWidthLeft() + (HighwayBuilder.this.mineAboveRailings.get() != false ? 2 : 1));
            return new MBPIterator(){
                private int w;
                private int y;
                private int pw;
                private int py;

                private int getWidth() {
                    return HighwayBuilder.this.width.get() + (HighwayBuilder.this.mineAboveRailings.get() != false ? 2 : 0);
                }

                @Override
                public boolean hasNext() {
                    return this.w < this.getWidth() + 2 && this.y < HighwayBuilder.this.height.get() + 1;
                }

                @Override
                public MBlockPos next() {
                    StraightBlockPosProvider.this.pos2.set(StraightBlockPosProvider.this.pos).offset(HighwayBuilder.this.rightDir, this.w).add(0, this.y, 0);
                    ++this.w;
                    if (this.w >= this.getWidth() + 2) {
                        this.w = 0;
                        ++this.y;
                    }
                    return StraightBlockPosProvider.this.pos2;
                }

                @Override
                public void save() {
                    this.pw = this.w;
                    this.py = this.y;
                    this.y = 0;
                    this.w = 0;
                }

                @Override
                public void restore() {
                    this.w = this.pw;
                    this.y = this.py;
                }
            };
        }

        @Override
        public MBPIterator getBlockade(final boolean mine, final BlockadeType blockadeType) {
            return new MBPIterator(){
                private int i;
                private int y;
                private int pi;
                private int py;
                {
                    this.i = mine ? -1 : 0;
                }

                private MBlockPos get(int i) {
                    StraightBlockPosProvider.this.pos.coerceBlockLevel((class_1297)((HighwayBuilder)HighwayBuilder.this).mc.field_1724).offset(HighwayBuilder.this.dir.opposite());
                    return switch (i) {
                        case -1 -> StraightBlockPosProvider.this.pos;
                        case 0 -> StraightBlockPosProvider.this.pos.offset(HighwayBuilder.this.dir.opposite());
                        case 1 -> StraightBlockPosProvider.this.pos.offset(HighwayBuilder.this.leftDir);
                        case 2 -> StraightBlockPosProvider.this.pos.offset(HighwayBuilder.this.rightDir);
                        case 3 -> StraightBlockPosProvider.this.pos.offset(HighwayBuilder.this.dir, 2);
                        case 4 -> StraightBlockPosProvider.this.pos.offset(HighwayBuilder.this.dir).offset(HighwayBuilder.this.leftDir);
                        case 5 -> StraightBlockPosProvider.this.pos.offset(HighwayBuilder.this.dir).offset(HighwayBuilder.this.rightDir);
                        default -> throw new IllegalStateException("Unexpected value: " + i);
                    };
                }

                @Override
                public boolean hasNext() {
                    return this.i < blockadeType.columns && this.y < 2;
                }

                @Override
                public MBlockPos next() {
                    if (HighwayBuilder.this.width.get() == 1 && HighwayBuilder.this.railings.get().booleanValue() && this.i > 0 && this.y == 0) {
                        ++this.y;
                    }
                    MBlockPos pos = this.get(this.i).add(0, this.y, 0);
                    ++this.y;
                    if (this.y > 1) {
                        this.y = 0;
                        ++this.i;
                    }
                    return pos;
                }

                @Override
                public void save() {
                    this.pi = this.i;
                    this.py = this.y;
                    this.y = 0;
                    this.i = 0;
                }

                @Override
                public void restore() {
                    this.i = this.pi;
                    this.y = this.py;
                }

                @Override
                public int placementsPerTick(HighwayBuilder b) {
                    return 1;
                }
            };
        }
    }

    private static interface IBlockPosProvider {
        public MBPIterator getFront();

        public MBPIterator getFloor();

        public MBPIterator getRailings(int var1);

        public MBPIterator getLiquids();

        public MBPIterator getBlockade(boolean var1, BlockadeType var2);
    }

    private static enum State {
        Center{

            @Override
            protected void start(HighwayBuilder b) {
                if (((HighwayBuilder)b).mc.field_1724.method_73189().method_24802((class_2374)class_243.method_24955((class_2382)((HighwayBuilder)b).mc.field_1724.method_24515()), 0.1)) {
                    this.stop(b);
                }
            }

            @Override
            protected void tick(HighwayBuilder b) {
                boolean isZ;
                double x = Math.abs(((HighwayBuilder)b).mc.field_1724.method_23317() - (double)((int)((HighwayBuilder)b).mc.field_1724.method_23317())) - 0.5;
                double z = Math.abs(((HighwayBuilder)b).mc.field_1724.method_23321() - (double)((int)((HighwayBuilder)b).mc.field_1724.method_23321())) - 0.5;
                boolean isX = Math.abs(x) <= 0.1;
                boolean bl = isZ = Math.abs(z) <= 0.1;
                if (isX && isZ) {
                    this.stop(b);
                } else {
                    ((HighwayBuilder)b).mc.field_1724.method_36456(0.0f);
                    if (!isZ) {
                        b.input.forward(z < 0.0);
                        b.input.backward(z > 0.0);
                        if (((HighwayBuilder)b).mc.field_1724.method_23321() < 0.0) {
                            boolean forward = b.input.field_54155.comp_3159();
                            b.input.forward(b.input.field_54155.comp_3160());
                            b.input.backward(forward);
                        }
                    }
                    if (!isX) {
                        b.input.right(x > 0.0);
                        b.input.left(x < 0.0);
                        if (((HighwayBuilder)b).mc.field_1724.method_23317() < 0.0) {
                            boolean right = b.input.field_54155.comp_3162();
                            b.input.right(b.input.field_54155.comp_3161());
                            b.input.left(right);
                        }
                    }
                    b.input.sneak(true);
                }
            }

            private void stop(HighwayBuilder b) {
                b.input.stop();
                ((HighwayBuilder)b).mc.field_1724.method_18800(0.0, 0.0, 0.0);
                ((HighwayBuilder)b).mc.field_1724.method_5814((double)((int)((HighwayBuilder)b).mc.field_1724.method_23317()) + (((HighwayBuilder)b).mc.field_1724.method_23317() < 0.0 ? -0.5 : 0.5), ((HighwayBuilder)b).mc.field_1724.method_23318(), (double)((int)((HighwayBuilder)b).mc.field_1724.method_23321()) + (((HighwayBuilder)b).mc.field_1724.method_23321() < 0.0 ? -0.5 : 0.5));
                b.setState(b.lastState);
            }
        }
        ,
        Forward{

            @Override
            protected void start(HighwayBuilder b) {
                this.checkTasks(b);
                if (b.state == Forward) {
                    ((HighwayBuilder)b).mc.field_1724.method_36456(b.dir.yaw);
                }
            }

            @Override
            protected void tick(HighwayBuilder b) {
                this.checkTasks(b);
                if (b.state == Forward) {
                    b.input.forward(true);
                }
            }

            private void checkTasks(HighwayBuilder b) {
                if (b.destroyCrystalTraps.get().booleanValue() && this.isCrystalTrap(b)) {
                    b.setState(DefuseCrystalTraps);
                } else if (this.needsToPlace(b, b.blockPosProvider.getLiquids(), true)) {
                    b.setState(FillLiquids);
                } else if (this.needsToMine(b, b.blockPosProvider.getFront(), true)) {
                    b.setState(MineFront);
                } else if (b.floor.get() == Floor.Replace && this.needsToMine(b, b.blockPosProvider.getFloor(), false)) {
                    b.setState(MineFloor);
                } else if (b.railings.get().booleanValue() && this.needsToMine(b, b.blockPosProvider.getRailings(0), false)) {
                    b.setState(MineRailings);
                } else if (b.mineAboveRailings.get().booleanValue() && this.needsToMine(b, b.blockPosProvider.getRailings(1), true)) {
                    b.setState(MineAboveRailings);
                } else if (b.railings.get().booleanValue() && this.needsToPlace(b, b.blockPosProvider.getRailings(0), false)) {
                    if (b.cornerBlock.get().booleanValue() && this.needsToPlace(b, b.blockPosProvider.getRailings(-1), false)) {
                        b.setState(PlaceCornerBlock);
                    } else {
                        b.setState(PlaceRailings);
                    }
                } else if (this.needsToPlace(b, b.blockPosProvider.getFloor(), false)) {
                    b.setState(PlaceFloor);
                }
            }

            private boolean needsToMine(HighwayBuilder b, MBPIterator it, boolean mineBlocksToPlace) {
                for (MBlockPos pos : it) {
                    if (!b.canMine(pos, mineBlocksToPlace)) continue;
                    return true;
                }
                return false;
            }

            private boolean needsToPlace(HighwayBuilder b, MBPIterator it, boolean liquids) {
                for (MBlockPos pos : it) {
                    if (!b.canPlace(pos, liquids)) continue;
                    return true;
                }
                return false;
            }

            private boolean isCrystalTrap(HighwayBuilder b) {
                for (class_1297 entity : ((HighwayBuilder)b).mc.field_1687.method_18112()) {
                    class_1511 endCrystal;
                    if (!(entity instanceof class_1511) || PlayerUtils.isWithin((class_1297)(endCrystal = (class_1511)entity), 12.0) || !PlayerUtils.isWithin((class_1297)endCrystal, 24.0) || b.ignoreCrystals.contains(endCrystal)) continue;
                    class_243 vec1 = new class_243(0.0, 0.0, 0.0);
                    class_243 vec2 = new class_243(0.0, 0.0, 0.0);
                    ((IVec3d)vec1).meteor$set(((HighwayBuilder)b).mc.field_1724.method_23317(), ((HighwayBuilder)b).mc.field_1724.method_23318() + (double)((HighwayBuilder)b).mc.field_1724.method_5751(), ((HighwayBuilder)b).mc.field_1724.method_23321());
                    ((IVec3d)vec2).meteor$set(entity.method_23317(), entity.method_23318() + 0.5, entity.method_23321());
                    return ((HighwayBuilder)b).mc.field_1687.method_17742(new class_3959(vec1, vec2, class_3959.class_3960.field_17558, class_3959.class_242.field_1348, (class_1297)((HighwayBuilder)b).mc.field_1724)).method_17783() == class_239.class_240.field_1333;
                }
                return false;
            }
        }
        ,
        ReLevel{
            private final class_2338.class_2339 pos = new class_2338.class_2339();
            private class_2338 startPos;
            private int timer = 30;

            @Override
            protected void start(HighwayBuilder b) {
                this.startPos = class_2338.method_49638((class_2374)b.start);
            }

            @Override
            protected void tick(HighwayBuilder b) {
                class_243 vec = ((HighwayBuilder)b).mc.field_1724.method_73189().method_1019(((HighwayBuilder)b).mc.field_1724.method_18798()).method_1031(0.0, -0.75, 0.0);
                this.pos.method_10102((double)((HighwayBuilder)b).mc.field_1724.method_31477(), vec.field_1351, (double)((HighwayBuilder)b).mc.field_1724.method_31479());
                if (this.pos.method_10264() >= ((HighwayBuilder)b).mc.field_1724.method_24515().method_10264()) {
                    this.pos.method_33098(((HighwayBuilder)b).mc.field_1724.method_24515().method_10264() - 1);
                }
                if (this.pos.method_10264() >= this.startPos.method_10264()) {
                    this.pos.method_33098(this.startPos.method_10264() - 1);
                }
                if (((HighwayBuilder)b).mc.field_1724.method_23318() > b.start.field_1351 - 0.5 && !((HighwayBuilder)b).mc.field_1687.method_8320((class_2338)this.pos).method_45474()) {
                    b.input.jump(false);
                    if (this.timer > 0) {
                        --this.timer;
                    } else {
                        b.setState(Forward);
                        this.timer = 30;
                    }
                    return;
                }
                if (b.placeTimer > 0) {
                    return;
                }
                if (this.timer < 30) {
                    this.timer = 30;
                }
                b.input.jump(true);
                int slot = -1;
                if (this.pos.method_10264() == this.startPos.method_10074().method_10264()) {
                    slot = this.findAndMoveToHotbar(b, itemStack -> {
                        class_1792 patt0$temp = itemStack.method_7909();
                        if (!(patt0$temp instanceof class_1747)) return false;
                        class_1747 blockItem = (class_1747)patt0$temp;
                        if (!b.blocksToPlace.get().contains(blockItem.method_7711())) return false;
                        return true;
                    });
                }
                if (slot == -1 && (slot = this.findAcceptablePlacementBlock(b)) == -1) {
                    return;
                }
                if (BlockUtils.place(this.pos.method_10062(), class_1268.field_5808, slot, b.rotation.get().place, 100, true, true, false)) {
                    if (b.renderPlace.get().booleanValue()) {
                        RenderUtils.renderTickingBlock(this.pos.method_10062(), b.renderPlaceSideColor.get(), b.renderPlaceLineColor.get(), b.renderPlaceShape.get(), 0, 5, true, false);
                    }
                    b.placeTimer = b.placeDelay.get();
                }
            }

            private int findAcceptablePlacementBlock(HighwayBuilder b) {
                int slot = this.findAndMoveToHotbar(b, itemStack -> {
                    if (!(itemStack.method_7909() instanceof class_1747)) {
                        return false;
                    }
                    return b.trashItems.get().contains(itemStack.method_7909());
                });
                if (slot == -1) {
                    slot = this.findAndMoveToHotbar(b, itemStack -> {
                        class_1792 patt0$temp = itemStack.method_7909();
                        if (!(patt0$temp instanceof class_1747)) {
                            return false;
                        }
                        class_1747 bi = (class_1747)patt0$temp;
                        return b.blocksToPlace.get().contains(bi.method_7711());
                    });
                }
                return slot != -1 ? slot : this.findAndMoveToHotbar(b, itemStack -> {
                    class_1792 patt0$temp = itemStack.method_7909();
                    if (!(patt0$temp instanceof class_1747)) {
                        return false;
                    }
                    class_1747 bi = (class_1747)patt0$temp;
                    if (Utils.isShulker((class_1792)bi)) {
                        return false;
                    }
                    class_2248 block = bi.method_7711();
                    if (!class_2248.method_9614((class_265)block.method_9564().method_26220((class_1922)((HighwayBuilder)b).mc.field_1687, (class_2338)this.pos))) {
                        return false;
                    }
                    return !(block instanceof class_2346) || !class_2346.method_10128((class_2680)((HighwayBuilder)b).mc.field_1687.method_8320((class_2338)this.pos));
                });
            }
        }
        ,
        FillLiquids{

            @Override
            protected void tick(HighwayBuilder b) {
                int slot = this.findBlocksToPlacePrioritizeTrash(b);
                if (slot == -1) {
                    return;
                }
                this.place(b, new MBPIteratorFilter(b.blockPosProvider.getLiquids(), pos -> !pos.getState().method_26227().method_15769()), slot, Forward);
            }
        }
        ,
        MineFront{

            @Override
            protected void start(HighwayBuilder b) {
                this.mine(b, b.blockPosProvider.getFront(), true, Forward, this);
            }

            @Override
            protected void tick(HighwayBuilder b) {
                this.mine(b, b.blockPosProvider.getFront(), true, Forward, this);
            }
        }
        ,
        MineFloor{

            @Override
            protected void start(HighwayBuilder b) {
                this.mine(b, b.blockPosProvider.getFloor(), false, Forward, this);
            }

            @Override
            protected void tick(HighwayBuilder b) {
                this.mine(b, b.blockPosProvider.getFloor(), false, Forward, this);
            }
        }
        ,
        MineRailings{

            @Override
            protected void start(HighwayBuilder b) {
                this.mine(b, b.blockPosProvider.getRailings(0), false, Forward, this);
            }

            @Override
            protected void tick(HighwayBuilder b) {
                this.mine(b, b.blockPosProvider.getRailings(0), false, Forward, this);
            }
        }
        ,
        MineAboveRailings{

            @Override
            protected void start(HighwayBuilder b) {
                this.mine(b, b.blockPosProvider.getRailings(1), true, Forward, this);
            }

            @Override
            protected void tick(HighwayBuilder b) {
                this.mine(b, b.blockPosProvider.getRailings(1), true, Forward, this);
            }
        }
        ,
        PlaceCornerBlock{

            @Override
            protected void start(HighwayBuilder b) {
                int slot = this.findBlocksToPlacePrioritizeTrash(b);
                if (slot == -1) {
                    return;
                }
                this.place(b, b.blockPosProvider.getRailings(-1), slot, Forward);
            }

            @Override
            protected void tick(HighwayBuilder b) {
                int slot = this.findBlocksToPlacePrioritizeTrash(b);
                if (slot == -1) {
                    return;
                }
                this.place(b, b.blockPosProvider.getRailings(-1), slot, Forward);
            }
        }
        ,
        PlaceRailings{

            @Override
            protected void start(HighwayBuilder b) {
                int slot = this.findBlocksToPlace(b);
                if (slot == -1) {
                    return;
                }
                this.place(b, b.blockPosProvider.getRailings(0), slot, Forward);
            }

            @Override
            protected void tick(HighwayBuilder b) {
                int slot = this.findBlocksToPlace(b);
                if (slot == -1) {
                    return;
                }
                this.place(b, b.blockPosProvider.getRailings(0), slot, Forward);
            }
        }
        ,
        PlaceFloor{

            @Override
            protected void start(HighwayBuilder b) {
                int slot = this.findBlocksToPlace(b);
                if (slot == -1) {
                    return;
                }
                this.place(b, b.blockPosProvider.getFloor(), slot, Forward);
            }

            @Override
            protected void tick(HighwayBuilder b) {
                int slot = this.findBlocksToPlace(b);
                if (slot == -1) {
                    return;
                }
                this.place(b, b.blockPosProvider.getFloor(), slot, Forward);
            }
        }
        ,
        ThrowOutTrash{
            private int skipSlot;
            private boolean timerEnabled;
            private boolean firstTick;
            private boolean threwItems;
            private int timer;
            private static final class_1799[] ITEMS = new class_1799[27];

            @Override
            protected void start(HighwayBuilder b) {
                int biggestCount = 0;
                for (int i = 0; i < ((HighwayBuilder)b).mc.field_1724.method_31548().method_67533().size(); ++i) {
                    class_1799 itemStack = ((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(i);
                    if (!(itemStack.method_7909() instanceof class_1747) || !b.trashItems.get().contains(itemStack.method_7909()) || itemStack.method_7947() <= biggestCount) continue;
                    biggestCount = itemStack.method_7947();
                    this.skipSlot = i;
                    if (biggestCount >= 64) break;
                }
                if (biggestCount == 0) {
                    this.skipSlot = -1;
                }
                this.timerEnabled = false;
                this.firstTick = true;
                this.threwItems = false;
            }

            @Override
            protected void tick(HighwayBuilder b) {
                if (this.timerEnabled) {
                    if (this.timer > 0) {
                        --this.timer;
                    } else {
                        b.setState(b.lastState);
                    }
                    return;
                }
                ((HighwayBuilder)b).mc.field_1724.method_36456(b.dir.opposite().yaw);
                ((HighwayBuilder)b).mc.field_1724.method_36457(-25.0f);
                if (this.firstTick) {
                    this.firstTick = false;
                    return;
                }
                if (!((HighwayBuilder)b).mc.field_1724.field_7512.method_34255().method_7960()) {
                    InvUtils.dropHand();
                    return;
                }
                for (int i = 0; i < ((HighwayBuilder)b).mc.field_1724.method_31548().method_67533().size(); ++i) {
                    if (i == this.skipSlot) continue;
                    class_1799 itemStack = ((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(i);
                    if (b.trashItems.get().contains(itemStack.method_7909())) {
                        InvUtils.drop().slot(i);
                        this.threwItems = true;
                        return;
                    }
                    if (!b.ejectUselessShulkers.get().booleanValue() || !Utils.isShulker(itemStack.method_7909())) continue;
                    Utils.getItemsInContainerItem(itemStack, ITEMS);
                    boolean eject = true;
                    for (class_1799 stack : ITEMS) {
                        class_1792 class_17922 = stack.method_7909();
                        if (class_17922 instanceof class_1747) {
                            class_1747 bi = (class_1747)class_17922;
                            if (b.blocksToPlace.get().contains(bi.method_7711()) || b.blocksToPlace.get().contains(class_2246.field_10540) && bi == class_1802.field_8466) {
                                eject = false;
                                break;
                            }
                        }
                        if (stack.method_31573(class_3489.field_42614)) {
                            eject = false;
                            break;
                        }
                        if (!stack.method_57826(class_9334.field_50075) || Modules.get().get(AutoEat.class).blacklist.get().contains(stack.method_7909())) continue;
                        eject = false;
                        break;
                    }
                    if (!eject) continue;
                    InvUtils.drop().slot(i);
                    this.threwItems = true;
                    return;
                }
                this.timerEnabled = true;
                this.timer = this.threwItems ? 10 : 1;
            }
        }
        ,
        PlaceEChestBlockade{

            @Override
            protected void tick(HighwayBuilder b) {
                int slot = this.findBlocksToPlacePrioritizeTrash(b);
                if (slot == -1) {
                    return;
                }
                this.place(b, b.blockPosProvider.getBlockade(false, b.blockadeType.get()), slot, MineEnderChests);
            }
        }
        ,
        MineEChestBlockade{

            @Override
            protected void tick(HighwayBuilder b) {
                this.mine(b, b.blockPosProvider.getBlockade(true, b.blockadeType.get()), true, Center, Forward);
            }
        }
        ,
        MineEnderChests{
            private static final MBlockPos pos = new MBlockPos();
            private int minimumObsidian;
            private boolean first;
            private boolean primed;
            private boolean stopTimerEnabled;
            private int stopTimer;
            private int moveTimer;
            private int rebreakTimer;
            private int timeout;

            @Override
            protected void start(HighwayBuilder b) {
                if (b.lastState != Center && b.lastState != ThrowOutTrash && b.lastState != PlaceEChestBlockade) {
                    b.setState(Center);
                    return;
                }
                if (b.lastState == Center) {
                    b.setState(ThrowOutTrash);
                    return;
                }
                if (b.lastState == ThrowOutTrash) {
                    b.setState(PlaceEChestBlockade);
                    return;
                }
                int emptySlots = 0;
                for (int i = 0; i < ((HighwayBuilder)b).mc.field_1724.method_31548().method_67533().size(); ++i) {
                    if (!((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(i).method_7960()) continue;
                    ++emptySlots;
                }
                if (emptySlots == 0) {
                    b.error("No empty slots.", new Object[0]);
                    return;
                }
                int minimumSlots = Math.max(emptySlots - b.minEmpty.get(), 1);
                this.minimumObsidian = minimumSlots * 64;
                this.first = true;
                this.timeout = 0;
                this.moveTimer = 0;
                this.stopTimerEnabled = false;
                this.primed = false;
            }

            @Override
            protected void tick(HighwayBuilder b) {
                if (this.stopTimerEnabled) {
                    if (this.stopTimer > 0) {
                        --this.stopTimer;
                    } else {
                        b.setState(MineEChestBlockade);
                    }
                    return;
                }
                HorizontalDirection dir = b.dir.diagonal ? b.dir.rotateLeft().rotateLeftSkipOne() : b.dir.opposite();
                pos.set((class_1297)((HighwayBuilder)b).mc.field_1724).offset(dir);
                if (this.moveTimer > 0) {
                    ((HighwayBuilder)b).mc.field_1724.method_36456(dir.yaw);
                    b.input.forward(this.moveTimer > 2);
                    --this.moveTimer;
                    return;
                }
                int obsidianCount = 0;
                for (class_1297 entity : ((HighwayBuilder)b).mc.field_1687.method_8335((class_1297)((HighwayBuilder)b).mc.field_1724, new class_238((double)15.pos.x, (double)15.pos.y, (double)15.pos.z, (double)(15.pos.x + 1), (double)(15.pos.y + 2), (double)(15.pos.z + 1)))) {
                    class_1542 itemEntity;
                    if (!(entity instanceof class_1542) || (itemEntity = (class_1542)entity).method_6983().method_7909() != class_1802.field_8281) continue;
                    obsidianCount += itemEntity.method_6983().method_7947();
                }
                for (int i = 0; i < ((HighwayBuilder)b).mc.field_1724.method_31548().method_67533().size(); ++i) {
                    class_1799 itemStack2 = ((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(i);
                    if (itemStack2.method_7909() != class_1802.field_8281) continue;
                    obsidianCount += itemStack2.method_7947();
                }
                if (obsidianCount >= this.minimumObsidian) {
                    this.stopTimerEnabled = true;
                    this.stopTimer = 12;
                    return;
                }
                class_2338 bp = pos.getBlockPos();
                class_2680 blockState = ((HighwayBuilder)b).mc.field_1687.method_8320(bp);
                if (blockState.method_26204() == class_2246.field_10443) {
                    class_437 class_4372 = ((HighwayBuilder)b).mc.field_1755;
                    if (class_4372 instanceof class_476) {
                        class_476 screen = (class_476)class_4372;
                        if (((class_1707)screen.method_17577()).field_7763 != b.syncId) {
                            return;
                        }
                        ((HighwayBuilder)b).mc.field_1755.method_25419();
                    }
                    if (!EChestMemory.isKnown()) {
                        if (b.rotation.get().place) {
                            Rotations.rotate(Rotations.getYaw(bp), Rotations.getPitch(bp), () -> ((HighwayBuilder)b).mc.field_1761.method_2896(((HighwayBuilder)b).mc.field_1724, class_1268.field_5808, new class_3965(class_243.method_24953((class_2382)bp), class_2350.field_11036, bp, false)));
                        } else {
                            ((HighwayBuilder)b).mc.field_1761.method_2896(((HighwayBuilder)b).mc.field_1724, class_1268.field_5808, new class_3965(class_243.method_24953((class_2382)bp), class_2350.field_11036, bp, false));
                        }
                        return;
                    }
                    if (this.first) {
                        this.moveTimer = 8;
                        this.first = false;
                        return;
                    }
                    int slot = this.findAndMoveBestToolToHotbar(b, blockState, true);
                    if (slot == -1) {
                        b.error("Cannot find pickaxe without silk touch to mine ender chests.", new Object[0]);
                        return;
                    }
                    InvUtils.swap(slot, false);
                    if (b.rebreakEchests.get().booleanValue() && this.primed) {
                        ++this.timeout;
                        if (this.timeout > 60) {
                            this.primed = false;
                            this.timeout = 0;
                            return;
                        }
                        if (this.rebreakTimer > 0) {
                            --this.rebreakTimer;
                            return;
                        }
                        this.rebreakTimer = b.rebreakTimer.get();
                        if (b.rotation.get().mine) {
                            Rotations.rotate(Rotations.getYaw(bp), Rotations.getPitch(bp), () -> ((HighwayBuilder)b).mc.field_1761.method_41931(((HighwayBuilder)b).mc.field_1687, sequence -> new class_2846(class_2846.class_2847.field_12973, bp, BlockUtils.getDirection(bp), sequence)));
                        } else {
                            ((HighwayBuilder)b).mc.field_1761.method_41931(((HighwayBuilder)b).mc.field_1687, sequence -> new class_2846(class_2846.class_2847.field_12973, bp, BlockUtils.getDirection(bp), sequence));
                        }
                    } else if (b.rotation.get().mine) {
                        Rotations.rotate(Rotations.getYaw(bp), Rotations.getPitch(bp), () -> BlockUtils.breakBlock(bp, true));
                    } else {
                        BlockUtils.breakBlock(bp, true);
                    }
                } else {
                    int slot = this.findAndMoveToHotbar(b, itemStack -> itemStack.method_7909() == class_1802.field_8466);
                    if (slot == -1 || this.countItem(b, stack -> stack.method_7909().equals(class_1802.field_8466)) <= b.saveEchests.get()) {
                        this.stopTimerEnabled = true;
                        this.stopTimer = 12;
                        return;
                    }
                    if (this.countItem(b, stack -> stack.method_31573(class_3489.field_42614)) <= b.savePickaxes.get() && (b.searchEnderChest.get().booleanValue() || b.searchShulkers.get().booleanValue())) {
                        b.restockTask.setPickaxes();
                    }
                    if (!this.first) {
                        this.primed = true;
                    }
                    BlockUtils.place(bp, class_1268.field_5808, slot, b.rotation.get().place, 0, true, true, false);
                    this.timeout = 0;
                }
            }
        }
        ,
        Restock{
            private static final MBlockPos pos = new MBlockPos();
            private static final class_1799[] ITEMS = new class_1799[27];
            private int minimumSlots;
            private int stopTimer;
            private int delayTimer;
            private boolean breakContainer;
            private boolean indicateStopping;
            private Predicate<class_1799> shulkerPredicate;
            private int slot = -1;

            @Override
            protected void start(HighwayBuilder b) {
                this.slot = -1;
                if (this.shulkerPredicate == null) {
                    this.setShulkerPredicate(b);
                }
                if (b.restockTask.tasksInactive()) {
                    b.setState(Forward);
                    return;
                }
                if (b.lastState != Center && b.lastState != ThrowOutTrash && b.lastState != PlaceShulkerBlockade && b.lastState != this) {
                    b.setState(Center);
                    return;
                }
                if (b.lastState == Center) {
                    b.setState(ThrowOutTrash);
                    return;
                }
                if (this.slot == -1 && b.searchShulkers.get().booleanValue()) {
                    this.slot = this.findAndMoveToHotbar(b, this.shulkerPredicate);
                    if (this.slot != -1 && b.lastState != PlaceShulkerBlockade) {
                        b.setState(PlaceShulkerBlockade);
                    }
                }
                if (this.slot == -1 && b.searchEnderChest.get().booleanValue() && this.countItem(b, stack -> stack.method_7909().equals(class_1802.field_8466)) > 0) {
                    boolean stop = EChestMemory.isKnown();
                    if (EChestMemory.isKnown()) {
                        for (class_1799 stack2 : EChestMemory.ITEMS) {
                            class_1792 class_17922;
                            if (b.restockTask.materials && (class_17922 = stack2.method_7909()) instanceof class_1747) {
                                class_1747 bi = (class_1747)class_17922;
                                if (b.blocksToPlace.get().contains(bi.method_7711()) || b.blocksToPlace.get().contains(class_2246.field_10540) && bi == class_1802.field_8466) {
                                    stop = false;
                                    break;
                                }
                            }
                            if (b.restockTask.pickaxes && stack2.method_31573(class_3489.field_42614)) {
                                stop = false;
                                break;
                            }
                            if (b.restockTask.food && stack2.method_57826(class_9334.field_50075) && !Modules.get().get(AutoEat.class).blacklist.get().contains(stack2.method_7909())) {
                                stop = false;
                                break;
                            }
                            if (!b.searchShulkers.get().booleanValue() || !this.shulkerPredicate.test(stack2)) continue;
                            stop = false;
                            break;
                        }
                    }
                    if (!stop) {
                        this.slot = this.findAndMoveToHotbar(b, itemStack -> itemStack.method_7909() == class_1802.field_8466);
                    }
                }
                if (this.slot == -1) {
                    boolean restockOccurred;
                    boolean bl = restockOccurred = b.restockTask.materials && (this.hasItem(b, stack -> {
                        class_1792 patt0$temp = stack.method_7909();
                        if (!(patt0$temp instanceof class_1747)) return false;
                        class_1747 bi = (class_1747)patt0$temp;
                        if (!b.blocksToPlace.get().contains(bi.method_7711())) return false;
                        return true;
                    }) || b.blocksToPlace.get().contains(class_2246.field_10540) && this.countItem(b, itemStack -> itemStack.method_7909() == class_1802.field_8466) > b.saveEchests.get()) || b.restockTask.pickaxes && this.countItem(b, itemStack -> itemStack.method_31573(class_3489.field_42614)) > b.savePickaxes.get() || b.restockTask.food && this.hasItem(b, itemStack -> itemStack.method_57826(class_9334.field_50075) && !Modules.get().get(AutoEat.class).blacklist.get().contains(itemStack.method_7909()));
                    if (restockOccurred) {
                        b.setState(ThrowOutTrash, Forward);
                    } else {
                        b.error("Unable to perform restock for '" + b.restockTask.item() + "'.", new Object[0]);
                    }
                    return;
                }
                int restockSlots = -b.minEmpty.get().intValue();
                for (int i = 0; i < ((HighwayBuilder)b).mc.field_1724.method_31548().method_67533().size(); ++i) {
                    if (!((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(i).method_7960()) continue;
                    ++restockSlots;
                }
                if (restockSlots <= 0) {
                    b.error("No empty slots for restocking items.", new Object[0]);
                    return;
                }
                this.minimumSlots = b.restockTask.materials ? restockSlots : 1;
                HorizontalDirection dir = b.dir.diagonal ? b.dir.rotateLeft().rotateLeftSkipOne() : b.dir.opposite();
                pos.set((class_1297)((HighwayBuilder)b).mc.field_1724).offset(dir);
                this.breakContainer = ((HighwayBuilder)b).mc.field_1687.method_8320(pos.getBlockPos()).method_26204() == class_2246.field_10443;
                this.indicateStopping = false;
                this.delayTimer = b.inventoryDelay.get();
            }

            @Override
            protected void tick(HighwayBuilder b) {
                if (this.slot == -1) {
                    b.error("Invalid restocking action.", new Object[0]);
                    return;
                }
                if (this.indicateStopping && !this.breakContainer) {
                    if (this.stopTimer > 0) {
                        --this.stopTimer;
                    } else if (b.lastState == PlaceShulkerBlockade) {
                        b.setState(MineShulkerBlockade);
                    } else {
                        b.setState(ThrowOutTrash, Forward);
                    }
                    return;
                }
                if (b.restockTask.tasksInactive()) {
                    b.setState(Forward);
                    return;
                }
                if (this.delayTimer > 0) {
                    --this.delayTimer;
                    return;
                }
                int slotsPulled = 0;
                if (b.restockTask.materials) {
                    slotsPulled += this.countSlots(b, itemStack -> {
                        class_1792 patt0$temp = itemStack.method_7909();
                        if (!(patt0$temp instanceof class_1747)) return false;
                        class_1747 bi = (class_1747)patt0$temp;
                        if (!b.blocksToPlace.get().contains(bi.method_7711())) return false;
                        return true;
                    });
                    if (b.blocksToPlace.get().contains(class_2246.field_10540)) {
                        slotsPulled += (this.countItem(b, itemStack -> itemStack.method_7909() == class_1802.field_8466) - b.saveEchests.get()) * 8 / 64;
                    }
                }
                if (b.restockTask.pickaxes) {
                    slotsPulled += this.countSlots(b, itemStack -> itemStack.method_31573(class_3489.field_42614)) - b.savePickaxes.get();
                }
                if (b.restockTask.food) {
                    slotsPulled += this.countSlots(b, itemStack -> itemStack.method_57826(class_9334.field_50075) && !Modules.get().get(AutoEat.class).blacklist.get().contains(itemStack.method_7909()));
                }
                if (slotsPulled >= this.minimumSlots && !this.indicateStopping) {
                    this.indicateStopping = true;
                    this.breakContainer = true;
                    this.stopTimer = 12;
                    if (((HighwayBuilder)b).mc.field_1755 != null) {
                        ((HighwayBuilder)b).mc.field_1755.method_25419();
                    }
                    return;
                }
                class_2338 blockPos = pos.getBlockPos();
                class_2680 blockState = ((HighwayBuilder)b).mc.field_1687.method_8320(blockPos);
                class_2248 class_22482 = blockState.method_26204();
                Objects.requireNonNull(class_22482);
                class_2248 class_22483 = class_22482;
                int n = 0;
                switch (SwitchBootstraps.typeSwitch("typeSwitch", new Object[]{class_2480.class, class_2336.class, class_2189.class}, (Object)class_22483, n)) {
                    case 0: {
                        class_2480 ignored = (class_2480)class_22483;
                        class_437 class_4372 = ((HighwayBuilder)b).mc.field_1755;
                        if (class_4372 instanceof class_495) {
                            class_495 screen = (class_495)class_4372;
                            if (((class_1733)screen.method_17577()).field_7763 != b.syncId) {
                                return;
                            }
                            class_1263 inv = ((ShulkerBoxScreenHandlerAccessor)screen.method_17577()).meteor$getInventory();
                            if (this.restockItems(b, inv)) {
                                this.delayTimer = b.inventoryDelay.get();
                                return;
                            }
                            ((HighwayBuilder)b).mc.field_1755.method_25419();
                            this.breakContainer = true;
                            break;
                        }
                        if (!b.searchShulkers.get().booleanValue()) {
                            this.breakContainer = true;
                        }
                        this.handleContainerBlock(b, blockPos);
                        break;
                    }
                    case 1: {
                        class_2336 ignored = (class_2336)class_22483;
                        class_437 class_4373 = ((HighwayBuilder)b).mc.field_1755;
                        if (class_4373 instanceof class_476) {
                            int moveTo;
                            class_476 screen = (class_476)class_4373;
                            if (((class_1707)screen.method_17577()).field_7763 != b.syncId) {
                                return;
                            }
                            class_1263 inv = ((class_1707)screen.method_17577()).method_7629();
                            if (this.restockItems(b, inv)) {
                                this.delayTimer = b.inventoryDelay.get();
                                return;
                            }
                            if (b.searchShulkers.get().booleanValue() && (moveTo = InvUtils.findEmpty().slot()) != -1) {
                                for (int i = 0; i < inv.method_5439(); ++i) {
                                    if (!this.shulkerPredicate.test(inv.method_5438(i))) continue;
                                    InvUtils.move().fromId(i).to(moveTo);
                                    this.delayTimer = b.inventoryDelay.get();
                                    break;
                                }
                            }
                            ((HighwayBuilder)b).mc.field_1755.method_25419();
                            this.breakContainer = true;
                            break;
                        }
                        if (!b.searchEnderChest.get().booleanValue()) {
                            this.breakContainer = true;
                        }
                        this.handleContainerBlock(b, blockPos);
                        break;
                    }
                    case 2: {
                        class_2189 ignored = (class_2189)class_22483;
                        if (this.breakContainer) {
                            this.breakContainer = false;
                            if (this.indicateStopping) {
                                b.restockTask.complete();
                            } else {
                                this.start(b);
                            }
                            return;
                        }
                        BlockUtils.place(blockPos, class_1268.field_5808, this.slot, b.rotation.get().place, 0, true, true, false);
                        break;
                    }
                    default: {
                        b.error("Invalid block at container restocking position?", new Object[0]);
                    }
                }
            }

            private boolean restockItems(HighwayBuilder b, class_1263 inv) {
                if (b.restockTask.materials) {
                    if (this.grabFromInventory(inv, itemStack -> {
                        class_1792 patt0$temp = itemStack.method_7909();
                        if (!(patt0$temp instanceof class_1747)) return false;
                        class_1747 bi = (class_1747)patt0$temp;
                        if (!b.blocksToPlace.get().contains(bi.method_7711())) return false;
                        return true;
                    })) {
                        return true;
                    }
                    if (b.blocksToPlace.get().contains(class_2246.field_10540) && this.grabFromInventory(inv, itemStack -> itemStack.method_7909() == class_1802.field_8466)) {
                        return true;
                    }
                }
                if (b.restockTask.pickaxes && this.grabFromInventory(inv, itemStack -> itemStack.method_31573(class_3489.field_42614))) {
                    return true;
                }
                if (b.restockTask.food) {
                    return this.grabFromInventory(inv, itemStack -> itemStack.method_57826(class_9334.field_50075) && !Modules.get().get(AutoEat.class).blacklist.get().contains(itemStack.method_7909()));
                }
                return false;
            }

            private boolean grabFromInventory(class_1263 inv, Predicate<class_1799> filterItem) {
                for (int i = 0; i < inv.method_5439(); ++i) {
                    if (!filterItem.test(inv.method_5438(i))) continue;
                    InvUtils.shiftClick().slotId(i);
                    return true;
                }
                return false;
            }

            private void setShulkerPredicate(HighwayBuilder b) {
                this.shulkerPredicate = itemStack -> {
                    if (!Utils.isShulker(itemStack.method_7909())) {
                        return false;
                    }
                    Utils.getItemsInContainerItem(itemStack, ITEMS);
                    for (class_1799 stack : ITEMS) {
                        class_1792 patt0$temp;
                        if (b.restockTask.materials && (patt0$temp = stack.method_7909()) instanceof class_1747) {
                            class_1747 bi = (class_1747)patt0$temp;
                            if (b.blocksToPlace.get().contains(bi.method_7711()) || b.blocksToPlace.get().contains(class_2246.field_10540) && bi == class_1802.field_8466) {
                                return true;
                            }
                        }
                        if (b.restockTask.pickaxes && stack.method_31573(class_3489.field_42614)) {
                            return true;
                        }
                        if (!b.restockTask.food || !stack.method_57826(class_9334.field_50075) || Modules.get().get(AutoEat.class).blacklist.get().contains(stack.method_7909())) continue;
                        return true;
                    }
                    return false;
                };
            }

            private void handleContainerBlock(HighwayBuilder b, class_2338 bp) {
                if (this.breakContainer) {
                    class_2680 state = ((HighwayBuilder)b).mc.field_1687.method_8320(bp);
                    int toolSlot = this.findAndMoveBestToolToHotbar(b, state, false);
                    InvUtils.swap(toolSlot, false);
                    if (b.rotation.get().mine) {
                        Rotations.rotate(Rotations.getYaw(bp), Rotations.getPitch(bp), () -> BlockUtils.breakBlock(bp, true));
                    } else {
                        BlockUtils.breakBlock(bp, true);
                    }
                } else {
                    if (b.rotation.get().place) {
                        Rotations.rotate(Rotations.getYaw(bp), Rotations.getPitch(bp), () -> ((HighwayBuilder)b).mc.field_1761.method_2896(((HighwayBuilder)b).mc.field_1724, class_1268.field_5808, new class_3965(class_243.method_24953((class_2382)bp), class_2350.field_11036, bp, false)));
                    } else {
                        ((HighwayBuilder)b).mc.field_1761.method_2896(((HighwayBuilder)b).mc.field_1724, class_1268.field_5808, new class_3965(class_243.method_24953((class_2382)bp), class_2350.field_11036, bp, false));
                    }
                    this.delayTimer = b.inventoryDelay.get();
                }
            }

            private int countSlots(HighwayBuilder b, Predicate<class_1799> predicate) {
                int count = 0;
                for (int i = 0; i < ((HighwayBuilder)b).mc.field_1724.method_31548().method_67533().size(); ++i) {
                    class_1799 stack = ((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(i);
                    if (!predicate.test(stack)) continue;
                    ++count;
                }
                return count;
            }
        }
        ,
        PlaceShulkerBlockade{

            @Override
            protected void tick(HighwayBuilder b) {
                int slot = this.findBlocksToPlacePrioritizeTrash(b);
                if (slot == -1) {
                    return;
                }
                this.place(b, b.blockPosProvider.getBlockade(false, BlockadeType.Shulker), slot, Restock);
            }
        }
        ,
        MineShulkerBlockade{
            private boolean stopTimerEnabled;
            private int stopTimer;

            @Override
            protected void start(HighwayBuilder b) {
                this.stopTimerEnabled = false;
                if (b.lastState == this) {
                    this.stopTimerEnabled = true;
                    this.stopTimer = 12;
                }
            }

            @Override
            protected void tick(HighwayBuilder b) {
                if (!this.stopTimerEnabled) {
                    this.mine(b, b.blockPosProvider.getBlockade(true, b.blockadeType.get()), true, this, this);
                } else {
                    --this.stopTimer;
                    if (this.stopTimer <= 0) {
                        b.setState(ThrowOutTrash, Forward);
                    }
                }
            }
        }
        ,
        DefuseCrystalTraps{
            private int cooldown;
            private int shots;
            private class_1511 target;

            @Override
            protected void start(HighwayBuilder b) {
                if (!InvUtils.find(class_1802.field_8102).found() || !InvUtils.find(itemStack -> itemStack.method_7909() instanceof class_1744).found() && !((HighwayBuilder)b).mc.field_1724.method_31549().field_7477) {
                    b.destroyCrystalTraps.set(false);
                    b.warning("No bow found to destroy crystal traps with. Toggling the setting off.", new Object[0]);
                    b.setState(Forward);
                }
                this.cooldown = 0;
                this.shots = 0;
                this.target = null;
            }

            @Override
            protected void tick(HighwayBuilder b) {
                if (this.cooldown > 0) {
                    --this.cooldown;
                    return;
                }
                if (!InvUtils.testInMainHand(class_1802.field_8102)) {
                    int slot = this.findAndMoveToHotbar(b, itemStack -> itemStack.method_7909() instanceof class_1753);
                    if (slot == -1) {
                        b.destroyCrystalTraps.set(false);
                        b.warning("No bow found to destroy crystal traps with. Toggling the setting off.", new Object[0]);
                        b.setState(Forward);
                        ((HighwayBuilder)b).mc.field_1761.method_2897((class_1657)((HighwayBuilder)b).mc.field_1724);
                        b.drawingBow = false;
                        return;
                    }
                    InvUtils.swap(slot, false);
                }
                class_1511 potentialTarget = (class_1511)TargetUtils.get(entity -> {
                    if (!(entity instanceof class_1511)) {
                        return false;
                    }
                    class_1511 endCrystal = (class_1511)entity;
                    if (PlayerUtils.isWithin((class_1297)endCrystal, 12.0) || !PlayerUtils.isWithin((class_1297)endCrystal, 24.0)) {
                        return false;
                    }
                    if (b.ignoreCrystals.contains(endCrystal)) {
                        return false;
                    }
                    class_243 vec1 = new class_243(0.0, 0.0, 0.0);
                    class_243 vec2 = new class_243(0.0, 0.0, 0.0);
                    ((IVec3d)vec1).meteor$set(((HighwayBuilder)b).mc.field_1724.method_23317(), ((HighwayBuilder)b).mc.field_1724.method_23318() + (double)((HighwayBuilder)b).mc.field_1724.method_5751(), ((HighwayBuilder)b).mc.field_1724.method_23321());
                    ((IVec3d)vec2).meteor$set(entity.method_23317(), entity.method_23318() + 0.5, entity.method_23321());
                    return ((HighwayBuilder)b).mc.field_1687.method_17742(new class_3959(vec1, vec2, class_3959.class_3960.field_17558, class_3959.class_242.field_1348, (class_1297)((HighwayBuilder)b).mc.field_1724)).method_17783() == class_239.class_240.field_1333;
                }, SortPriority.LowestDistance);
                if (this.target == null || this.target.method_31481()) {
                    if (potentialTarget == null) {
                        b.setState(Forward);
                        ((HighwayBuilder)b).mc.field_1761.method_2897((class_1657)((HighwayBuilder)b).mc.field_1724);
                        b.drawingBow = false;
                        return;
                    }
                    this.target = potentialTarget;
                    this.shots = 0;
                }
                if (this.shots >= 3) {
                    b.ignoreCrystals.add(this.target);
                    b.warning("Detected potential hangup on a crystal. Adding it to ignore list and continuing forward.", new Object[0]);
                    b.setState(Forward);
                    ((HighwayBuilder)b).mc.field_1761.method_2897((class_1657)((HighwayBuilder)b).mc.field_1724);
                    b.drawingBow = false;
                    return;
                }
                ((HighwayBuilder)b).mc.field_1724.method_36456((float)Rotations.getYaw((class_1297)this.target));
                float pitch = this.aim(b, (class_1297)this.target);
                if (Float.isNaN(pitch)) {
                    ((HighwayBuilder)b).mc.field_1724.method_36457((float)Rotations.getPitch((class_1297)this.target));
                } else {
                    ((HighwayBuilder)b).mc.field_1724.method_36457(pitch);
                }
                if (class_1753.method_7722((int)(((HighwayBuilder)b).mc.field_1724.method_6048() - 3)) >= 1.0f) {
                    ((HighwayBuilder)b).mc.field_1761.method_2897((class_1657)((HighwayBuilder)b).mc.field_1724);
                    b.drawingBow = false;
                    this.cooldown = 20;
                    ++this.shots;
                } else {
                    b.drawingBow = true;
                    ((HighwayBuilder)b).mc.field_1761.method_2919((class_1657)((HighwayBuilder)b).mc.field_1724, class_1268.field_5808);
                }
            }

            private float aim(HighwayBuilder b, class_1297 target) {
                float velocity = class_1753.method_7722((int)((HighwayBuilder)b).mc.field_1724.method_6048());
                class_243 pos = target.method_73189();
                double relativeX = pos.field_1352 - ((HighwayBuilder)b).mc.field_1724.method_23317();
                double relativeY = pos.field_1351 + 0.5 - ((HighwayBuilder)b).mc.field_1724.method_23320();
                double relativeZ = pos.field_1350 - ((HighwayBuilder)b).mc.field_1724.method_23321();
                double hDistance = Math.sqrt(relativeX * relativeX + relativeZ * relativeZ);
                double hDistanceSq = hDistance * hDistance;
                float g = 0.006f;
                float velocitySq = velocity * velocity;
                return (float)(-Math.toDegrees(Math.atan(((double)velocitySq - Math.sqrt((double)(velocitySq * velocitySq) - (double)g * ((double)g * hDistanceSq + 2.0 * relativeY * (double)velocitySq))) / ((double)g * hDistance))));
            }
        };


        protected void start(HighwayBuilder b) {
        }

        protected abstract void tick(HighwayBuilder var1);

        protected void mine(HighwayBuilder b, MBPIterator it, boolean mineBlocksToPlace, State nextState, State lastState) {
            boolean breaking = false;
            boolean finishedBreaking = false;
            if (b.doubleMine.get().booleanValue()) {
                int slot;
                ArrayDeque<class_2338> toDoubleMine = new ArrayDeque<class_2338>();
                it.save();
                it.forEach(pos -> {
                    if (!(!BlockUtils.canBreak(pos.getBlockPos(), pos.getState()) || !mineBlocksToPlace && b.blocksToPlace.get().contains(pos.getState().method_26204()) || BlockUtils.canInstaBreak(pos.getBlockPos()) || Modules.get().get(SpeedMine.class).instamine() && !((double)pos.getState().method_26165((class_1657)((HighwayBuilder)b).mc.field_1724, (class_1922)((HighwayBuilder)b).mc.field_1687, pos.getBlockPos()) <= 0.5) || b.normalMining != null && pos.getBlockPos().equals((Object)b.normalMining.blockPos) || b.packetMining != null && pos.getBlockPos().equals((Object)b.packetMining.blockPos))) {
                        toDoubleMine.add((class_2338)pos.getBlockPos().method_25503());
                    }
                });
                it.restore();
                if (!toDoubleMine.isEmpty()) {
                    slot = this.findAndMoveBestToolToHotbar(b, ((HighwayBuilder)b).mc.field_1687.method_8320((class_2338)toDoubleMine.peek()), false);
                    if (slot == -1) {
                        return;
                    }
                    InvUtils.swap(slot, false);
                    this.doubleMine(b, toDoubleMine);
                }
                if (b.normalMining != null || b.packetMining != null) {
                    slot = this.findAndMoveBestToolToHotbar(b, b.normalMining != null ? b.normalMining.blockState : b.packetMining.blockState, false);
                    if (slot == -1) {
                        return;
                    }
                    InvUtils.swap(slot, false);
                    return;
                }
            }
            for (MBlockPos pos2 : it) {
                boolean multiBreak;
                if (b.count >= b.blocksPerTick.get()) {
                    return;
                }
                if (b.breakTimer > 0) {
                    return;
                }
                class_2680 state = pos2.getState();
                if (state.method_26215() || !mineBlocksToPlace && b.blocksToPlace.get().contains(state.method_26204())) continue;
                int slot = this.findAndMoveBestToolToHotbar(b, state, false);
                if (slot == -1) {
                    return;
                }
                InvUtils.swap(slot, false);
                class_2338 mcPos = pos2.getBlockPos();
                boolean bl = multiBreak = b.blocksPerTick.get() > 1 && BlockUtils.canInstaBreak(mcPos) && !b.rotation.get().mine;
                if (BlockUtils.canBreak(mcPos)) {
                    if (b.rotation.get().mine) {
                        Rotations.rotate(Rotations.getYaw(mcPos), Rotations.getPitch(mcPos), () -> BlockUtils.breakBlock(mcPos, true));
                    } else {
                        BlockUtils.breakBlock(mcPos, true);
                    }
                    breaking = true;
                    b.breakTimer = b.breakDelay.get();
                    if (!b.lastBreakingPos.equals(pos2)) {
                        b.lastBreakingPos.set(pos2);
                        ++b.blocksBroken;
                    }
                    ++b.count;
                    if (!multiBreak) break;
                }
                if (it.hasNext() || !BlockUtils.canInstaBreak(mcPos)) continue;
                finishedBreaking = true;
            }
            if (finishedBreaking || !breaking) {
                b.setState(nextState, lastState);
            }
        }

        private void doubleMine(HighwayBuilder b, ArrayDeque<class_2338> blocks) {
            DoubleMineBlock block;
            if (b.breakTimer > 0) {
                return;
            }
            if (b.normalMining == null) {
                block = new DoubleMineBlock(b, blocks.pop());
                b.normalMining = block.startDestroying();
                b.breakTimer = b.breakDelay.get();
                if (b.breakTimer > 0) {
                    return;
                }
            }
            if (DoubleMineBlock.rateLimited) {
                return;
            }
            if (b.packetMining == null && !blocks.isEmpty() && (block = new DoubleMineBlock(b, blocks.pop())) != null) {
                b.packetMining = b.normalMining.packetMine();
                b.normalMining = block.startDestroying();
                b.breakTimer = b.breakDelay.get();
            }
        }

        protected void place(HighwayBuilder b, MBPIterator it, int slot, State nextState) {
            boolean placed = false;
            boolean finishedPlacing = false;
            for (MBlockPos pos : it) {
                if (b.count >= it.placementsPerTick(b)) {
                    return;
                }
                if (b.placeTimer > 0) {
                    return;
                }
                if (pos.getBlockPos().method_19770((class_2374)((HighwayBuilder)b).mc.field_1724.method_33571()) > b.placeRange.get() * b.placeRange.get()) continue;
                if (BlockUtils.place(pos.getBlockPos(), class_1268.field_5808, slot, b.rotation.get().place, 0, true, true, true)) {
                    placed = true;
                    ++b.blocksPlaced;
                    b.placeTimer = b.placeDelay.get();
                    ++b.count;
                    if (b.placementsPerTick.get() == 1) break;
                }
                if (it.hasNext()) continue;
                finishedPlacing = true;
            }
            if (finishedPlacing || !placed) {
                b.setState(nextState);
            }
        }

        private int findSlot(HighwayBuilder b, Predicate<class_1799> predicate, boolean hotbar) {
            for (int i = hotbar ? 0 : 9; i < (hotbar ? 9 : ((HighwayBuilder)b).mc.field_1724.method_31548().method_67533().size()); ++i) {
                if (!predicate.test(((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(i))) continue;
                return i;
            }
            return -1;
        }

        protected int findHotbarSlot(HighwayBuilder b, boolean replaceTools) {
            int thrashSlot = -1;
            int slotsWithBlocks = 0;
            int slotWithLeastBlocks = -1;
            int slotWithLeastBlocksCount = Integer.MAX_VALUE;
            for (int i = 0; i < 9; ++i) {
                class_1792 class_17922;
                class_1799 itemStack = ((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(i);
                if (itemStack.method_7960()) {
                    return i;
                }
                if (replaceTools && AutoTool.isTool(itemStack)) {
                    return i;
                }
                if (b.trashItems.get().contains(itemStack.method_7909())) {
                    thrashSlot = i;
                }
                if (!((class_17922 = itemStack.method_7909()) instanceof class_1747)) continue;
                class_1747 blockItem = (class_1747)class_17922;
                if (!b.blocksToPlace.get().contains(blockItem.method_7711()) && (!b.blocksToPlace.get().contains(class_2246.field_10540) || blockItem != class_1802.field_8466)) continue;
                ++slotsWithBlocks;
                if (itemStack.method_7947() >= slotWithLeastBlocksCount) continue;
                slotWithLeastBlocksCount = itemStack.method_7947();
                slotWithLeastBlocks = i;
            }
            if (thrashSlot != -1) {
                return thrashSlot;
            }
            if (slotsWithBlocks > 0) {
                return slotWithLeastBlocks;
            }
            b.error("No empty space in hotbar.", new Object[0]);
            return -1;
        }

        protected boolean hasItem(HighwayBuilder b, Predicate<class_1799> predicate) {
            for (int i = 0; i < ((HighwayBuilder)b).mc.field_1724.method_31548().method_67533().size(); ++i) {
                if (!predicate.test(((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(i))) continue;
                return true;
            }
            return false;
        }

        protected int countItem(HighwayBuilder b, Predicate<class_1799> predicate) {
            int count = 0;
            for (int i = 0; i < ((HighwayBuilder)b).mc.field_1724.method_31548().method_67533().size(); ++i) {
                class_1799 stack = ((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(i);
                if (!predicate.test(stack)) continue;
                count += stack.method_7947();
            }
            return count;
        }

        protected int findAndMoveToHotbar(HighwayBuilder b, Predicate<class_1799> predicate) {
            int slot = this.findSlot(b, predicate, true);
            if (slot != -1) {
                return slot;
            }
            int hotbarSlot = this.findHotbarSlot(b, false);
            if (hotbarSlot == -1) {
                return -1;
            }
            slot = this.findSlot(b, predicate, false);
            if (slot == -1) {
                return -1;
            }
            InvUtils.move().from(slot).toHotbar(hotbarSlot);
            InvUtils.dropHand();
            return hotbarSlot;
        }

        protected int findAndMoveBestToolToHotbar(HighwayBuilder b, class_2680 blockState, boolean noSilkTouch) {
            int count;
            if (((HighwayBuilder)b).mc.field_1724.method_68878()) {
                return ((HighwayBuilder)b).mc.field_1724.method_31548().method_67532();
            }
            double bestScore = -1.0;
            int bestSlot = -1;
            for (int i = 0; i < ((HighwayBuilder)b).mc.field_1724.method_31548().method_67533().size(); ++i) {
                double score = AutoTool.getScore(((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(i), blockState, false, false, AutoTool.EnchantPreference.None, itemStack -> {
                    if (noSilkTouch && Utils.hasEnchantment(itemStack, (class_5321<class_1887>)class_1893.field_9099)) {
                        return false;
                    }
                    return b.dontBreakTools.get() == false || itemStack.method_7936() - itemStack.method_7919() > itemStack.method_7936() * (b.breakDurability.get() / 100);
                });
                if (!(score > bestScore)) continue;
                bestScore = score;
                bestSlot = i;
            }
            if (bestSlot == -1) {
                return ((HighwayBuilder)b).mc.field_1724.method_31548().method_67532();
            }
            class_1799 bestStack = ((HighwayBuilder)b).mc.field_1724.method_31548().method_5438(bestSlot);
            if (bestStack.method_31573(class_3489.field_42614) && (count = this.countItem(b, stack -> stack.method_31573(class_3489.field_42614))) <= b.savePickaxes.get() && (!b.restockTask.pickaxes || bestStack.method_7936() - bestStack.method_7919() <= bestStack.method_7936() * (b.breakDurability.get() / 100))) {
                if (!b.restockTask.pickaxes && (b.searchEnderChest.get().booleanValue() || b.searchShulkers.get().booleanValue())) {
                    b.restockTask.setPickaxes();
                } else {
                    b.error("Found less than the minimum amount of pickaxes required: " + count + "/" + (b.savePickaxes.get() + 1), new Object[0]);
                }
                return -1;
            }
            if (bestSlot < 9) {
                return bestSlot;
            }
            int hotbarSlot = this.findHotbarSlot(b, true);
            if (hotbarSlot == -1) {
                return -1;
            }
            InvUtils.move().from(bestSlot).toHotbar(hotbarSlot);
            InvUtils.dropHand();
            return hotbarSlot;
        }

        protected int findBlocksToPlace(HighwayBuilder b) {
            int slot = this.findAndMoveToHotbar(b, itemStack -> {
                class_1792 patt0$temp = itemStack.method_7909();
                if (!(patt0$temp instanceof class_1747)) return false;
                class_1747 blockItem = (class_1747)patt0$temp;
                if (!b.blocksToPlace.get().contains(blockItem.method_7711())) return false;
                return true;
            });
            if (slot == -1) {
                if (b.mineEnderChests.get().booleanValue() && b.blocksToPlace.get().contains(class_2246.field_10540) && this.countItem(b, stack -> stack.method_7909().equals(class_1802.field_8466)) > b.saveEchests.get()) {
                    b.setState(MineEnderChests);
                } else if (b.searchEnderChest.get().booleanValue() || b.searchShulkers.get().booleanValue()) {
                    b.restockTask.setMaterials();
                } else {
                    b.error("Out of blocks to place.", new Object[0]);
                }
                return -1;
            }
            return slot;
        }

        protected int findBlocksToPlacePrioritizeTrash(HighwayBuilder b) {
            int slot = this.findAndMoveToHotbar(b, itemStack -> {
                if (!(itemStack.method_7909() instanceof class_1747)) {
                    return false;
                }
                return b.trashItems.get().contains(itemStack.method_7909());
            });
            return slot != -1 ? slot : this.findBlocksToPlace(b);
        }
    }

    public static class DoubleMineBlock {
        public static boolean rateLimited = false;
        public final class_2338 blockPos;
        public final class_2680 blockState;
        private final class_2248 block;
        private final class_2350 direction;
        private final HighwayBuilder b;
        private final Vector3d vec3 = new Vector3d(0.0);
        private int normalStartTime;
        private int packetStartTime;
        private boolean packet;

        public DoubleMineBlock(HighwayBuilder b, class_2338 pos) {
            this.b = b;
            this.blockPos = pos;
            this.blockState = ((HighwayBuilder)b).mc.field_1687.method_8320(this.blockPos);
            this.block = this.blockState.method_26204();
            this.direction = BlockUtils.getDirection(pos);
            this.packet = false;
        }

        public DoubleMineBlock startDestroying() {
            ((HighwayBuilder)this.b).mc.field_1761.method_41931(((HighwayBuilder)this.b).mc.field_1687, sequence -> new class_2846(class_2846.class_2847.field_12968, this.blockPos, this.direction, sequence));
            this.normalStartTime = ((HighwayBuilder)this.b).mc.field_1724.field_6012;
            return this;
        }

        public DoubleMineBlock stopDestroying() {
            ((HighwayBuilder)this.b).mc.field_1761.method_41931(((HighwayBuilder)this.b).mc.field_1687, sequence -> new class_2846(class_2846.class_2847.field_12973, this.blockPos, this.direction, sequence));
            return this;
        }

        public DoubleMineBlock packetMine() {
            this.packetStartTime = ((HighwayBuilder)this.b).mc.field_1724.field_6012;
            this.packet = true;
            return this.stopDestroying();
        }

        public boolean isReady() {
            return this.progress() >= (this.b.fastBreak.get() != false ? 0.7 : 1.0);
        }

        public boolean shouldRemove() {
            boolean distance;
            boolean bl = distance = !this.packet && Utils.distance(((HighwayBuilder)this.b).mc.field_1724.method_33571().field_1352, ((HighwayBuilder)this.b).mc.field_1724.method_33571().field_1351, ((HighwayBuilder)this.b).mc.field_1724.method_33571().field_1350, this.blockPos.method_10263() + this.direction.method_10148(), this.blockPos.method_10264() + this.direction.method_10164(), this.blockPos.method_10260() + this.direction.method_10165()) > ((HighwayBuilder)this.b).mc.field_1724.method_55754();
            boolean timeout = this.progress() > 2.0 && ((HighwayBuilder)this.b).mc.field_1724.field_6012 - (this.packet ? this.packetStartTime : this.normalStartTime) > 60;
            return distance || timeout;
        }

        public double progress() {
            int slot = ((HighwayBuilder)this.b).mc.field_1724.method_31548().method_67532();
            return BlockUtils.getBreakDelta(slot, this.blockState) * (double)(((HighwayBuilder)this.b).mc.field_1724.field_6012 - (this.packet ? this.packetStartTime : this.normalStartTime) + 1);
        }

        public void renderLetter() {
            this.vec3.set((double)this.blockPos.method_10263() + 0.5, (double)this.blockPos.method_10264() + 0.5, (double)this.blockPos.method_10260() + 0.5);
            if (!NametagUtils.to2D(this.vec3, 2.0)) {
                return;
            }
            NametagUtils.begin(this.vec3);
            TextRenderer.get().begin(1.0, false, true);
            String letter = this.packet ? "P" : "N";
            double w = TextRenderer.get().getWidth(letter) / 2.0;
            TextRenderer.get().render(letter, -w, 0.0, Color.WHITE, true);
            TextRenderer.get().end();
            NametagUtils.end();
        }
    }

    private static interface MBPIterator
    extends Iterator<MBlockPos>,
    Iterable<MBlockPos> {
        public void save();

        public void restore();

        @Override
        @NotNull
        default public Iterator<MBlockPos> iterator() {
            return this;
        }

        default public int placementsPerTick(HighwayBuilder b) {
            return b.placementsPerTick.get();
        }
    }

    private static class MBPIteratorFilter
    implements MBPIterator {
        private final MBPIterator it;
        private final Predicate<MBlockPos> predicate;
        private MBlockPos pos;
        private boolean isOld = true;
        private boolean pisOld = true;

        public MBPIteratorFilter(MBPIterator it, Predicate<MBlockPos> predicate) {
            this.it = it;
            this.predicate = predicate;
        }

        @Override
        public void save() {
            this.it.save();
            this.pisOld = this.isOld;
            this.isOld = true;
        }

        @Override
        public void restore() {
            this.it.restore();
            this.isOld = this.pisOld;
        }

        @Override
        public boolean hasNext() {
            if (this.isOld) {
                this.isOld = false;
                this.pos = null;
                while (this.it.hasNext()) {
                    this.pos = (MBlockPos)this.it.next();
                    if (this.predicate.test(this.pos)) {
                        return true;
                    }
                    this.pos = null;
                }
            }
            return this.pos != null && this.predicate.test(this.pos);
        }

        @Override
        public MBlockPos next() {
            this.isOld = true;
            return this.pos;
        }
    }
}

