diff --git a/src/main/java/baritone/behavior/MemoryBehavior.java b/src/main/java/baritone/behavior/MemoryBehavior.java index b7d31223..ee8bf0e4 100644 --- a/src/main/java/baritone/behavior/MemoryBehavior.java +++ b/src/main/java/baritone/behavior/MemoryBehavior.java @@ -18,6 +18,7 @@ package baritone.behavior; import baritone.Baritone; +import baritone.api.cache.IWaypoint; import baritone.api.cache.Waypoint; import baritone.api.event.events.BlockInteractEvent; import baritone.api.event.events.PacketEvent; @@ -30,6 +31,7 @@ import baritone.cache.ContainerMemory; import baritone.utils.BlockStateInterface; import net.minecraft.block.Block; import net.minecraft.block.BlockBed; +import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.network.Packet; @@ -167,8 +169,19 @@ public final class MemoryBehavior extends Behavior { @Override public void onBlockInteract(BlockInteractEvent event) { - if (event.getType() == BlockInteractEvent.Type.USE && BlockStateInterface.getBlock(ctx, event.getPos()) instanceof BlockBed) { - baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("bed", Waypoint.Tag.BED, BetterBlockPos.from(event.getPos()))); + if (event.getType() == BlockInteractEvent.Type.USE) { + BetterBlockPos pos = BetterBlockPos.from(event.getPos()); + IBlockState state = BlockStateInterface.get(ctx, pos); + if (state.getBlock() instanceof BlockBed) { + if (state.getValue(BlockBed.PART) == BlockBed.EnumPartType.FOOT) { + pos = pos.offset(state.getValue(BlockBed.FACING)); + } + Set waypoints = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getByTag(IWaypoint.Tag.BED); + boolean exists = waypoints.stream().map(IWaypoint::getLocation).filter(pos::equals).findFirst().isPresent(); + if (!exists) { + baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("bed", Waypoint.Tag.BED, pos)); + } + } } } diff --git a/src/main/java/baritone/command/defaults/WaypointsCommand.java b/src/main/java/baritone/command/defaults/WaypointsCommand.java index 5d964da4..5a45686a 100644 --- a/src/main/java/baritone/command/defaults/WaypointsCommand.java +++ b/src/main/java/baritone/command/defaults/WaypointsCommand.java @@ -17,9 +17,11 @@ package baritone.command.defaults; +import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.cache.IWaypoint; import baritone.api.cache.Waypoint; +import baritone.api.cache.IWorldData; import baritone.api.command.Command; import baritone.api.command.argument.IArgConsumer; import baritone.api.command.datatypes.ForWaypoints; @@ -41,12 +43,15 @@ import net.minecraft.util.text.event.HoverEvent; import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.Stream; import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX; public class WaypointsCommand extends Command { + private Map> deletedWaypoints = new HashMap<>(); + public WaypointsCommand(IBaritone baritone) { super(baritone, "waypoints", "waypoint", "wp"); } @@ -125,11 +130,13 @@ public class WaypointsCommand extends Command { ); } } else if (action == Action.SAVE) { - IWaypoint.Tag tag = IWaypoint.Tag.getByName(args.getString()); + IWaypoint.Tag tag = args.hasAny() ? IWaypoint.Tag.getByName(args.peekString()) : null; if (tag == null) { - throw new CommandInvalidStateException(String.format("'%s' is not a tag ", args.consumedString())); + tag = IWaypoint.Tag.USER; + } else { + args.get(); } - String name = args.hasAny() ? args.getString() : ""; + String name = (args.hasExactlyOne() || args.hasExactly(4)) ? args.getString() : ""; BetterBlockPos pos = args.hasAny() ? args.getDatatypePost(RelativeBlockPos.INSTANCE, ctx.playerFeet()) : ctx.playerFeet(); @@ -147,7 +154,42 @@ public class WaypointsCommand extends Command { for (IWaypoint waypoint : waypoints) { ForWaypoints.waypoints(this.baritone).removeWaypoint(waypoint); } - logDirect(String.format("Cleared %d waypoints", waypoints.length)); + deletedWaypoints.computeIfAbsent(baritone.getWorldProvider().getCurrentWorld(), k -> new ArrayList<>()).addAll(Arrays.asList(waypoints)); + ITextComponent textComponent = new TextComponentString(String.format("Cleared %d waypoints, click to restore them", waypoints.length)); + textComponent.getStyle().setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + String.format( + "%s%s restore @ %s", + FORCE_COMMAND_PREFIX, + label, + Stream.of(waypoints).map(wp -> Long.toString(wp.getCreationTimestamp())).collect(Collectors.joining(" ")) + ) + )); + logDirect(textComponent); + } else if (action == Action.RESTORE) { + List waypoints = new ArrayList<>(); + List deletedWaypoints = this.deletedWaypoints.getOrDefault(baritone.getWorldProvider().getCurrentWorld(), Collections.emptyList()); + if (args.peekString().equals("@")) { + args.get(); + // no args.requireMin(1) because if the user clears an empty tag there is nothing to restore + while (args.hasAny()) { + long timestamp = args.getAs(Long.class); + for (IWaypoint waypoint : deletedWaypoints) { + if (waypoint.getCreationTimestamp() == timestamp) { + waypoints.add(waypoint); + break; + } + } + } + } else { + args.requireExactly(1); + int size = deletedWaypoints.size(); + int amount = Math.min(size, args.getAs(Integer.class)); + waypoints = new ArrayList<>(deletedWaypoints.subList(size - amount, size)); + } + waypoints.forEach(ForWaypoints.waypoints(this.baritone)::addWaypoint); + deletedWaypoints.removeIf(waypoints::contains); + logDirect(String.format("Restored %d waypoints", waypoints.size())); } else { IWaypoint[] waypoints = args.getDatatypeFor(ForWaypoints.INSTANCE); IWaypoint waypoint = null; @@ -216,6 +258,20 @@ public class WaypointsCommand extends Command { waypoint.getCreationTimestamp() ) )); + ITextComponent recreateComponent = new TextComponentString("Click to show a command to recreate this waypoint"); + recreateComponent.getStyle().setClickEvent(new ClickEvent( + ClickEvent.Action.SUGGEST_COMMAND, + String.format( + "%s%s save %s %s %s %s %s", + Baritone.settings().prefix.value, // This uses the normal prefix because it is run by the user. + label, + waypoint.getTag().getName(), + waypoint.getName(), + waypoint.getLocation().x, + waypoint.getLocation().y, + waypoint.getLocation().z + ) + )); ITextComponent backComponent = new TextComponentString("Click to return to the waypoints list"); backComponent.getStyle().setClickEvent(new ClickEvent( ClickEvent.Action.RUN_COMMAND, @@ -227,10 +283,22 @@ public class WaypointsCommand extends Command { )); logDirect(deleteComponent); logDirect(goalComponent); + logDirect(recreateComponent); logDirect(backComponent); } else if (action == Action.DELETE) { ForWaypoints.waypoints(this.baritone).removeWaypoint(waypoint); - logDirect("That waypoint has successfully been deleted"); + deletedWaypoints.computeIfAbsent(baritone.getWorldProvider().getCurrentWorld(), k -> new ArrayList<>()).add(waypoint); + ITextComponent textComponent = new TextComponentString("That waypoint has successfully been deleted, click to restore it"); + textComponent.getStyle().setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + String.format( + "%s%s restore @ %s", + FORCE_COMMAND_PREFIX, + label, + waypoint.getCreationTimestamp() + ) + )); + logDirect(textComponent); } else if (action == Action.GOAL) { Goal goal = new GoalBlock(waypoint.getLocation()); baritone.getCustomGoalProcess().setGoal(goal); @@ -262,6 +330,8 @@ public class WaypointsCommand extends Command { .sortAlphabetically() .filterPrefix(args.getString()) .stream(); + } else if (action == Action.RESTORE) { + return Stream.empty(); } else { return args.tabCompleteDatatype(ForWaypoints.INSTANCE); } @@ -289,15 +359,19 @@ public class WaypointsCommand extends Command { "", "Note that the info, delete, and goal commands let you specify a waypoint by tag. If there is more than one waypoint with a certain tag, then they will let you select which waypoint you mean.", "", + "Missing arguments for the save command use the USER tag, creating an unnamed waypoint and your current position as defaults.", + "", "Usage:", "> wp [l/list] - List all waypoints.", - "> wp - Save your current position as an unnamed waypoint with the specified tag.", - "> wp - Save the waypoint with the specified name.", - "> wp - Save the waypoint with the specified name and position.", - "> wp - Show info on a waypoint by tag.", - "> wp - Delete a waypoint by tag.", - "> wp - Set a goal to a waypoint by tag.", - "> wp - Set a goal to a waypoint by tag and start pathing." + "> wp - List all waypoints by tag.", + "> wp - Save an unnamed USER waypoint at your current position", + "> wp [tag] [name] [pos] - Save a waypoint with the specified tag, name and position.", + "> wp - Show info on a waypoint by tag or name.", + "> wp - Delete a waypoint by tag or name.", + "> wp - Restore the last n deleted waypoints.", + "> wp - Delete all waypoints with the specified tag.", + "> wp - Set a goal to a waypoint by tag or name.", + "> wp - Set a goal to a waypoint by tag or name and start pathing." ); } @@ -307,6 +381,7 @@ public class WaypointsCommand extends Command { SAVE("save", "s"), INFO("info", "show", "i"), DELETE("delete", "d"), + RESTORE("restore"), GOAL("goal", "g"), GOTO("goto"); private final String[] names;