incorrectPositions;
+ private String name;
+ private ISchematic schematic;
+ private Vec3i origin;
+
+ public boolean build(String schematicFile) {
+ File file = new File(new File(Minecraft.getMinecraft().gameDir, "schematics"), schematicFile);
+ NBTTagCompound tag;
+ try {
+ tag = CompressedStreamTools.read(file);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ if (tag == null) {
+ return false;
+ }
+ name = schematicFile;
+ schematic = parse(tag);
+ origin = ctx.playerFeet();
+ return true;
+ }
+
+ private static ISchematic parse(NBTTagCompound schematic) {
+ throw new UnsupportedOperationException("would rather die than parse " + schematic);
+ }
+
+ @Override
+ public boolean isActive() {
+ return schematic != null;
+ }
+
+ @Override
+ public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) {
+ // TODO somehow tell inventorybehavior what we'd like to have on the hotbar
+ // perhaps take the 16 closest positions in incorrectPositions to ctx.playerFeet that aren't desired to be air, and then snag the top 4 most common block states, then request those on the hotbar
+
+
+ // this will work as is, but it'll be trashy
+ // need to iterate over incorrectPositions and see which ones we can "correct" from our current standing position
+
+ // considerations:
+ // shouldn't break blocks that are supporting our current path segment, maybe?
+ //
+ return new PathingCommandContext(new GoalComposite(assemble()), PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH, new BuilderCalculationContext(schematic, origin));
+ }
+
+ private Goal[] assemble() {
+ BlockStateInterface bsi = new CalculationContext(baritone).bsi;
+ return incorrectPositions.stream().map(pos ->
+ bsi.get0(pos).getBlock() == Blocks.AIR ?
+ // it's air and it shouldn't be
+ new GoalBlock(pos.up())
+ // it's a block and it shouldn't be
+ : new GoalGetToBlock(pos) // replace with GoalTwoBlocks to mine using pathfinding system only
+ ).toArray(Goal[]::new);
+ }
+
+ @Override
+ public void onLostControl() {
+ incorrectPositions = null;
+ name = null;
+ schematic = null;
+ }
+
+ @Override
+ public String displayName() {
+ return "Building " + name;
+ }
+
+ /**
+ * Hotbar contents, if they were placed
+ *
+ * Always length nine, empty slots become Blocks.AIR.getDefaultState()
+ *
+ * @return
+ */
+ public List placable() {
+ List result = new ArrayList<>();
+ for (int i = 0; i < 9; i++) {
+ ItemStack stack = ctx.player().inventory.mainInventory.get(i);
+ if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) {
+ result.add(Blocks.AIR.getDefaultState());
+ continue;
+ }
+ //
+ result.add(((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(ctx.world(), ctx.playerFeet(), EnumFacing.UP, (float) ctx.player().posX, (float) ctx.player().posY, (float) ctx.player().posZ, stack.getItem().getMetadata(stack.getMetadata()), ctx.player()));
+ //
+ }
+ return result;
+ }
+
+ public class BuilderCalculationContext extends CalculationContext {
+ private final List placable;
+ private final ISchematic schematic;
+ private final int originX;
+ private final int originY;
+ private final int originZ;
+
+ public BuilderCalculationContext(ISchematic schematic, Vec3i schematicOrigin) {
+ super(BuilderProcess.this.baritone, true); // wew lad
+ this.placable = placable();
+ this.schematic = schematic;
+ this.originX = schematicOrigin.getX();
+ this.originY = schematicOrigin.getY();
+ this.originZ = schematicOrigin.getZ();
+ }
+
+ private IBlockState getSchematic(int x, int y, int z) {
+ if (schematic.inSchematic(x - originX, y - originY, z - originZ)) {
+ return schematic.desiredState(x - originX, y - originY, z - originZ);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public double costOfPlacingAt(int x, int y, int z) {
+ if (isPossiblyProtected(x, y, z) || !worldBorder.canPlaceAt(x, z)) { // make calculation fail properly if we can't build
+ return COST_INF;
+ }
+ IBlockState sch = getSchematic(x, y, z);
+ if (sch != null) {
+ // TODO this can return true even when allowPlace is off.... is that an issue?
+ if (placable.contains(sch)) {
+ return 0; // thats right we gonna make it FREE to place a block where it should go in a structure
+ // no place block penalty at all 😎
+ // i'm such an idiot that i just tried to copy and paste the epic gamer moment emoji too
+ // get added to unicode when?
+ }
+ if (!hasThrowaway) {
+ return COST_INF;
+ }
+ if (sch.getBlock() == Blocks.AIR) {
+ // we want this to be air, but they're asking if they can place here
+ // this won't be a schematic block, this will be a throwaway
+ return placeBlockCost * 2; // we're going to have to break it eventually
+ } else {
+ // we want it to be something that we don't have
+ // even more of a pain to place something wrong
+ return placeBlockCost * 3;
+ }
+ } else {
+ if (hasThrowaway) {
+ return placeBlockCost;
+ } else {
+ return COST_INF;
+ }
+ }
+ }
+
+ @Override
+ public boolean canBreakAt(int x, int y, int z) {
+ if (!allowBreak || isPossiblyProtected(x, y, z)) {
+ return false;
+ }
+ IBlockState sch = getSchematic(x, y, z);
+ if (sch != null) {
+ if (sch.getBlock() == Blocks.AIR) {
+ // it should be air
+ // regardless of current contents, we can break it
+ return true;
+ }
+ // it should be a real block
+ // is it already that block?
+ return !bsi.get0(x, y, z).equals(sch); // can break if it's wrong
+ // TODO do blocks in render distace only?
+ // TODO allow breaking blocks that we have a tool to harvest and immediately place back?
+ } else {
+ return true; // why not lol
+ }
+ }
+ }
+}
diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java
index 508a5e1b..53b93aab 100644
--- a/src/main/java/baritone/utils/ExampleBaritoneControl.java
+++ b/src/main/java/baritone/utils/ExampleBaritoneControl.java
@@ -239,6 +239,11 @@ public class ExampleBaritoneControl extends Behavior implements Helper {
logDirect("Queued " + count + " chunks for repacking");
return true;
}
+ if (msg.startsWith("build")) {
+ String file = msg.substring(5) + ".schematic";
+ logDirect("" + baritone.getBuilderProcess().build(file));
+ return true;
+ }
if (msg.equals("axis")) {
customGoalProcess.setGoalAndPath(new GoalAxis());
return true;
diff --git a/src/main/java/baritone/utils/ISchematic.java b/src/main/java/baritone/utils/ISchematic.java
new file mode 100644
index 00000000..db494c29
--- /dev/null
+++ b/src/main/java/baritone/utils/ISchematic.java
@@ -0,0 +1,39 @@
+/*
+ * This file is part of Baritone.
+ *
+ * Baritone is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Baritone is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Baritone. If not, see .
+ */
+
+package baritone.utils;
+
+import net.minecraft.block.state.IBlockState;
+
+public interface ISchematic {
+ /**
+ * Does the block at this coordinate matter to the schematic?
+ *
+ * Normally just a check for if the coordinate is in the cube.
+ *
+ * However, in the case of something like a map art, anything that's below the level of the map art doesn't matter,
+ * so this function should return false in that case. (i.e. it doesn't really have to be air below the art blocks)
+ *
+ * @param x
+ * @param y
+ * @param z
+ * @return
+ */
+ boolean inSchematic(int x, int y, int z);
+
+ IBlockState desiredState(int x, int y, int z);
+}
\ No newline at end of file
diff --git a/src/main/java/baritone/utils/PathingCommandContext.java b/src/main/java/baritone/utils/PathingCommandContext.java
new file mode 100644
index 00000000..1e8bfe6a
--- /dev/null
+++ b/src/main/java/baritone/utils/PathingCommandContext.java
@@ -0,0 +1,32 @@
+/*
+ * This file is part of Baritone.
+ *
+ * Baritone is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Baritone is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Baritone. If not, see .
+ */
+
+package baritone.utils;
+
+import baritone.api.pathing.goals.Goal;
+import baritone.api.process.PathingCommand;
+import baritone.api.process.PathingCommandType;
+import baritone.pathing.movement.CalculationContext;
+
+public class PathingCommandContext extends PathingCommand {
+ public final CalculationContext desiredCalcContext;
+
+ public PathingCommandContext(Goal goal, PathingCommandType commandType, CalculationContext context) {
+ super(goal, commandType);
+ this.desiredCalcContext = context;
+ }
+}
diff --git a/src/main/java/baritone/utils/PathingControlManager.java b/src/main/java/baritone/utils/PathingControlManager.java
index f5fff546..97685ac0 100644
--- a/src/main/java/baritone/utils/PathingControlManager.java
+++ b/src/main/java/baritone/utils/PathingControlManager.java
@@ -98,18 +98,18 @@ public class PathingControlManager implements IPathingControlManager {
break;
case FORCE_REVALIDATE_GOAL_AND_PATH:
if (!p.isPathing() && !p.getInProgress().isPresent()) {
- p.secretInternalSetGoalAndPath(command.goal);
+ p.secretInternalSetGoalAndPath(command);
}
break;
case REVALIDATE_GOAL_AND_PATH:
if (!p.isPathing() && !p.getInProgress().isPresent()) {
- p.secretInternalSetGoalAndPath(command.goal);
+ p.secretInternalSetGoalAndPath(command);
}
break;
case SET_GOAL_AND_PATH:
// now this i can do
if (command.goal != null) {
- baritone.getPathingBehavior().secretInternalSetGoalAndPath(command.goal);
+ baritone.getPathingBehavior().secretInternalSetGoalAndPath(command);
}
break;
default:
@@ -132,13 +132,13 @@ public class PathingControlManager implements IPathingControlManager {
// pwnage
p.softCancelIfSafe();
}
- p.secretInternalSetGoalAndPath(command.goal);
+ p.secretInternalSetGoalAndPath(command);
break;
case REVALIDATE_GOAL_AND_PATH:
if (Baritone.settings().cancelOnGoalInvalidation.get() && (command.goal == null || revalidateGoal(command.goal))) {
p.softCancelIfSafe();
}
- p.secretInternalSetGoalAndPath(command.goal);
+ p.secretInternalSetGoalAndPath(command);
break;
default:
}