diff options
author | Kevin <92656833+kevinthegreat1@users.noreply.github.com> | 2023-10-15 13:46:21 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-15 13:46:21 -0400 |
commit | a30a0f061c187b3e9700388b62b8de32d659dd12 (patch) | |
tree | 038b9c60ae2f3d7e3ed92c1eef87442d5ae837e3 /src/main/java/de/hysky | |
parent | a7912e665bfb1308166d4e496512037baa10b655 (diff) | |
parent | 87f8ee2c0be3d87f800fa09064ea80de61812970 (diff) | |
download | Skyblocker-a30a0f061c187b3e9700388b62b8de32d659dd12.tar.gz Skyblocker-a30a0f061c187b3e9700388b62b8de32d659dd12.tar.bz2 Skyblocker-a30a0f061c187b3e9700388b62b8de32d659dd12.zip |
Merge pull request #355 from msg-programs/beeper-creams
Creeper beam puzzle solver
Diffstat (limited to 'src/main/java/de/hysky')
5 files changed, 283 insertions, 23 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 2cf46706..bd1cd5bd 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -8,10 +8,7 @@ import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonSecrets; import de.hysky.skyblocker.skyblock.item.*; import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenMaster; import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.skyblock.*; -import de.hysky.skyblocker.skyblock.dungeon.*; import de.hysky.skyblocker.skyblock.dwarven.DwarvenHud; -import de.hysky.skyblocker.skyblock.item.*; import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry; import de.hysky.skyblocker.skyblock.quicknav.QuickNav; import de.hysky.skyblocker.skyblock.rift.TheRift; @@ -105,6 +102,7 @@ public class SkyblockerMod implements ClientModInitializer { QuiverWarning.init(); SpecialEffects.init(); ItemProtection.init(); + CreeperBeams.init(); ItemRarityBackgrounds.init(); containerSolverManager.init(); statusBarTracker.init(); diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index cb51afdc..189c6af7 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -47,8 +47,8 @@ public class SkyblockerConfig { /* REGEX Explanation * "Pets" : simple match on letters - * "(?: \\(\\d+\\/\\d+\\))?" : optional match on the non-capturing group for the page in the format " ($number/$number)" - */ + * "(?: \\(\\d+\\/\\d+\\))?" : optional match on the non-capturing group for the page in the format " ($number/$number)" + */ @SerialEntry public QuickNavItem button3 = new QuickNavItem(true, new ItemData("bone"), "Pets(:? \\(\\d+\\/\\d+\\))?", "/pets"); @@ -332,16 +332,16 @@ public class SkyblockerConfig { public boolean enableCommandArgShortcuts = true; } - public static class QuiverWarning { - @SerialEntry - public boolean enableQuiverWarning = true; - - @SerialEntry - public boolean enableQuiverWarningInDungeons = true; - - @SerialEntry - public boolean enableQuiverWarningAfterDungeon = true; - } + public static class QuiverWarning { + @SerialEntry + public boolean enableQuiverWarning = true; + + @SerialEntry + public boolean enableQuiverWarningInDungeons = true; + + @SerialEntry + public boolean enableQuiverWarningAfterDungeon = true; + } public static class Hitbox { @SerialEntry @@ -526,7 +526,10 @@ public class SkyblockerConfig { public boolean solveThreeWeirdos = true; @SerialEntry - public boolean blazesolver = true; + public boolean blazeSolver = true; + + @SerialEntry + public boolean creeperSolver = true; @SerialEntry public boolean solveTrivia = true; diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index ffd979eb..a9c0c26b 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -240,11 +240,19 @@ public class DungeonsCategory { .controller(ConfigUtils::createBooleanController) .build()) .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.blazesolver")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.blazesolver.@Tooltip"))) - .binding(defaults.locations.dungeons.blazesolver, - () -> config.locations.dungeons.blazesolver, - newValue -> config.locations.dungeons.blazesolver = newValue) + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.blazeSolver")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.blazeSolver.@Tooltip"))) + .binding(defaults.locations.dungeons.blazeSolver, + () -> config.locations.dungeons.blazeSolver, + newValue -> config.locations.dungeons.blazeSolver = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.creeperSolver")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.creeperSolver.@Tooltip"))) + .binding(defaults.locations.dungeons.creeperSolver, + () -> config.locations.dungeons.creeperSolver, + newValue -> config.locations.dungeons.creeperSolver = newValue) .controller(ConfigUtils::createBooleanController) .build()) .option(Option.<Boolean>createBuilder() @@ -262,7 +270,7 @@ public class DungeonsCategory { newValue -> config.locations.dungeons.solveTicTacToe = newValue) .controller(ConfigUtils::createBooleanController) .build()) - + //Livid Color .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor")) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CreeperBeams.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CreeperBeams.java new file mode 100644 index 00000000..5356658e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CreeperBeams.java @@ -0,0 +1,251 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import it.unimi.dsi.fastutil.objects.ObjectDoublePair; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.mob.CreeperEntity; +import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.util.DyeColor; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Vec3d; +import org.joml.Intersectiond; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class CreeperBeams { + + private static final Logger LOGGER = LoggerFactory.getLogger(CreeperBeams.class.getName()); + + // "missing, this palette looks like you stole it from a 2018 bootstrap webapp!" + private static final float[][] COLORS = { + DyeColor.LIGHT_BLUE.getColorComponents(), + DyeColor.PINK.getColorComponents(), + DyeColor.ORANGE.getColorComponents(), + DyeColor.MAGENTA.getColorComponents(), + }; + private static final float[] LIME_COLOR_COMPONENTS = DyeColor.LIME.getColorComponents(); + + private static final int FLOOR_Y = 68; + private static final int BASE_Y = 74; + + private static ArrayList<Beam> beams = new ArrayList<>(); + private static BlockPos base = null; + private static boolean solved = false; + + public static void init() { + Scheduler.INSTANCE.scheduleCyclic(CreeperBeams::update, 20); + WorldRenderEvents.BEFORE_DEBUG_RENDER.register(CreeperBeams::render); + ClientPlayConnectionEvents.JOIN.register(((handler, sender, client) -> reset())); + } + + private static void reset() { + beams.clear(); + base = null; + solved = false; + } + + private static void update() { + + // don't do anything if the room is solved + if (solved) { + return; + } + + MinecraftClient client = MinecraftClient.getInstance(); + ClientWorld world = client.world; + ClientPlayerEntity player = client.player; + + // clear state if not in dungeon + if (world == null || player == null || !Utils.isInDungeons()) { + return; + } + + // try to find base if not found and solve + if (base == null) { + base = findCreeperBase(player, world); + if (base == null) { + return; + } + Vec3d creeperPos = new Vec3d(base.getX() + 0.5, BASE_Y + 3.5, base.getZ() + 0.5); + ArrayList<BlockPos> targets = findTargets(world, base); + beams = findLines(creeperPos, targets); + } + + // update the beam states + beams.forEach(b -> b.updateState(world)); + + // check if the room is solved + if (!isTarget(world, base)) { + solved = true; + } + } + + // find the sea lantern block beneath the creeper + private static BlockPos findCreeperBase(ClientPlayerEntity player, ClientWorld world) { + + // find all creepers + List<CreeperEntity> creepers = world.getEntitiesByClass( + CreeperEntity.class, + player.getBoundingBox().expand(50D), + EntityPredicates.VALID_ENTITY); + + if (creepers.isEmpty()) { + return null; + } + + // (sanity) check: + // if the creeper isn't above a sea lantern, it's not the target. + for (CreeperEntity ce : creepers) { + Vec3d creeperPos = ce.getPos(); + BlockPos potentialBase = BlockPos.ofFloored(creeperPos.x, BASE_Y, creeperPos.z); + if (isTarget(world, potentialBase)) { + return potentialBase; + } + } + + return null; + + } + + // find the sea lanterns (and the ONE prismarine ty hypixel) in the room + private static ArrayList<BlockPos> findTargets(ClientWorld world, BlockPos basePos) { + ArrayList<BlockPos> targets = new ArrayList<>(); + + BlockPos start = new BlockPos(basePos.getX() - 15, BASE_Y + 12, basePos.getZ() - 15); + BlockPos end = new BlockPos(basePos.getX() + 16, FLOOR_Y, basePos.getZ() + 16); + + for (BlockPos pos : BlockPos.iterate(start, end)) { + if (isTarget(world, pos)) { + targets.add(new BlockPos(pos)); + } + } + return targets; + } + + // generate lines between targets and finally find the solution + private static ArrayList<Beam> findLines(Vec3d creeperPos, ArrayList<BlockPos> targets) { + + ArrayList<ObjectDoublePair<Beam>> allLines = new ArrayList<>(); + + // optimize this a little bit by + // only generating lines "one way", i.e. 1 -> 2 but not 2 -> 1 + for (int i = 0; i < targets.size(); i++) { + for (int j = i + 1; j < targets.size(); j++) { + Beam beam = new Beam(targets.get(i), targets.get(j)); + double dist = Intersectiond.distancePointLine( + creeperPos.x, creeperPos.y, creeperPos.z, + beam.line[0].x, beam.line[0].y, beam.line[0].z, + beam.line[1].x, beam.line[1].y, beam.line[1].z); + allLines.add(ObjectDoublePair.of(beam, dist)); + } + } + + // this feels a bit heavy-handed, but it works for now. + + ArrayList<Beam> result = new ArrayList<>(); + allLines.sort(Comparator.comparingDouble(ObjectDoublePair::rightDouble)); + + while (result.size() < 4 && !allLines.isEmpty()) { + Beam solution = allLines.get(0).left(); + result.add(solution); + + // remove the line we just added and other lines that use blocks we're using for + // that line + allLines.remove(0); + allLines.removeIf(beam -> solution.containsComponentOf(beam.left())); + } + + if (result.size() != 4) { + LOGGER.error("Not enough solutions found. This is bad..."); + } + + return result; + } + + private static void render(WorldRenderContext wrc) { + + // don't render if solved or disabled + if (solved || !SkyblockerConfigManager.get().locations.dungeons.creeperSolver) { + return; + } + + // lines.size() is always <= 4 so no issues OOB issues with the colors here. + for (int i = 0; i < beams.size(); i++) { + beams.get(i).render(wrc, COLORS[i]); + } + } + + private static boolean isTarget(ClientWorld world, BlockPos pos) { + Block block = world.getBlockState(pos).getBlock(); + return block == Blocks.SEA_LANTERN || block == Blocks.PRISMARINE; + } + + // helper class to hold all the things needed to render a beam + private static class Beam { + + // raw block pos of target + public BlockPos blockOne; + public BlockPos blockTwo; + + // middle of targets used for rendering the line + public Vec3d[] line = new Vec3d[2]; + + // boxes used for rendering the block outline + public Box outlineOne; + public Box outlineTwo; + + // state: is this beam created/inputted or not? + private boolean toDo = true; + + public Beam(BlockPos a, BlockPos b) { + blockOne = a; + blockTwo = b; + line[0] = new Vec3d(a.getX() + 0.5, a.getY() + 0.5, a.getZ() + 0.5); + line[1] = new Vec3d(b.getX() + 0.5, b.getY() + 0.5, b.getZ() + 0.5); + outlineOne = new Box(a); + outlineTwo = new Box(b); + } + + // used to filter the list of all beams so that no two beams share a target + public boolean containsComponentOf(Beam other) { + return this.blockOne.equals(other.blockOne) + || this.blockOne.equals(other.blockTwo) + || this.blockTwo.equals(other.blockOne) + || this.blockTwo.equals(other.blockTwo); + } + + // update the state: is the beam created or not? + public void updateState(ClientWorld world) { + toDo = !(world.getBlockState(blockOne).getBlock() == Blocks.PRISMARINE + && world.getBlockState(blockTwo).getBlock() == Blocks.PRISMARINE); + } + + // render either in a color if not created or faintly green if created + public void render(WorldRenderContext wrc, float[] color) { + if (toDo) { + RenderHelper.renderOutline(wrc, outlineOne, color, 3); + RenderHelper.renderOutline(wrc, outlineTwo, color, 3); + RenderHelper.renderLinesFromPoints(wrc, line, color, 1, 2); + } else { + RenderHelper.renderOutline(wrc, outlineOne, LIME_COLOR_COMPONENTS, 1); + RenderHelper.renderOutline(wrc, outlineTwo, LIME_COLOR_COMPONENTS, 1); + RenderHelper.renderLinesFromPoints(wrc, line, LIME_COLOR_COMPONENTS, 0.75f, 1); + } + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java index 044949db..cfb16b4d 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java @@ -106,7 +106,7 @@ public class DungeonBlaze { */ public static void blazeRenderer(WorldRenderContext wrc) { try { - if (highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive() && SkyblockerConfigManager.get().locations.dungeons.blazesolver) { + if (highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive() && SkyblockerConfigManager.get().locations.dungeons.blazeSolver) { if (highestBlaze.getY() < 69) { renderBlazeOutline(highestBlaze, nextHighestBlaze, wrc); } |