From c46600b5412634092db8b10cc0f1e17b871a9f6c Mon Sep 17 00:00:00 2001 From: onion Date: Sun, 25 Jan 2026 21:43:34 -0500 Subject: [PATCH 1/9] moved the toggle icons to the left of the seedmap (previously they were above it) --- build.gradle | 22 +++-- buildSrc/build.gradle | 11 --- .../buildscript/CreateJavaBindingsTask.java | 18 ---- .../buildscript/GenerateBuildInfoTask.java | 61 ------------- gradle.properties | 2 +- .../command/CustomClientCommandSource.java | 1 + .../command/arguments/BiomeArgument.java | 1 + .../command/arguments/BlockArgument.java | 1 + .../arguments/CanyonCarverArgument.java | 1 + .../command/arguments/CaveCarverArgument.java | 1 + .../arguments/DensityFunctionArgument.java | 1 + .../command/arguments/DimensionArgument.java | 1 + .../arguments/GeneratorFlagArgument.java | 1 + .../ItemAndEnchantmentsPredicateArgument.java | 1 + .../arguments/StructurePredicateArgument.java | 1 + .../command/arguments/VersionArgument.java | 1 + .../command/commands/HighlightCommand.java | 1 + .../command/commands/LocateCommand.java | 1 + .../command/commands/SampleCommand.java | 1 + .../seedmapper/feature/StructureChecks.java | 7 +- .../StructureVariantFeedbackHelper.java | 5 +- .../seedmapper/seedmap/ChestLootWidget.java | 1 + .../seedmap/FeatureToggleWidget.java | 6 +- .../xpple/seedmapper/seedmap/MapFeature.java | 7 +- .../seedmapper/seedmap/SeedMapScreen.java | 89 ++++++++++++++----- .../xpple/seedmapper/util/ComponentUtils.java | 3 +- 26 files changed, 109 insertions(+), 137 deletions(-) delete mode 100644 buildSrc/build.gradle delete mode 100644 buildSrc/src/main/java/dev/xpple/seedmapper/buildscript/CreateJavaBindingsTask.java delete mode 100644 buildSrc/src/main/java/dev/xpple/seedmapper/buildscript/GenerateBuildInfoTask.java diff --git a/build.gradle b/build.gradle index 087d56fc..fe2af67a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,3 @@ -import dev.xpple.seedmapper.buildscript.CreateJavaBindingsTask -import dev.xpple.seedmapper.buildscript.GenerateBuildInfoTask import net.fabricmc.loom.task.prod.ClientProductionRunTask plugins { @@ -69,19 +67,19 @@ dependencies { } clean { - delete fileTree("${projectDir}/src/main/java/com/github/cubiomes") { - exclude '.gitkeep' - } + //delete fileTree("${projectDir}/src/main/java/com/github/cubiomes") { + // exclude '.gitkeep' + //} } -tasks.register('createJavaBindings', CreateJavaBindingsTask) {} -compileJava.dependsOn('createJavaBindings') +//tasks.register('createJavaBindings', CreateJavaBindingsTask) {} +// compileJava.dependsOn('createJavaBindings') -createJavaBindings.dependsOn(clean) +//createJavaBindings.dependsOn(clean) -tasks.register('generateBuildInfo', GenerateBuildInfoTask) { - outputFile = new File(temporaryDir, "build_info.json") -} +//tasks.register('generateBuildInfo', GenerateBuildInfoTask) { +// outputFile = new File(temporaryDir, "build_info.json") +//} tasks.register("prodClient", ClientProductionRunTask) { runDir = file("run") @@ -112,7 +110,7 @@ processResources { from("src/main/c/cubiomes/loot/LICENSE_loot_library.h.txt") - from generateBuildInfo + //from generateBuildInfo } tasks.withType(JavaCompile).configureEach { diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle deleted file mode 100644 index d75f7f2f..00000000 --- a/buildSrc/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -plugins { - id 'java' -} - -repositories { - mavenCentral() -} - -dependencies { - implementation("com.google.code.gson:gson:2.11.0") -} diff --git a/buildSrc/src/main/java/dev/xpple/seedmapper/buildscript/CreateJavaBindingsTask.java b/buildSrc/src/main/java/dev/xpple/seedmapper/buildscript/CreateJavaBindingsTask.java deleted file mode 100644 index f347b7f2..00000000 --- a/buildSrc/src/main/java/dev/xpple/seedmapper/buildscript/CreateJavaBindingsTask.java +++ /dev/null @@ -1,18 +0,0 @@ -package dev.xpple.seedmapper.buildscript; - -import org.apache.tools.ant.taskdefs.condition.Os; -import org.gradle.api.tasks.Exec; - -public abstract class CreateJavaBindingsTask extends Exec { - - private static final String EXTENSION = Os.isFamily(Os.FAMILY_WINDOWS) ? ".bat" : ""; - - { - // always run task - this.getOutputs().upToDateWhen(_ -> false); - - this.setWorkingDir(this.getProject().getRootDir()); - this.setStandardOutput(System.out); - this.commandLine("./jextract/build/jextract/bin/jextract" + EXTENSION, "--include-dir", "src/main/c/cubiomes", "--output", "src/main/java", "--use-system-load-library", "--target-package", "com.github.cubiomes", "--header-class-name", "Cubiomes", "@includes.txt", "biomenoise.h", "biomes.h", "finders.h", "generator.h", "layers.h", "biomenoise.h", "biomes.h", "noise.h", "terrainnoise.h", "rng.h", "util.h", "quadbase.h", "xrms.h", "loot/items.h", "loot/logging.h", "loot/loot_functions.h", "loot/loot_table_context.h", "loot/loot_table_parser.h", "loot/loot_tables.h", "loot/mc_loot.h"); - } -} diff --git a/buildSrc/src/main/java/dev/xpple/seedmapper/buildscript/GenerateBuildInfoTask.java b/buildSrc/src/main/java/dev/xpple/seedmapper/buildscript/GenerateBuildInfoTask.java deleted file mode 100644 index c391d365..00000000 --- a/buildSrc/src/main/java/dev/xpple/seedmapper/buildscript/GenerateBuildInfoTask.java +++ /dev/null @@ -1,61 +0,0 @@ -package dev.xpple.seedmapper.buildscript; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import org.gradle.api.DefaultTask; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.tasks.OutputFile; -import org.gradle.api.tasks.TaskAction; -import org.gradle.process.ExecOperations; - -import javax.inject.Inject; -import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -public abstract class GenerateBuildInfoTask extends DefaultTask { - - private static final Gson GSON = new Gson(); - - @OutputFile - public abstract RegularFileProperty getOutputFile(); - - @Inject - protected abstract ExecOperations getExecOperations(); - - { - // never reuse previous outputs - this.getOutputs().upToDateWhen(_ -> false); - } - - @TaskAction - protected void run() { - String version = this.getProject().getVersion().toString(); - String branch = this.executeCommand("git", "branch", "--show-current"); - String shortCommitHash = this.executeCommand("git", "rev-parse", "--short", "HEAD"); - String commitHash = this.executeCommand("git", "rev-parse", "HEAD"); - - JsonObject object = new JsonObject(); - object.addProperty("version", version); - object.addProperty("branch", branch); - object.addProperty("shortCommitHash", shortCommitHash); - object.addProperty("commitHash", commitHash); - - try (BufferedWriter writer = Files.newBufferedWriter(this.getOutputFile().getAsFile().get().toPath())) { - GSON.toJson(object, writer); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private String executeCommand(Object... args) { - ByteArrayOutputStream outputBytes = new ByteArrayOutputStream(); - this.getExecOperations().exec(execSpec -> { - execSpec.setStandardOutput(outputBytes); - execSpec.commandLine(args); - }).rethrowFailure(); - return outputBytes.toString(StandardCharsets.UTF_8).trim(); - } -} diff --git a/gradle.properties b/gradle.properties index 21feb5ac..e8cacd1e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ minecraft_version_dependency=>=1.21.11 <1.22 fabric_api_version=0.139.4+1.21.11 parchment_mappings=parchment-1.21.9:2025.10.05@zip fabric_loader_version=0.18.2 -fabric_loom_version=1.14-SNAPSHOT +fabric_loom_version=1.15-SNAPSHOT # Library dependencies betterconfig_version=2.5.0 diff --git a/src/main/java/dev/xpple/seedmapper/command/CustomClientCommandSource.java b/src/main/java/dev/xpple/seedmapper/command/CustomClientCommandSource.java index 92dcaced..20efb16a 100644 --- a/src/main/java/dev/xpple/seedmapper/command/CustomClientCommandSource.java +++ b/src/main/java/dev/xpple/seedmapper/command/CustomClientCommandSource.java @@ -4,6 +4,7 @@ import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.datafixers.util.Pair; + import dev.xpple.seedmapper.command.arguments.DimensionArgument; import dev.xpple.seedmapper.command.arguments.SeedResolutionArgument; import dev.xpple.seedmapper.command.arguments.VersionArgument; diff --git a/src/main/java/dev/xpple/seedmapper/command/arguments/BiomeArgument.java b/src/main/java/dev/xpple/seedmapper/command/arguments/BiomeArgument.java index 1f94b0a6..cdf8b63d 100644 --- a/src/main/java/dev/xpple/seedmapper/command/arguments/BiomeArgument.java +++ b/src/main/java/dev/xpple/seedmapper/command/arguments/BiomeArgument.java @@ -8,6 +8,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; + import dev.xpple.seedmapper.command.CommandExceptions; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.commands.SharedSuggestionProvider; diff --git a/src/main/java/dev/xpple/seedmapper/command/arguments/BlockArgument.java b/src/main/java/dev/xpple/seedmapper/command/arguments/BlockArgument.java index 962e3fdb..b3ee8c1f 100644 --- a/src/main/java/dev/xpple/seedmapper/command/arguments/BlockArgument.java +++ b/src/main/java/dev/xpple/seedmapper/command/arguments/BlockArgument.java @@ -9,6 +9,7 @@ import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.datafixers.util.Pair; + import dev.xpple.seedmapper.command.CommandExceptions; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.commands.SharedSuggestionProvider; diff --git a/src/main/java/dev/xpple/seedmapper/command/arguments/CanyonCarverArgument.java b/src/main/java/dev/xpple/seedmapper/command/arguments/CanyonCarverArgument.java index b4a9cf0d..b2f3cc9b 100644 --- a/src/main/java/dev/xpple/seedmapper/command/arguments/CanyonCarverArgument.java +++ b/src/main/java/dev/xpple/seedmapper/command/arguments/CanyonCarverArgument.java @@ -8,6 +8,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; + import dev.xpple.seedmapper.command.CommandExceptions; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.commands.SharedSuggestionProvider; diff --git a/src/main/java/dev/xpple/seedmapper/command/arguments/CaveCarverArgument.java b/src/main/java/dev/xpple/seedmapper/command/arguments/CaveCarverArgument.java index f909d7a1..966762a5 100644 --- a/src/main/java/dev/xpple/seedmapper/command/arguments/CaveCarverArgument.java +++ b/src/main/java/dev/xpple/seedmapper/command/arguments/CaveCarverArgument.java @@ -8,6 +8,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; + import dev.xpple.seedmapper.command.CommandExceptions; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.commands.SharedSuggestionProvider; diff --git a/src/main/java/dev/xpple/seedmapper/command/arguments/DensityFunctionArgument.java b/src/main/java/dev/xpple/seedmapper/command/arguments/DensityFunctionArgument.java index 5870c748..3e72ef67 100644 --- a/src/main/java/dev/xpple/seedmapper/command/arguments/DensityFunctionArgument.java +++ b/src/main/java/dev/xpple/seedmapper/command/arguments/DensityFunctionArgument.java @@ -12,6 +12,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; + import dev.xpple.seedmapper.command.CommandExceptions; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.commands.SharedSuggestionProvider; diff --git a/src/main/java/dev/xpple/seedmapper/command/arguments/DimensionArgument.java b/src/main/java/dev/xpple/seedmapper/command/arguments/DimensionArgument.java index f2125b28..df2267e4 100644 --- a/src/main/java/dev/xpple/seedmapper/command/arguments/DimensionArgument.java +++ b/src/main/java/dev/xpple/seedmapper/command/arguments/DimensionArgument.java @@ -9,6 +9,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; + import dev.xpple.seedmapper.command.CommandExceptions; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.commands.SharedSuggestionProvider; diff --git a/src/main/java/dev/xpple/seedmapper/command/arguments/GeneratorFlagArgument.java b/src/main/java/dev/xpple/seedmapper/command/arguments/GeneratorFlagArgument.java index 2efdaae8..53ec4cf8 100644 --- a/src/main/java/dev/xpple/seedmapper/command/arguments/GeneratorFlagArgument.java +++ b/src/main/java/dev/xpple/seedmapper/command/arguments/GeneratorFlagArgument.java @@ -8,6 +8,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; + import dev.xpple.seedmapper.command.CommandExceptions; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.commands.SharedSuggestionProvider; diff --git a/src/main/java/dev/xpple/seedmapper/command/arguments/ItemAndEnchantmentsPredicateArgument.java b/src/main/java/dev/xpple/seedmapper/command/arguments/ItemAndEnchantmentsPredicateArgument.java index 96ad79f8..1a780d74 100644 --- a/src/main/java/dev/xpple/seedmapper/command/arguments/ItemAndEnchantmentsPredicateArgument.java +++ b/src/main/java/dev/xpple/seedmapper/command/arguments/ItemAndEnchantmentsPredicateArgument.java @@ -12,6 +12,7 @@ import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; + import dev.xpple.seedmapper.command.CommandExceptions; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.commands.SharedSuggestionProvider; diff --git a/src/main/java/dev/xpple/seedmapper/command/arguments/StructurePredicateArgument.java b/src/main/java/dev/xpple/seedmapper/command/arguments/StructurePredicateArgument.java index 5e847249..0fd49a80 100644 --- a/src/main/java/dev/xpple/seedmapper/command/arguments/StructurePredicateArgument.java +++ b/src/main/java/dev/xpple/seedmapper/command/arguments/StructurePredicateArgument.java @@ -11,6 +11,7 @@ import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.datafixers.util.Pair; + import dev.xpple.seedmapper.command.CommandExceptions; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.commands.SharedSuggestionProvider; diff --git a/src/main/java/dev/xpple/seedmapper/command/arguments/VersionArgument.java b/src/main/java/dev/xpple/seedmapper/command/arguments/VersionArgument.java index f9ebb358..b384ee7d 100644 --- a/src/main/java/dev/xpple/seedmapper/command/arguments/VersionArgument.java +++ b/src/main/java/dev/xpple/seedmapper/command/arguments/VersionArgument.java @@ -8,6 +8,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; + import dev.xpple.seedmapper.command.CommandExceptions; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.commands.SharedSuggestionProvider; diff --git a/src/main/java/dev/xpple/seedmapper/command/commands/HighlightCommand.java b/src/main/java/dev/xpple/seedmapper/command/commands/HighlightCommand.java index ab90203e..cd4e3623 100644 --- a/src/main/java/dev/xpple/seedmapper/command/commands/HighlightCommand.java +++ b/src/main/java/dev/xpple/seedmapper/command/commands/HighlightCommand.java @@ -13,6 +13,7 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.datafixers.util.Pair; + import dev.xpple.seedmapper.SeedMapper; import dev.xpple.seedmapper.command.CommandExceptions; import dev.xpple.seedmapper.command.CustomClientCommandSource; diff --git a/src/main/java/dev/xpple/seedmapper/command/commands/LocateCommand.java b/src/main/java/dev/xpple/seedmapper/command/commands/LocateCommand.java index e0bdbcba..dd619118 100644 --- a/src/main/java/dev/xpple/seedmapper/command/commands/LocateCommand.java +++ b/src/main/java/dev/xpple/seedmapper/command/commands/LocateCommand.java @@ -17,6 +17,7 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; + import dev.xpple.seedmapper.command.CommandExceptions; import dev.xpple.seedmapper.command.CustomClientCommandSource; import dev.xpple.seedmapper.command.arguments.CanyonCarverArgument; diff --git a/src/main/java/dev/xpple/seedmapper/command/commands/SampleCommand.java b/src/main/java/dev/xpple/seedmapper/command/commands/SampleCommand.java index 5d509569..689fabd9 100644 --- a/src/main/java/dev/xpple/seedmapper/command/commands/SampleCommand.java +++ b/src/main/java/dev/xpple/seedmapper/command/commands/SampleCommand.java @@ -4,6 +4,7 @@ import com.github.cubiomes.TerrainNoise; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; + import dev.xpple.seedmapper.command.CommandExceptions; import dev.xpple.seedmapper.command.CustomClientCommandSource; import dev.xpple.seedmapper.config.Configs; diff --git a/src/main/java/dev/xpple/seedmapper/feature/StructureChecks.java b/src/main/java/dev/xpple/seedmapper/feature/StructureChecks.java index 4e7f2880..ca7297e0 100644 --- a/src/main/java/dev/xpple/seedmapper/feature/StructureChecks.java +++ b/src/main/java/dev/xpple/seedmapper/feature/StructureChecks.java @@ -1,8 +1,5 @@ package dev.xpple.seedmapper.feature; -import com.github.cubiomes.Cubiomes; -import com.github.cubiomes.Generator; -import com.github.cubiomes.Pos; import dev.xpple.seedmapper.command.arguments.StructurePredicateArgument; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; @@ -11,6 +8,10 @@ import java.lang.foreign.MemorySegment; import java.util.function.IntFunction; +import com.github.cubiomes.Cubiomes; +import com.github.cubiomes.Generator; +import com.github.cubiomes.Pos; + public final class StructureChecks { private StructureChecks() { diff --git a/src/main/java/dev/xpple/seedmapper/feature/StructureVariantFeedbackHelper.java b/src/main/java/dev/xpple/seedmapper/feature/StructureVariantFeedbackHelper.java index 293f4134..ffee5c4d 100644 --- a/src/main/java/dev/xpple/seedmapper/feature/StructureVariantFeedbackHelper.java +++ b/src/main/java/dev/xpple/seedmapper/feature/StructureVariantFeedbackHelper.java @@ -1,7 +1,5 @@ package dev.xpple.seedmapper.feature; -import com.github.cubiomes.Cubiomes; -import com.github.cubiomes.StructureVariant; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -13,6 +11,9 @@ import java.util.List; import java.util.function.Function; +import com.github.cubiomes.Cubiomes; +import com.github.cubiomes.StructureVariant; + import static dev.xpple.seedmapper.util.ChatBuilder.*; public final class StructureVariantFeedbackHelper { diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/ChestLootWidget.java b/src/main/java/dev/xpple/seedmapper/seedmap/ChestLootWidget.java index 30098076..4199879d 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/ChestLootWidget.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/ChestLootWidget.java @@ -2,6 +2,7 @@ import com.github.cubiomes.Cubiomes; import com.mojang.blaze3d.platform.InputConstants; + import dev.xpple.seedmapper.SeedMapper; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/FeatureToggleWidget.java b/src/main/java/dev/xpple/seedmapper/seedmap/FeatureToggleWidget.java index 91b0509d..b0570021 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/FeatureToggleWidget.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/FeatureToggleWidget.java @@ -11,8 +11,8 @@ public class FeatureToggleWidget extends Button { private final MapFeature feature; - public FeatureToggleWidget(MapFeature feature, int x, int y) { - super(x, y, feature.getDefaultTexture().width(), feature.getDefaultTexture().height(), Component.literal(feature.getName()), FeatureToggleWidget::onButtonPress, DEFAULT_NARRATION); + public FeatureToggleWidget(MapFeature feature, int x, int y, double scale) { + super(x, y, (int)Math.floor(scale * feature.getDefaultTexture().width()), (int)Math.floor(scale * feature.getDefaultTexture().height()), Component.literal(feature.getName()), FeatureToggleWidget::onButtonPress, DEFAULT_NARRATION); this.feature = feature; this.setTooltip(Tooltip.create(Component.literal(this.feature.getName()))); } @@ -23,7 +23,7 @@ protected void renderContents(GuiGraphics guiGraphics, int mouseX, int mouseY, f if (!Configs.ToggledFeatures.contains(this.feature)) { colour = ARGB.color(255 >> 1, 255, 255, 255); } - SeedMapScreen.FeatureWidget.drawFeatureIcon(guiGraphics, this.feature.getDefaultTexture(), this.getX(), this.getY(), colour); + SeedMapScreen.drawIconStatic(guiGraphics, this.feature.getDefaultTexture().identifier(), this.getX(), this.getY(), this.getWidth(), this.getHeight(), colour); } private static void onButtonPress(Button button) { diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/MapFeature.java b/src/main/java/dev/xpple/seedmapper/seedmap/MapFeature.java index 8af9eecd..d9d2c589 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/MapFeature.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/MapFeature.java @@ -1,8 +1,5 @@ package dev.xpple.seedmapper.seedmap; -import com.github.cubiomes.Cubiomes; -import com.github.cubiomes.Piece; -import com.github.cubiomes.StructureVariant; import dev.xpple.seedmapper.SeedMapper; import dev.xpple.seedmapper.feature.StructureChecks; import dev.xpple.seedmapper.util.WorldIdentifier; @@ -15,6 +12,10 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import com.github.cubiomes.Cubiomes; +import com.github.cubiomes.Piece; +import com.github.cubiomes.StructureVariant; + public enum MapFeature { DESERT_PYRAMID("desert_pyramid", Cubiomes.Desert_Pyramid(), Cubiomes.DIM_OVERWORLD(), Cubiomes.MC_1_3(), "cubiomes_viewer_icons", 19, 20), JUNGLE_PYRAMID("jungle_pyramid", Cubiomes.Jungle_Pyramid(), Cubiomes.DIM_OVERWORLD(), Cubiomes.MC_1_3(), "cubiomes_viewer_icons", 19, 20), diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java index 4c884054..aa74158c 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java @@ -18,6 +18,8 @@ import com.google.common.collect.ImmutableBiMap; import com.mojang.blaze3d.platform.InputConstants; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.logging.LogUtils; + import dev.xpple.seedmapper.SeedMapper; import dev.xpple.seedmapper.command.arguments.CanyonCarverArgument; import dev.xpple.seedmapper.command.arguments.ItemAndEnchantmentsPredicateArgument; @@ -34,6 +36,7 @@ import dev.xpple.seedmapper.util.WorldIdentifier; import dev.xpple.simplewaypoints.api.SimpleWaypointsAPI; import dev.xpple.simplewaypoints.api.Waypoint; +import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.ints.AbstractIntCollection; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.IntArraySet; @@ -84,6 +87,7 @@ import org.jetbrains.annotations.Nullable; import org.joml.Matrix3x2f; import org.joml.Vector2f; +import org.slf4j.Logger; import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; @@ -151,15 +155,16 @@ public class SeedMapScreen extends Screen { public static final int MIN_PIXELS_PER_BIOME = 1; public static final int MAX_PIXELS_PER_BIOME = 100; - private static final int HORIZONTAL_FEATURE_TOGGLE_SPACING = 5; + private static final int HORIZONTAL_FEATURE_TOGGLE_SPACING = 1; private static final int VERTICAL_FEATURE_TOGGLE_SPACING = 1; - private static final int FEATURE_TOGGLE_HEIGHT = 20; private static final int TELEPORT_FIELD_WIDTH = 70; private static final int WAYPOINT_NAME_FIELD_WIDTH = 100; private static final IntSupplier TILE_SIZE_PIXELS = () -> TilePos.TILE_SIZE_CHUNKS * SCALED_CHUNK_SIZE * Configs.PixelsPerBiome; + private static final double FEATURE_TOGGLE_LOWER_PADDING_FACTOR = 0.95; + private static final Object2ObjectMap> biomeDataCache = new Object2ObjectOpenHashMap<>(); private static final Object2ObjectMap> structureDataCache = new Object2ObjectOpenHashMap<>(); public static final Object2ObjectMap strongholdDataCache = new Object2ObjectOpenHashMap<>(); @@ -210,8 +215,10 @@ public class SeedMapScreen extends Screen { private int seedMapWidth; private int seedMapHeight; + // A list of all features that can have their appearance on the map toggled on or off. private final List toggleableFeatures; - private final int featureIconsCombinedWidth; + // The combined height of the images of *all* toggleable features. Includes padding. + private final int featureIconsCombinedHeight; private final ObjectSet featureWidgets = new ObjectOpenHashSet<>(); @@ -297,9 +304,9 @@ public SeedMapScreen(long seed, int dimension, int version, int generatorFlags, }); } - this.featureIconsCombinedWidth = this.toggleableFeatures.stream() - .map(feature -> feature.getDefaultTexture().width()) - .reduce((l, r) -> l + HORIZONTAL_FEATURE_TOGGLE_SPACING + r) + this.featureIconsCombinedHeight = this.toggleableFeatures.stream() + .map(feature -> feature.getDefaultTexture().height()) + .reduce((l, r) -> l + VERTICAL_FEATURE_TOGGLE_SPACING + r) .orElseThrow(); this.playerPos = playerPos; @@ -652,27 +659,65 @@ protected void drawDirectionArrow(GuiGraphics guiGraphics, int playerMinX, int p guiGraphics.pose().popMatrix(); } + // Formerly, this laid the feature toggles out in rows above the map. + // Now they are laid out in columns besides the map. private void createFeatureToggles() { // TODO: replace with Gatherers API? // TODO: only calculate on resize? - int rows = Math.ceilDiv(this.featureIconsCombinedWidth, this.seedMapWidth); - int togglesPerRow = Math.ceilDiv(this.toggleableFeatures.size(), rows); - int toggleMinY = 1; - for (int row = 0; row < rows - 1; row++) { - this.createFeatureTogglesInner(row, togglesPerRow, togglesPerRow, this.horizontalPadding(), toggleMinY); - toggleMinY += FEATURE_TOGGLE_HEIGHT + VERTICAL_FEATURE_TOGGLE_SPACING; - } - int togglesInLastRow = this.toggleableFeatures.size() - togglesPerRow * (rows - 1); - this.createFeatureTogglesInner(rows - 1, togglesPerRow, togglesInLastRow, this.horizontalPadding(), toggleMinY); + + Pair columnCountAndScale = this.computeFeatureToggleScale(); + int columns = columnCountAndScale.left(); + double scale = columnCountAndScale.right(); + + int togglesPerColumn = Math.ceilDiv(this.toggleableFeatures.size(), columns); + + int row = 0; + int iconLeftX = 0, iconTopY = 0; + int maxIconWidth = 0; + for (MapFeature toggleableFeature : this.toggleableFeatures) { + // Draw the icon. + int iconHeight = (int)Math.ceil(scale*toggleableFeature.getDefaultTexture().height()); + this.addRenderableWidget(new FeatureToggleWidget(toggleableFeature, iconLeftX, iconTopY, scale)); + + // Set up the position for where to draw the next icon. + iconTopY += iconHeight + (int)Math.ceil(scale*VERTICAL_FEATURE_TOGGLE_SPACING); + maxIconWidth = Math.max(maxIconWidth, (int)Math.ceil(scale*toggleableFeature.getDefaultTexture().width())); + + ++row; + if (row >= togglesPerColumn) { + // Begin a new column. + row = 0; + iconTopY = 0; + iconLeftX += maxIconWidth + Math.ceil(scale*HORIZONTAL_FEATURE_TOGGLE_SPACING); + maxIconWidth = 0; + } + } } - private void createFeatureTogglesInner(int row, int togglesPerRow, int maxToggles, int toggleMinX, int toggleMinY) { - for (int toggle = 0; toggle < maxToggles; toggle++) { - MapFeature feature = this.toggleableFeatures.get(row * togglesPerRow + toggle); - MapFeature.Texture featureIcon = feature.getDefaultTexture(); - this.addRenderableWidget(new FeatureToggleWidget(feature, toggleMinX, toggleMinY)); - toggleMinX += featureIcon.width() + HORIZONTAL_FEATURE_TOGGLE_SPACING; + private Pair computeFeatureToggleScale() { + int baseColumnWidth = 0; + for (MapFeature toggleableFeature : this.toggleableFeatures) { + baseColumnWidth = Math.max(baseColumnWidth, toggleableFeature.getDefaultTexture().width()); } + + int n = this.toggleableFeatures.size(); + Pair bestResult = null; + for (int columns = 1; columns <= n; ++columns) { + // Approximate the maximum column height. This doesn't work if the textures have very different heights. + /// TODO: enforce textures being squares of the same size. + int maxColumnHeight = this.featureIconsCombinedHeight / columns; + + double scaleX = (double)HORIZONTAL_PADDING / (columns * (baseColumnWidth + HORIZONTAL_FEATURE_TOGGLE_SPACING)); + double scaleY = (double)this.height * FEATURE_TOGGLE_LOWER_PADDING_FACTOR / maxColumnHeight; + double scale = Math.min(scaleX, scaleY); + + if (scale <= 0) continue; + + if (bestResult == null || scale > bestResult.right()) + bestResult = Pair.of(columns, scale); + } + + return bestResult; } private int[] calculateBiomeData(TilePos tilePos) { @@ -1225,7 +1270,7 @@ private void drawIcon(GuiGraphics guiGraphics, Identifier identifier, int minX, pose.popMatrix(); } - private static void drawIconStatic(GuiGraphics guiGraphics, Identifier identifier, int minX, int minY, int iconWidth, int iconHeight, int colour) { + public static void drawIconStatic(GuiGraphics guiGraphics, Identifier identifier, int minX, int minY, int iconWidth, int iconHeight, int colour) { // Skip intersection checks (GuiRenderState.hasIntersection) you would otherwise get when calling // GuiGraphics.blit as these checks incur a significant performance hit AbstractTexture texture = Minecraft.getInstance().getTextureManager().getTexture(identifier); diff --git a/src/main/java/dev/xpple/seedmapper/util/ComponentUtils.java b/src/main/java/dev/xpple/seedmapper/util/ComponentUtils.java index 3118b66f..dd3d52ae 100644 --- a/src/main/java/dev/xpple/seedmapper/util/ComponentUtils.java +++ b/src/main/java/dev/xpple/seedmapper/util/ComponentUtils.java @@ -1,12 +1,13 @@ package dev.xpple.seedmapper.util; -import com.github.cubiomes.Cubiomes; import dev.xpple.seedmapper.command.arguments.GeneratorFlagArgument; import net.minecraft.core.Vec3i; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import org.jetbrains.annotations.Nullable; +import com.github.cubiomes.Cubiomes; + import java.util.Collection; import static dev.xpple.seedmapper.util.ChatBuilder.*; From 920b8d54418aec9210a8e1fcc0f75d75bf67bfb1 Mon Sep 17 00:00:00 2001 From: onion Date: Sat, 7 Feb 2026 09:03:58 -0500 Subject: [PATCH 2/9] expanded the seed map --- .../seedmapper/seedmap/MinimapScreen.java | 2 +- .../seedmapper/seedmap/SeedMapScreen.java | 175 +++++++++++++----- .../assets/seedmapper/lang/en_us.json | 4 +- 3 files changed, 137 insertions(+), 44 deletions(-) diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java index 7dc4ef3b..55625889 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java @@ -104,7 +104,7 @@ protected int horizontalPadding() { return Configs.MinimapOffsetX; } - @Override + //@Override protected int verticalPadding() { return Configs.MinimapOffsetY; } diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java index aa74158c..749cb011 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java @@ -18,7 +18,6 @@ import com.google.common.collect.ImmutableBiMap; import com.mojang.blaze3d.platform.InputConstants; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.logging.LogUtils; import dev.xpple.seedmapper.SeedMapper; import dev.xpple.seedmapper.command.arguments.CanyonCarverArgument; @@ -28,7 +27,6 @@ import dev.xpple.seedmapper.feature.StructureChecks; import dev.xpple.seedmapper.thread.SeedMapCache; import dev.xpple.seedmapper.thread.SeedMapExecutor; -import dev.xpple.seedmapper.util.ComponentUtils; import dev.xpple.seedmapper.util.QuartPos2; import dev.xpple.seedmapper.util.QuartPos2f; import dev.xpple.seedmapper.util.RegionPos; @@ -55,6 +53,8 @@ import net.minecraft.client.gui.render.TextureSetup; import net.minecraft.client.gui.render.state.BlitRenderState; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; +import net.minecraft.client.gui.screens.inventory.tooltip.DefaultTooltipPositioner; import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.client.player.LocalPlayer; @@ -87,7 +87,6 @@ import org.jetbrains.annotations.Nullable; import org.joml.Matrix3x2f; import org.joml.Vector2f; -import org.slf4j.Logger; import java.lang.foreign.Arena; import java.lang.foreign.MemoryLayout; @@ -150,7 +149,8 @@ public class SeedMapScreen extends Screen { public static final int SCALED_CHUNK_SIZE = LevelChunkSection.SECTION_WIDTH / BIOME_SCALE; private static final int HORIZONTAL_PADDING = 50; - private static final int VERTICAL_PADDING = 50; + //private static final int VERTICAL_PADDING = 0; + private static final int VERTICAL_LOWER_PADDING = 20; public static final int MIN_PIXELS_PER_BIOME = 1; public static final int MAX_PIXELS_PER_BIOME = 100; @@ -158,8 +158,12 @@ public class SeedMapScreen extends Screen { private static final int HORIZONTAL_FEATURE_TOGGLE_SPACING = 1; private static final int VERTICAL_FEATURE_TOGGLE_SPACING = 1; - private static final int TELEPORT_FIELD_WIDTH = 70; - private static final int WAYPOINT_NAME_FIELD_WIDTH = 100; + private static final Identifier SEED_ICON_TEXTURE = Identifier.fromNamespaceAndPath("minecraft", "textures/item/wheat_seeds.png"); + private static final int SEED_ICON_SIZE = 16; + private static final int SEED_ICON_PADDING = 4; + + //private static final int TELEPORT_FIELD_WIDTH = 70; + //private static final int WAYPOINT_NAME_FIELD_WIDTH = 100; private static final IntSupplier TILE_SIZE_PIXELS = () -> TilePos.TILE_SIZE_CHUNKS * SCALED_CHUNK_SIZE * Configs.PixelsPerBiome; @@ -226,6 +230,9 @@ public class SeedMapScreen extends Screen { private int displayCoordinatesCopiedTicks = 0; + private int lastMouseX = 0; + private int lastMouseY = 0; + private EditBox teleportEditBoxX; private EditBox teleportEditBoxZ; @@ -323,8 +330,8 @@ protected void init() { this.centerX = this.width / 2; this.centerY = this.height / 2; - this.seedMapWidth = 2 * (this.centerX - this.horizontalPadding()); - this.seedMapHeight = 2 * (this.centerY - this.verticalPadding()); + this.seedMapWidth = this.width - this.horizontalPadding(); + this.seedMapHeight = this.height - VERTICAL_LOWER_PADDING; if (!this.isMinimap()) { this.createFeatureToggles(); @@ -337,23 +344,37 @@ protected void init() { @Override public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + this.lastMouseX = mouseX; + this.lastMouseY = mouseY; super.render(guiGraphics, mouseX, mouseY, partialTick); // draw title - Component seedComponent = Component.translatable("seedMap.seed", accent(Long.toString(this.seed)), Cubiomes.mc2str(this.version).getString(0), ComponentUtils.formatGeneratorFlags(this.generatorFlags)); - guiGraphics.drawString(this.font, seedComponent, this.horizontalPadding(), this.verticalPadding() - this.font.lineHeight - 1, -1); + //Component seedComponent = Component.translatable("seedMap.seed", accent(Long.toString(this.seed)), Cubiomes.mc2str(this.version).getString(0), ComponentUtils.formatGeneratorFlags(this.generatorFlags)); + //guiGraphics.drawString(this.font, seedComponent, this.horizontalPadding(), this.verticalPadding() - this.font.lineHeight - 1, -1); this.renderBiomes(guiGraphics, mouseX, mouseY, partialTick); guiGraphics.nextStratum(); this.renderFeatures(guiGraphics, mouseX, mouseY, partialTick); - // draw hovered coordinates and biome - MutableComponent coordinates = accent("x: %d, z: %d".formatted(QuartPos.toBlock(this.mouseQuart.x()), QuartPos.toBlock(this.mouseQuart.z()))); - OptionalInt optionalBiome = getBiome(this.mouseQuart); - if (optionalBiome.isPresent()) { - coordinates = coordinates.append(" [%s]".formatted(Cubiomes.biome2str(this.version, optionalBiome.getAsInt()).getString(0))); + // draw hovered coordinates + //MutableComponent coordinates = accent("x: %d, z: %d".formatted(QuartPos.toBlock(this.mouseQuart.x()), QuartPos.toBlock(this.mouseQuart.z()))); + //if (this.displayCoordinatesCopiedTicks > 0) { + // coordinates = Component.translatable("seedMap.coordinatesCopied", coordinates); + //} + //guiGraphics.drawString(this.font, coordinates, this.horizontalPadding(), this.verticalPadding() + this.seedMapHeight + 1, -1); + + if (!this.isMinimap()) { + this.teleportEditBoxX.setHint(Component.literal("X: %d".formatted(QuartPos.toBlock(this.mouseQuart.x())))); + this.teleportEditBoxZ.setHint(Component.literal("Z: %d".formatted(QuartPos.toBlock(this.mouseQuart.z())))); } - if (this.displayCoordinatesCopiedTicks > 0) { - coordinates = Component.translatable("seedMap.coordinatesCopied", coordinates); + + if (this.isMouseOverMap(this.lastMouseX, this.lastMouseY)) { + OptionalInt optionalBiome = getBiome(this.mouseQuart); + if (optionalBiome.isPresent()) { + Component tooltip = Component.literal(Cubiomes.biome2str(this.version, optionalBiome.getAsInt()).getString(0)); + List tooltips = List.of(ClientTooltipComponent.create(tooltip.getVisualOrderText())); + guiGraphics.renderTooltip(this.font, tooltips, this.lastMouseX, this.lastMouseY, DefaultTooltipPositioner.INSTANCE, null); + } } - guiGraphics.drawString(this.font, coordinates, this.horizontalPadding(), this.verticalPadding() + this.seedMapHeight + 1, -1); + + this.drawSeedWidget(guiGraphics); } protected void renderBiomes(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { @@ -542,7 +563,7 @@ private void drawTile(GuiGraphics guiGraphics, Tile tile) { if (maxX < this.horizontalPadding() || minX > this.horizontalPadding() + this.seedMapWidth) { return; } - if (maxY < this.verticalPadding() || minY > this.verticalPadding() + this.seedMapHeight) { + if (maxY < 0 || minY > this.seedMapHeight()) { return; } @@ -555,13 +576,13 @@ private void drawTile(GuiGraphics guiGraphics, Tile tile) { u1 = 1 - ((float) (maxX - this.horizontalPadding() - this.seedMapWidth) / tileSizePixels); maxX = this.horizontalPadding() + this.seedMapWidth; } else u1 = 1; - if (minY < this.verticalPadding()) { - v0 = (float) (this.verticalPadding() - minY) / tileSizePixels; - minY = this.verticalPadding(); + if (minY < 0) { + v0 = (float) (0 - minY) / tileSizePixels; + minY = 0; } else v0 = 0; - if (maxY > this.verticalPadding() + this.seedMapHeight) { - v1 = 1 - ((float) (maxY - this.verticalPadding() - this.seedMapHeight) / tileSizePixels); - maxY = this.verticalPadding() + this.seedMapHeight; + if (maxY > this.seedMapHeight()) { + v1 = 1 - ((float) (maxY - this.seedMapHeight()) / tileSizePixels); + maxY = this.seedMapHeight(); } else v1 = 1; guiGraphics.submitBlit(RenderPipelines.GUI_TEXTURED, tile.texture().getTextureView(), tile.texture().getSampler(), minX, minY, maxX, maxY, u0, u1, v0, v1, 0xFF_FFFFFF); @@ -632,7 +653,7 @@ protected void drawPlayerIndicator(GuiGraphics guiGraphics) { int playerMinY = this.centerY + Mth.floor(Configs.PixelsPerBiome * relPlayerQuart.z()) - 10; int playerMaxX = playerMinX + 20; int playerMaxY = playerMinY + 20; - if (playerMinX < this.horizontalPadding() || playerMaxX > this.horizontalPadding() + this.seedMapWidth || playerMinY < this.verticalPadding() || playerMaxY > this.verticalPadding() + this.seedMapHeight) { + if (playerMinX < this.horizontalPadding() || playerMaxX > this.horizontalPadding() + this.seedMapWidth || playerMinY < 0 || playerMaxY > this.seedMapHeight()) { return; } PlayerFaceRenderer.draw(guiGraphics, this.minecraft.player.getSkin(), playerMinX, playerMinY, 20); @@ -652,13 +673,54 @@ protected void drawDirectionArrow(GuiGraphics guiGraphics, int playerMinX, int p boolean withinBounds = Stream.of(new Vector2f(20, 0), new Vector2f(20, 20), new Vector2f(0, 20), new Vector2f(0, 0)) .map(transform::transformPosition) .allMatch(v -> v.x >= this.horizontalPadding() && v.x <= this.horizontalPadding() + this.seedMapWidth && - v.y >= this.verticalPadding() && v.y <= this.verticalPadding() + this.seedMapHeight); + v.y >= 0 && v.y <= this.seedMapHeight()); if (withinBounds) { drawIconStatic(guiGraphics, DIRECTION_ARROW_TEXTURE, 0, 0, 20, 20, 0xFF_FFFFFF); } guiGraphics.pose().popMatrix(); } + // Draw a seed icon in the bottom-right which shows the seed in a tooltip when hovered. + protected void drawSeedWidget(GuiGraphics guiGraphics) { + int minX = this.getSeedWidgetMinX(); + int minY = this.getSeedWidgetMinY(); + int maxX = minX + SEED_ICON_SIZE; + int maxY = minY + SEED_ICON_SIZE; + + drawIconStatic(guiGraphics, SEED_ICON_TEXTURE, minX, minY, SEED_ICON_SIZE, SEED_ICON_SIZE, 0xFF_FFFFFF); + + if (this.lastMouseX < minX || this.lastMouseX > maxX || this.lastMouseY < minY || this.lastMouseY > maxY) { + return; + } + + Component tooltip = Component.literal(Long.toString(this.seed)); + List tooltips = List.of(ClientTooltipComponent.create(tooltip.getVisualOrderText()), ClientTooltipComponent.create(Component.translatable("seedMap.clickToCopy").getVisualOrderText())); + guiGraphics.renderTooltip(this.font, tooltips, this.lastMouseX, this.lastMouseY, DefaultTooltipPositioner.INSTANCE, null); + } + + private int getSeedWidgetMinX() { + return this.width - SEED_ICON_SIZE - SEED_ICON_PADDING; + } + + private int getSeedWidgetMinY() { + return this.height - SEED_ICON_SIZE - SEED_ICON_PADDING; + } + + private boolean isMouseOverMap(double mouseX, double mouseY) { + return mouseX >= this.horizontalPadding() + && mouseX <= this.horizontalPadding() + this.seedMapWidth + && mouseY >= 0 + && mouseY <= this.seedMapHeight(); + } + + private boolean isMouseOverSeedWidget(double mouseX, double mouseY) { + int minX = this.getSeedWidgetMinX(); + int minY = this.getSeedWidgetMinY(); + int maxX = minX + SEED_ICON_SIZE; + int maxY = minY + SEED_ICON_SIZE; + return mouseX >= minX && mouseX <= maxX && mouseY >= minY && mouseY <= maxY; + } + // Formerly, this laid the feature toggles out in rows above the map. // Now they are laid out in columns besides the map. private void createFeatureToggles() { @@ -856,18 +918,18 @@ private BlockPos calculateSpawnData() { } private void createTeleportField() { - this.teleportEditBoxX = new EditBox(this.font, this.width / 2 - TELEPORT_FIELD_WIDTH, this.verticalPadding() + this.seedMapHeight + 1, TELEPORT_FIELD_WIDTH, 20, Component.translatable("seedMap.teleportEditBoxX")); - this.teleportEditBoxX.setHint(Component.literal("X")); + this.teleportEditBoxX = new EditBox(this.font, this.horizontalPadding(), this.height - 20, this.seedMapWidth / 4, 20, Component.translatable("seedMap.teleportEditBoxX")); + this.teleportEditBoxX.setHint(Component.literal("x")); this.teleportEditBoxX.setMaxLength(9); this.addRenderableWidget(this.teleportEditBoxX); - this.teleportEditBoxZ = new EditBox(this.font, this.width / 2, this.verticalPadding() + this.seedMapHeight + 1, TELEPORT_FIELD_WIDTH, 20, Component.translatable("seedMap.teleportEditBoxZ")); - this.teleportEditBoxZ.setHint(Component.literal("Z")); + this.teleportEditBoxZ = new EditBox(this.font, this.horizontalPadding() + this.seedMapWidth / 4, this.height - 20, this.seedMapWidth / 4, 20, Component.translatable("seedMap.teleportEditBoxZ")); + this.teleportEditBoxZ.setHint(Component.literal("z")); this.teleportEditBoxZ.setMaxLength(9); this.addRenderableWidget(this.teleportEditBoxZ); } private void createWaypointNameField() { - this.waypointNameEditBox = new EditBox(this.font, this.horizontalPadding() + this.seedMapWidth - WAYPOINT_NAME_FIELD_WIDTH, this.verticalPadding() + this.seedMapHeight + 1, WAYPOINT_NAME_FIELD_WIDTH, 20, Component.translatable("seedMap.waypointNameEditBox")); + this.waypointNameEditBox = new EditBox(this.font, this.horizontalPadding() + this.seedMapWidth / 2, this.height - 20, this.seedMapWidth / 2 - SEED_ICON_SIZE - 2 * SEED_ICON_PADDING, 20, Component.translatable("seedMap.waypointNameEditBox")); this.waypointNameEditBox.setHint(Component.literal("Waypoint name")); this.addRenderableWidget(this.waypointNameEditBox); } @@ -907,7 +969,7 @@ public void mouseMoved(double mouseX, double mouseY) { } private void handleMapMouseMoved(double mouseX, double mouseY) { - if (mouseX < this.horizontalPadding() || mouseX > this.horizontalPadding() + this.seedMapWidth || mouseY < this.verticalPadding() || mouseY > this.verticalPadding() + this.seedMapHeight) { + if (!this.isMouseOverMap(mouseX, mouseY)) { return; } @@ -947,7 +1009,7 @@ public boolean mouseDragged(MouseButtonEvent mouseButtonEvent, double dragX, dou } double mouseX = mouseButtonEvent.x(); double mouseY = mouseButtonEvent.y(); - if (mouseX < this.horizontalPadding() || mouseX > this.horizontalPadding() + this.seedMapWidth || mouseY < this.verticalPadding() || mouseY > this.verticalPadding() + this.seedMapHeight) { + if (!this.isMouseOverMap(mouseX, mouseY)) { return false; } @@ -963,6 +1025,10 @@ public boolean mouseClicked(MouseButtonEvent mouseButtonEvent, boolean doubleCli if (super.mouseClicked(mouseButtonEvent, doubleClick)) { return true; } + if (mouseButtonEvent.button() == InputConstants.MOUSE_BUTTON_LEFT && this.isMouseOverSeedWidget(mouseButtonEvent.x(), mouseButtonEvent.y())) { + this.minecraft.keyboardHandler.setClipboard(Long.toString(this.seed)); + return true; + } int button = mouseButtonEvent.button(); if (this.chestLootWidget != null && this.chestLootWidget.mouseClicked(mouseButtonEvent, doubleClick)) { return true; @@ -988,7 +1054,7 @@ private boolean handleMapFeatureLeftClicked(MouseButtonEvent mouseButtonEvent, b } double mouseX = mouseButtonEvent.x(); double mouseY = mouseButtonEvent.y(); - if (mouseX < this.horizontalPadding() || mouseX > this.horizontalPadding() + this.seedMapWidth || mouseY < this.verticalPadding() || mouseY > this.verticalPadding() + this.seedMapHeight) { + if (mouseX < this.horizontalPadding() || mouseX > this.horizontalPadding() + this.seedMapWidth || mouseY < 0 || mouseY > this.seedMapHeight()) { return false; } Optional optionalFeatureWidget = this.featureWidgets.stream() @@ -1082,7 +1148,7 @@ private boolean handleMapMiddleClicked(MouseButtonEvent mouseButtonEvent, boolea } double mouseX = mouseButtonEvent.x(); double mouseY = mouseButtonEvent.y(); - if (mouseX < this.horizontalPadding() || mouseX > this.horizontalPadding() + this.seedMapWidth || mouseY < this.verticalPadding() || mouseY > this.verticalPadding() + this.seedMapHeight) { + if (!this.isMouseOverMap(mouseX, mouseY)) { return false; } this.minecraft.keyboardHandler.setClipboard("%d ~ %d".formatted(QuartPos.toBlock(this.mouseQuart.x()), QuartPos.toBlock(this.mouseQuart.z()))); @@ -1097,7 +1163,7 @@ private boolean handleMapRightClicked(MouseButtonEvent mouseButtonEvent, boolean } double mouseX = mouseButtonEvent.x(); double mouseY = mouseButtonEvent.y(); - if (mouseX < this.horizontalPadding() || mouseX > this.horizontalPadding() + this.seedMapWidth || mouseY < this.verticalPadding() || mouseY > this.verticalPadding() + this.seedMapHeight) { + if (!this.isMouseOverMap(mouseX, mouseY)) { return false; } @@ -1124,7 +1190,11 @@ private boolean handleTeleportFieldEnter(KeyEvent keyEvent) { if (keyCode != InputConstants.KEY_RETURN) { return false; } - if (!this.teleportEditBoxX.isActive() && !this.teleportEditBoxZ.isActive()) { + if (this.teleportEditBoxX == null || this.teleportEditBoxZ == null) { + return false; + } + boolean hasTeleportFocus = this.teleportEditBoxX.isActive() || this.teleportEditBoxZ.isActive(); + if (!hasTeleportFocus) { return false; } int x, z; @@ -1132,17 +1202,21 @@ private boolean handleTeleportFieldEnter(KeyEvent keyEvent) { x = Integer.parseInt(this.teleportEditBoxX.getValue()); z = Integer.parseInt(this.teleportEditBoxZ.getValue()); } catch (NumberFormatException _) { + this.clearEntryBoxFocus(); return false; } if (x < -Level.MAX_LEVEL_SIZE || x > Level.MAX_LEVEL_SIZE) { + this.clearEntryBoxFocus(); return false; } if (z < -Level.MAX_LEVEL_SIZE || z > Level.MAX_LEVEL_SIZE) { + this.clearEntryBoxFocus(); return false; } this.moveCenter(new QuartPos2f(QuartPos.fromBlock(x), QuartPos.fromBlock(z))); this.teleportEditBoxX.setValue(""); this.teleportEditBoxZ.setValue(""); + this.clearEntryBoxFocus(); return true; } @@ -1173,9 +1247,22 @@ private boolean handleWaypointNameFieldEnter(KeyEvent keyEvent) { return false; } this.waypointNameEditBox.setValue(""); + this.clearEntryBoxFocus(); return true; } + private void clearEntryBoxFocus() { + if (this.teleportEditBoxX != null) { + this.teleportEditBoxX.setFocused(false); + } + if (this.teleportEditBoxZ != null) { + this.teleportEditBoxZ.setFocused(false); + } + if (this.waypointNameEditBox != null) { + this.waypointNameEditBox.setFocused(false); + } + } + @Override public void onClose() { super.onClose(); @@ -1227,10 +1314,10 @@ public boolean withinBounds() { int maxX = minX + this.width(); int maxY = minY + this.height(); - if (maxX >= horizontalPadding() + seedMapWidth || maxY >= verticalPadding() + seedMapHeight) { + if (maxX >= horizontalPadding() + seedMapWidth || maxY >= seedMapHeight()) { return false; } - if (minX < horizontalPadding() || minY < verticalPadding()) { + if (minX < horizontalPadding() || minY < 0) { return false; } return true; @@ -1304,8 +1391,12 @@ protected int horizontalPadding() { return HORIZONTAL_PADDING; } - protected int verticalPadding() { - return VERTICAL_PADDING; + //protected int verticalPadding() { + // return VERTICAL_PADDING; + //} + + protected int seedMapHeight() { + return this.height - VERTICAL_LOWER_PADDING; } protected long getSeed() { diff --git a/src/main/resources/assets/seedmapper/lang/en_us.json b/src/main/resources/assets/seedmapper/lang/en_us.json index d7c93cca..fcaf0c7e 100644 --- a/src/main/resources/assets/seedmapper/lang/en_us.json +++ b/src/main/resources/assets/seedmapper/lang/en_us.json @@ -111,5 +111,7 @@ "seedMap.coordinatesCopied": "%s (coordinates copied!)", "seedMap.teleportEditBoxX": "Edit box X coordinate", "seedMap.teleportEditBoxZ": "Edit box Z coordinate", - "seedMap.waypointNameEditBox": "Waypoint name edit box" + "seedMap.waypointNameEditBox": "Waypoint name edit box", + + "seedMap.clickToCopy": "Click to copy" } From 18a70ffe2e0e74f8be458a8b01141206bd6ae085 Mon Sep 17 00:00:00 2001 From: onion Date: Sat, 7 Feb 2026 09:24:03 -0500 Subject: [PATCH 3/9] modified the seed map scroll limit (no sampling optimisations) --- .../dev/xpple/seedmapper/config/Configs.java | 7 ++- .../seedmapper/seedmap/SeedMapScreen.java | 59 +++++++++++-------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/main/java/dev/xpple/seedmapper/config/Configs.java b/src/main/java/dev/xpple/seedmapper/config/Configs.java index e50d4bbe..6e843f0a 100644 --- a/src/main/java/dev/xpple/seedmapper/config/Configs.java +++ b/src/main/java/dev/xpple/seedmapper/config/Configs.java @@ -16,6 +16,7 @@ import net.minecraft.client.Minecraft; import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.network.chat.Component; +import net.minecraft.util.Mth; import net.minecraft.util.Util; import java.time.Duration; @@ -80,10 +81,10 @@ private static void setSeedMapThreads(int seedMapThreads) { } @Config(setter = @Config.Setter("setPixelsPerBiome")) - public static int PixelsPerBiome = 4; + public static float PixelsPerBiome = 4.0F; - private static void setPixelsPerBiome(int pixelsPerBiome) { - PixelsPerBiome = Math.clamp(pixelsPerBiome, SeedMapScreen.MIN_PIXELS_PER_BIOME, SeedMapScreen.MAX_PIXELS_PER_BIOME); + private static void setPixelsPerBiome(float pixelsPerBiome) { + PixelsPerBiome = Mth.clamp(pixelsPerBiome, SeedMapScreen.MIN_PIXELS_PER_BIOME, SeedMapScreen.MAX_PIXELS_PER_BIOME); } @Config(setter = @Config.Setter("setMinimapOffsetX")) diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java index 749cb011..10ef9793 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java @@ -150,10 +150,8 @@ public class SeedMapScreen extends Screen { private static final int HORIZONTAL_PADDING = 50; //private static final int VERTICAL_PADDING = 0; - private static final int VERTICAL_LOWER_PADDING = 20; - - public static final int MIN_PIXELS_PER_BIOME = 1; - public static final int MAX_PIXELS_PER_BIOME = 100; + public static final float MIN_PIXELS_PER_BIOME = 0.25F; + public static final float MAX_PIXELS_PER_BIOME = 100.0F; private static final int HORIZONTAL_FEATURE_TOGGLE_SPACING = 1; private static final int VERTICAL_FEATURE_TOGGLE_SPACING = 1; @@ -165,7 +163,9 @@ public class SeedMapScreen extends Screen { //private static final int TELEPORT_FIELD_WIDTH = 70; //private static final int WAYPOINT_NAME_FIELD_WIDTH = 100; - private static final IntSupplier TILE_SIZE_PIXELS = () -> TilePos.TILE_SIZE_CHUNKS * SCALED_CHUNK_SIZE * Configs.PixelsPerBiome; + private static float tileSizePixels() { + return TilePos.TILE_SIZE_CHUNKS * SCALED_CHUNK_SIZE * Configs.PixelsPerBiome; + } private static final double FEATURE_TOGGLE_LOWER_PADDING_FACTOR = 0.95; @@ -331,7 +331,7 @@ protected void init() { this.centerY = this.height / 2; this.seedMapWidth = this.width - this.horizontalPadding(); - this.seedMapHeight = this.height - VERTICAL_LOWER_PADDING; + this.seedMapHeight = this.height; if (!this.isMinimap()) { this.createFeatureToggles(); @@ -346,7 +346,6 @@ protected void init() { public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { this.lastMouseX = mouseX; this.lastMouseY = mouseY; - super.render(guiGraphics, mouseX, mouseY, partialTick); // draw title //Component seedComponent = Component.translatable("seedMap.seed", accent(Long.toString(this.seed)), Cubiomes.mc2str(this.version).getString(0), ComponentUtils.formatGeneratorFlags(this.generatorFlags)); //guiGraphics.drawString(this.font, seedComponent, this.horizontalPadding(), this.verticalPadding() - this.font.lineHeight - 1, -1); @@ -375,12 +374,19 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partia } this.drawSeedWidget(guiGraphics); + + super.render(guiGraphics, mouseX, mouseY, partialTick); + } + + @Override + public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + // No background; the map fills the screen. } protected void renderBiomes(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { - int tileSizePixels = TILE_SIZE_PIXELS.getAsInt(); - int horTileRadius = Math.ceilDiv(this.seedMapWidth, tileSizePixels) + 1; - int verTileRadius = Math.ceilDiv(this.seedMapHeight, tileSizePixels) + 1; + float tileSizePixels = tileSizePixels(); + int horTileRadius = Mth.ceil(this.seedMapWidth / tileSizePixels) + 1; + int verTileRadius = Mth.ceil(this.seedMapHeight / tileSizePixels) + 1; TilePos centerTile = TilePos.fromQuartPos(QuartPos2.fromQuartPos2f(this.centerQuart)); for (int relTileX = -horTileRadius; relTileX <= horTileRadius; relTileX++) { @@ -407,14 +413,14 @@ protected void renderBiomes(GuiGraphics guiGraphics, int mouseX, int mouseY, flo } protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { - int tileSizePixels = TILE_SIZE_PIXELS.getAsInt(); - int horTileRadius = Math.ceilDiv(this.seedMapWidth, tileSizePixels) + 1; - int verTileRadius = Math.ceilDiv(this.seedMapHeight, tileSizePixels) + 1; + float tileSizePixels = tileSizePixels(); + int horTileRadius = Mth.ceil(this.seedMapWidth / tileSizePixels) + 1; + int verTileRadius = Mth.ceil(this.seedMapHeight / tileSizePixels) + 1; TilePos centerTile = TilePos.fromQuartPos(QuartPos2.fromQuartPos2f(this.centerQuart)); - int horChunkRadius = Math.ceilDiv(this.seedMapWidth / 2, SCALED_CHUNK_SIZE * Configs.PixelsPerBiome); - int verChunkRadius = Math.ceilDiv(this.seedMapHeight / 2, SCALED_CHUNK_SIZE * Configs.PixelsPerBiome); + int horChunkRadius = Mth.ceil((this.seedMapWidth / 2.0F) / (SCALED_CHUNK_SIZE * Configs.PixelsPerBiome)); + int verChunkRadius = Mth.ceil((this.seedMapHeight / 2.0F) / (SCALED_CHUNK_SIZE * Configs.PixelsPerBiome)); // compute structures Configs.ToggledFeatures.stream() @@ -554,11 +560,11 @@ protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, f private void drawTile(GuiGraphics guiGraphics, Tile tile) { TilePos tilePos = tile.pos(); QuartPos2f relTileQuart = QuartPos2f.fromQuartPos(QuartPos2.fromTilePos(tilePos)).subtract(this.centerQuart); - int tileSizePixels = TILE_SIZE_PIXELS.getAsInt(); + float tileSizePixels = tileSizePixels(); int minX = this.centerX + Mth.floor(Configs.PixelsPerBiome * relTileQuart.x()); int minY = this.centerY + Mth.floor(Configs.PixelsPerBiome * relTileQuart.z()); - int maxX = minX + tileSizePixels; - int maxY = minY + tileSizePixels; + int maxX = minX + Mth.ceil(tileSizePixels); + int maxY = minY + Mth.ceil(tileSizePixels); if (maxX < this.horizontalPadding() || minX > this.horizontalPadding() + this.seedMapWidth) { return; @@ -569,22 +575,23 @@ private void drawTile(GuiGraphics guiGraphics, Tile tile) { float u0, u1, v0, v1; if (minX < this.horizontalPadding()) { - u0 = (float) (this.horizontalPadding() - minX) / tileSizePixels; + u0 = (this.horizontalPadding() - minX) / tileSizePixels; minX = this.horizontalPadding(); } else u0 = 0; if (maxX > this.horizontalPadding() + this.seedMapWidth) { - u1 = 1 - ((float) (maxX - this.horizontalPadding() - this.seedMapWidth) / tileSizePixels); + u1 = 1 - ((maxX - this.horizontalPadding() - this.seedMapWidth) / tileSizePixels); maxX = this.horizontalPadding() + this.seedMapWidth; } else u1 = 1; if (minY < 0) { - v0 = (float) (0 - minY) / tileSizePixels; + v0 = (0 - minY) / tileSizePixels; minY = 0; } else v0 = 0; if (maxY > this.seedMapHeight()) { - v1 = 1 - ((float) (maxY - this.seedMapHeight()) / tileSizePixels); + v1 = 1 - ((maxY - this.seedMapHeight()) / tileSizePixels); maxY = this.seedMapHeight(); } else v1 = 1; + //tile.texture().setFilter(Configs.PixelsPerBiome < 1.0F, false); guiGraphics.submitBlit(RenderPipelines.GUI_TEXTURED, tile.texture().getTextureView(), tile.texture().getSampler(), minX, minY, maxX, maxY, u0, u1, v0, v1, 0xFF_FFFFFF); } @@ -985,10 +992,10 @@ public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, doubl return true; } - float currentScroll = Mth.clamp((float) Configs.PixelsPerBiome / MAX_PIXELS_PER_BIOME, 0.0F, 1.0F); - currentScroll = Mth.clamp(currentScroll - (float) (-scrollY / MAX_PIXELS_PER_BIOME), 0.0F, 1.0F); + float currentScroll = Mth.clamp(Configs.PixelsPerBiome / MAX_PIXELS_PER_BIOME, MIN_PIXELS_PER_BIOME / MAX_PIXELS_PER_BIOME, 1.0F); + currentScroll = Mth.clamp(currentScroll - (float) (-scrollY / MAX_PIXELS_PER_BIOME), MIN_PIXELS_PER_BIOME / MAX_PIXELS_PER_BIOME, 1.0F); - Configs.PixelsPerBiome = Math.max((int) (currentScroll * MAX_PIXELS_PER_BIOME + 0.5), MIN_PIXELS_PER_BIOME); + Configs.PixelsPerBiome = currentScroll * MAX_PIXELS_PER_BIOME; this.featureWidgets.removeIf(widget -> { widget.updatePosition(); @@ -1396,7 +1403,7 @@ protected int horizontalPadding() { //} protected int seedMapHeight() { - return this.height - VERTICAL_LOWER_PADDING; + return this.height; } protected long getSeed() { From a079a143c5b7c7bca25d8e0e769a6c995ce94b83 Mon Sep 17 00:00:00 2001 From: onion Date: Sat, 7 Feb 2026 09:39:38 -0500 Subject: [PATCH 4/9] small tweaks --- .../seedmapper/seedmap/SeedMapScreen.java | 93 ++++++++++++++----- 1 file changed, 69 insertions(+), 24 deletions(-) diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java index 10ef9793..4614cb7c 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java @@ -102,7 +102,6 @@ import java.util.Objects; import java.util.Optional; import java.util.OptionalInt; -import java.util.function.IntSupplier; import java.util.function.ToIntBiFunction; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -153,12 +152,16 @@ public class SeedMapScreen extends Screen { public static final float MIN_PIXELS_PER_BIOME = 0.25F; public static final float MAX_PIXELS_PER_BIOME = 100.0F; + private static final int VERTICAL_LOWER_PADDING = 20; + private static final int HORIZONTAL_FEATURE_TOGGLE_SPACING = 1; private static final int VERTICAL_FEATURE_TOGGLE_SPACING = 1; private static final Identifier SEED_ICON_TEXTURE = Identifier.fromNamespaceAndPath("minecraft", "textures/item/wheat_seeds.png"); private static final int SEED_ICON_SIZE = 16; private static final int SEED_ICON_PADDING = 4; + private static final float MIN_STRUCTURE_REGION_PIXELS = 8.0F; + private static final float MIN_CHUNK_PIXELS = 4.0F; //private static final int TELEPORT_FIELD_WIDTH = 70; //private static final int WAYPOINT_NAME_FIELD_WIDTH = 100; @@ -331,7 +334,7 @@ protected void init() { this.centerY = this.height / 2; this.seedMapWidth = this.width - this.horizontalPadding(); - this.seedMapHeight = this.height; + this.seedMapHeight = this.height - VERTICAL_LOWER_PADDING; if (!this.isMinimap()) { this.createFeatureToggles(); @@ -364,7 +367,7 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partia this.teleportEditBoxZ.setHint(Component.literal("Z: %d".formatted(QuartPos.toBlock(this.mouseQuart.z())))); } - if (this.isMouseOverMap(this.lastMouseX, this.lastMouseY)) { + if (this.isMouseOverMap(this.lastMouseX, this.lastMouseY) && !this.isMouseOverSeedWidget(this.lastMouseX, this.lastMouseY)) { OptionalInt optionalBiome = getBiome(this.mouseQuart); if (optionalBiome.isPresent()) { Component tooltip = Component.literal(Cubiomes.biome2str(this.version, optionalBiome.getAsInt()).getString(0)); @@ -373,9 +376,9 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partia } } - this.drawSeedWidget(guiGraphics); - super.render(guiGraphics, mouseX, mouseY, partialTick); + + this.drawSeedWidget(guiGraphics); } @Override @@ -401,7 +404,9 @@ protected void renderBiomes(GuiGraphics guiGraphics, int mouseX, int mouseY, flo } // compute slime chunks and store in texture - if (this.toggleableFeatures.contains(MapFeature.SLIME_CHUNK) && Configs.ToggledFeatures.contains(MapFeature.SLIME_CHUNK)) { + if (this.shouldRenderFeature(MapFeature.SLIME_CHUNK) + && this.toggleableFeatures.contains(MapFeature.SLIME_CHUNK) + && Configs.ToggledFeatures.contains(MapFeature.SLIME_CHUNK)) { BitSet slimeChunkData = this.slimeChunkCache.computeIfAbsent(tilePos, this::calculateSlimeChunkData); if (slimeChunkData != null) { Tile tile = this.slimeChunkTileCache.computeIfAbsent(tilePos, _ -> this.createSlimeChunkTile(tilePos, slimeChunkData)); @@ -413,6 +418,7 @@ protected void renderBiomes(GuiGraphics guiGraphics, int mouseX, int mouseY, flo } protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + this.featureWidgets.clear(); float tileSizePixels = tileSizePixels(); int horTileRadius = Mth.ceil(this.seedMapWidth / tileSizePixels) + 1; int verTileRadius = Mth.ceil(this.seedMapHeight / tileSizePixels) + 1; @@ -425,6 +431,7 @@ protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, f // compute structures Configs.ToggledFeatures.stream() .filter(this.toggleableFeatures::contains) + .filter(this::shouldRenderFeature) .filter(f -> f.getStructureId() != -1) .forEach(feature -> { int structure = feature.getStructureId(); @@ -459,7 +466,9 @@ protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, f guiGraphics.nextStratum(); // draw strongholds - if (this.toggleableFeatures.contains(MapFeature.STRONGHOLD) && Configs.ToggledFeatures.contains(MapFeature.STRONGHOLD)) { + if (this.shouldRenderFeature(MapFeature.STRONGHOLD) + && this.toggleableFeatures.contains(MapFeature.STRONGHOLD) + && Configs.ToggledFeatures.contains(MapFeature.STRONGHOLD)) { TwoDTree tree = strongholdDataCache.get(this.worldIdentifier); if (tree != null) { for (BlockPos strongholdPos : tree) { @@ -469,7 +478,8 @@ protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, f } // compute ore veins - if ((this.toggleableFeatures.contains(MapFeature.COPPER_ORE_VEIN) || this.toggleableFeatures.contains(MapFeature.IRON_ORE_VEIN)) + if ((this.shouldRenderFeature(MapFeature.COPPER_ORE_VEIN) || this.shouldRenderFeature(MapFeature.IRON_ORE_VEIN)) + && (this.toggleableFeatures.contains(MapFeature.COPPER_ORE_VEIN) || this.toggleableFeatures.contains(MapFeature.IRON_ORE_VEIN)) && (Configs.ToggledFeatures.contains(MapFeature.COPPER_ORE_VEIN) || Configs.ToggledFeatures.contains(MapFeature.IRON_ORE_VEIN))) { for (int relTileX = -horTileRadius; relTileX <= horTileRadius; relTileX++) { for (int relTileZ = -verTileRadius; relTileZ <= verTileRadius; relTileZ++) { @@ -486,7 +496,8 @@ protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, f } // compute canyons - if ((this.toggleableFeatures.contains(MapFeature.CANYON)) && Configs.ToggledFeatures.contains(MapFeature.CANYON)) { + if (this.shouldRenderFeature(MapFeature.CANYON) + && (this.toggleableFeatures.contains(MapFeature.CANYON)) && Configs.ToggledFeatures.contains(MapFeature.CANYON)) { for (int relTileX = -horTileRadius; relTileX <= horTileRadius; relTileX++) { for (int relTileZ = -verTileRadius; relTileZ <= verTileRadius; relTileZ++) { TilePos tilePos = new TilePos(centerTile.x() + relTileX, centerTile.z() + relTileZ); @@ -504,7 +515,9 @@ protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, f } // draw waypoints - if (this.toggleableFeatures.contains(MapFeature.WAYPOINT) && Configs.ToggledFeatures.contains(MapFeature.WAYPOINT)) { + if (this.shouldRenderFeature(MapFeature.WAYPOINT) + && this.toggleableFeatures.contains(MapFeature.WAYPOINT) + && Configs.ToggledFeatures.contains(MapFeature.WAYPOINT)) { SimpleWaypointsAPI waypointsApi = SimpleWaypointsAPI.getInstance(); String identifier = waypointsApi.getWorldIdentifier(this.minecraft); Map worldWaypoints = waypointsApi.getWorldWaypoints(identifier); @@ -652,7 +665,9 @@ private void drawFeatureIcons(GuiGraphics guiGraphics) { } protected void drawPlayerIndicator(GuiGraphics guiGraphics) { - if (!this.toggleableFeatures.contains(MapFeature.PLAYER_ICON) || !Configs.ToggledFeatures.contains(MapFeature.PLAYER_ICON)) { + if (!this.shouldRenderFeature(MapFeature.PLAYER_ICON) + || !this.toggleableFeatures.contains(MapFeature.PLAYER_ICON) + || !Configs.ToggledFeatures.contains(MapFeature.PLAYER_ICON)) { return; } QuartPos2f relPlayerQuart = QuartPos2f.fromQuartPos(QuartPos2.fromBlockPos(this.playerPos)).subtract(this.centerQuart); @@ -710,7 +725,7 @@ private int getSeedWidgetMinX() { } private int getSeedWidgetMinY() { - return this.height - SEED_ICON_SIZE - SEED_ICON_PADDING; + return SEED_ICON_PADDING; } private boolean isMouseOverMap(double mouseX, double mouseY) { @@ -728,6 +743,26 @@ private boolean isMouseOverSeedWidget(double mouseX, double mouseY) { return mouseX >= minX && mouseX <= maxX && mouseY >= minY && mouseY <= maxY; } + private boolean shouldRenderFeature(MapFeature feature) { + if (feature == MapFeature.WAYPOINT || feature == MapFeature.WORLD_SPAWN || feature == MapFeature.PLAYER_ICON) { + return true; + } + if (feature == MapFeature.SLIME_CHUNK || feature == MapFeature.CANYON || feature == MapFeature.COPPER_ORE_VEIN || feature == MapFeature.IRON_ORE_VEIN) { + float pixelsPerChunk = SCALED_CHUNK_SIZE * Configs.PixelsPerBiome; + return pixelsPerChunk >= MIN_CHUNK_PIXELS; + } + int structureId = feature.getStructureId(); + if (structureId != -1 && this.structureConfigs != null) { + MemorySegment structureConfig = this.structureConfigs[structureId]; + if (structureConfig != null) { + int regionSize = StructureConfig.regionSize(structureConfig); + float pixelsPerRegion = regionSize * SCALED_CHUNK_SIZE * Configs.PixelsPerBiome; + return pixelsPerRegion >= MIN_STRUCTURE_REGION_PIXELS; + } + } + return true; + } + // Formerly, this laid the feature toggles out in rows above the map. // Now they are laid out in columns besides the map. private void createFeatureToggles() { @@ -936,7 +971,7 @@ private void createTeleportField() { } private void createWaypointNameField() { - this.waypointNameEditBox = new EditBox(this.font, this.horizontalPadding() + this.seedMapWidth / 2, this.height - 20, this.seedMapWidth / 2 - SEED_ICON_SIZE - 2 * SEED_ICON_PADDING, 20, Component.translatable("seedMap.waypointNameEditBox")); + this.waypointNameEditBox = new EditBox(this.font, this.horizontalPadding() + this.seedMapWidth / 2, this.height - 20, this.seedMapWidth / 2, 20, Component.translatable("seedMap.waypointNameEditBox")); this.waypointNameEditBox.setHint(Component.literal("Waypoint name")); this.addRenderableWidget(this.waypointNameEditBox); } @@ -992,18 +1027,28 @@ public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, doubl return true; } - float currentScroll = Mth.clamp(Configs.PixelsPerBiome / MAX_PIXELS_PER_BIOME, MIN_PIXELS_PER_BIOME / MAX_PIXELS_PER_BIOME, 1.0F); + float oldPixelsPerBiome = Configs.PixelsPerBiome; + float currentScroll = Mth.clamp(oldPixelsPerBiome / MAX_PIXELS_PER_BIOME, MIN_PIXELS_PER_BIOME / MAX_PIXELS_PER_BIOME, 1.0F); currentScroll = Mth.clamp(currentScroll - (float) (-scrollY / MAX_PIXELS_PER_BIOME), MIN_PIXELS_PER_BIOME / MAX_PIXELS_PER_BIOME, 1.0F); + float newPixelsPerBiome = currentScroll * MAX_PIXELS_PER_BIOME; + Configs.PixelsPerBiome = newPixelsPerBiome; + + if (this.isMouseOverMap(mouseX, mouseY)) { + float relXQuartOld = (float) ((mouseX - this.centerX) / oldPixelsPerBiome); + float relZQuartOld = (float) ((mouseY - this.centerY) / oldPixelsPerBiome); + QuartPos2f worldQuart = this.centerQuart.add(relXQuartOld, relZQuartOld); + float relXQuartNew = (float) ((mouseX - this.centerX) / newPixelsPerBiome); + float relZQuartNew = (float) ((mouseY - this.centerY) / newPixelsPerBiome); + this.moveCenter(worldQuart.subtract(new QuartPos2f(relXQuartNew, relZQuartNew))); + } else { + this.featureWidgets.removeIf(widget -> { + widget.updatePosition(); + return !widget.withinBounds(); + }); - Configs.PixelsPerBiome = currentScroll * MAX_PIXELS_PER_BIOME; - - this.featureWidgets.removeIf(widget -> { - widget.updatePosition(); - return !widget.withinBounds(); - }); - - if (this.markerWidget != null) { - this.markerWidget.updatePosition(); + if (this.markerWidget != null) { + this.markerWidget.updatePosition(); + } } return true; } @@ -1403,7 +1448,7 @@ protected int horizontalPadding() { //} protected int seedMapHeight() { - return this.height; + return this.height - VERTICAL_LOWER_PADDING; } protected long getSeed() { From 23cbe0d37c6dcd394eb3fe19f95aae45730e7808 Mon Sep 17 00:00:00 2001 From: onion Date: Sat, 7 Feb 2026 10:58:18 -0500 Subject: [PATCH 5/9] loot search --- .../seedmapper/seedmap/LootSearchScreen.java | 709 ++++++++++++++++++ .../seedmapper/seedmap/SeedMapScreen.java | 46 +- .../assets/seedmapper/lang/en_us.json | 26 + 3 files changed, 780 insertions(+), 1 deletion(-) create mode 100644 src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java new file mode 100644 index 00000000..9e94b31e --- /dev/null +++ b/src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java @@ -0,0 +1,709 @@ +package dev.xpple.seedmapper.seedmap; + +import com.github.cubiomes.Cubiomes; +import com.github.cubiomes.Generator; +import com.github.cubiomes.ItemStack; +import com.github.cubiomes.LootTableContext; +import com.github.cubiomes.Piece; +import com.github.cubiomes.Pos; +import com.github.cubiomes.StructureConfig; +import com.github.cubiomes.StructureSaltConfig; +import com.github.cubiomes.StructureVariant; +import com.github.cubiomes.SurfaceNoise; + +import dev.xpple.seedmapper.command.commands.LocateCommand; +import dev.xpple.seedmapper.feature.StructureChecks; +import dev.xpple.seedmapper.thread.SeedMapExecutor; +import dev.xpple.seedmapper.util.ComponentUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipPositioner; +import net.minecraft.client.gui.screens.inventory.tooltip.DefaultTooltipPositioner; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.util.Mth; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; + +import java.lang.foreign.Arena; +import java.lang.foreign.ValueLayout; +import java.lang.foreign.MemorySegment; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; + +import static dev.xpple.seedmapper.util.ChatBuilder.*; + +public class LootSearchScreen extends Screen { + + private static final int FIELD_WIDTH = 120; + private static final int FIELD_HEIGHT = 20; + private static final int BUTTON_WIDTH = 80; + private static final int BUTTON_HEIGHT = 20; + private static final int ROW_SPACING = 6; + private static final int TAB_HEIGHT = 20; + private static final int TAB_WIDTH = 80; + private static final int LIST_ROW_HEIGHT = 12; + private static final int LIST_PADDING = 8; + private static final int RESULTS_TOP = 44; + private static final int COLUMN_GAP = 6; + private static final int STRUCTURE_ICON_SIZE = 16; + private static final int STRUCTURE_ICON_GAP = 4; + private static final int STRUCTURE_BUTTON_ROWS = 2; + + private final Screen parent; + private final SeedMapExecutor executor = new SeedMapExecutor(); + private final long seed; + private final int dimension; + private final int version; + private final int generatorFlags; + private final BlockPos playerPos; + + private enum Tab { + SEARCH, + RESULTS + } + + private enum SortMode { + COUNT, + DISTANCE + } + + private Tab activeTab = Tab.SEARCH; + private SortMode structureSortMode = SortMode.COUNT; + + private @Nullable EditBox radiusEditBox; + private @Nullable Button searchButton; + private @Nullable Button searchTabButton; + private @Nullable Button resultsTabButton; + private @Nullable Button sortToggleButton; + private @Nullable EditBox resultsSearchEditBox; + private final List structureToggleButtons = new ArrayList<>(); + private final IntSet enabledStructureIds = new IntOpenHashSet(); + private final List lootFeatures; + + private Component status = Component.empty(); + private boolean searching = false; + private List itemResults = new ArrayList<>(); + private int resultsScroll = 0; + private @Nullable ItemResult selectedItem = null; + private int structureScroll = 0; + + public LootSearchScreen(Screen parent, long seed, int dimension, int version, int generatorFlags, BlockPos playerPos) { + super(Component.translatable("seedMap.lootSearch.title")); + this.parent = parent; + this.seed = seed; + this.dimension = dimension; + this.version = version; + this.generatorFlags = generatorFlags; + this.playerPos = playerPos; + this.lootFeatures = List.of(MapFeature.values()).stream() + .filter(feature -> LocateCommand.LOOT_SUPPORTED_STRUCTURES.contains(feature.getStructureId())) + .filter(feature -> feature.getDimension() == this.dimension) + .toList(); + this.lootFeatures.forEach(feature -> this.enabledStructureIds.add(feature.getStructureId())); + } + + @Override + protected void init() { + super.init(); + + int centerX = this.width / 2; + int tabsY = 12; + this.searchTabButton = Button.builder(Component.translatable("seedMap.lootSearch.tab.search"), button -> this.setActiveTab(Tab.SEARCH)) + .bounds(centerX - TAB_WIDTH - 4, tabsY, TAB_WIDTH, TAB_HEIGHT) + .build(); + this.resultsTabButton = Button.builder(Component.translatable("seedMap.lootSearch.tab.results"), button -> this.setActiveTab(Tab.RESULTS)) + .bounds(centerX + 4, tabsY, TAB_WIDTH, TAB_HEIGHT) + .build(); + this.resultsTabButton.active = false; + this.addRenderableWidget(this.searchTabButton); + this.addRenderableWidget(this.resultsTabButton); + + int startY = this.height / 2 - FIELD_HEIGHT - ROW_SPACING; + + this.radiusEditBox = new EditBox(this.font, centerX - FIELD_WIDTH / 2, startY, FIELD_WIDTH, FIELD_HEIGHT, Component.translatable("seedMap.lootSearch.radius")); + this.radiusEditBox.setHint(Component.translatable("seedMap.lootSearch.radiusHint")); + this.radiusEditBox.setMaxLength(7); + this.radiusEditBox.setValue("2000"); + this.addRenderableWidget(this.radiusEditBox); + + this.searchButton = Button.builder(Component.translatable("seedMap.lootSearch.search"), button -> this.startSearch()) + .bounds(centerX - BUTTON_WIDTH / 2, startY + FIELD_HEIGHT + ROW_SPACING, BUTTON_WIDTH, BUTTON_HEIGHT) + .build(); + this.addRenderableWidget(this.searchButton); + + int iconsTop = startY + FIELD_HEIGHT + ROW_SPACING + BUTTON_HEIGHT + ROW_SPACING; + int columns = (int) Math.ceil(this.lootFeatures.size() / (double) STRUCTURE_BUTTON_ROWS); + int totalWidth = columns * STRUCTURE_ICON_SIZE + (columns - 1) * STRUCTURE_ICON_GAP; + int iconStartX = centerX - totalWidth / 2; + for (int i = 0; i < this.lootFeatures.size(); i++) { + int row = i / columns; + int col = i % columns; + int x = iconStartX + col * (STRUCTURE_ICON_SIZE + STRUCTURE_ICON_GAP); + int y = iconsTop + row * (STRUCTURE_ICON_SIZE + STRUCTURE_ICON_GAP); + MapFeature feature = this.lootFeatures.get(i); + StructureToggleButton button = new StructureToggleButton(x, y, feature); + this.structureToggleButtons.add(button); + this.addRenderableWidget(button); + } + + int detailsLeft = this.width / 2 + LIST_PADDING; + int sortButtonY = this.height - LIST_PADDING - TAB_HEIGHT; + this.sortToggleButton = Button.builder(this.sortButtonLabel(), button -> this.toggleSortMode()) + .bounds(detailsLeft, sortButtonY, TAB_WIDTH + 20, TAB_HEIGHT) + .build(); + this.addRenderableWidget(this.sortToggleButton); + + int listLeft = LIST_PADDING; + int listTop = RESULTS_TOP; + int listWidth = this.width / 2 - LIST_PADDING * 2; + int searchY = this.height - LIST_PADDING - FIELD_HEIGHT; + this.resultsSearchEditBox = new EditBox(this.font, listLeft, searchY, listWidth, FIELD_HEIGHT, Component.translatable("seedMap.lootSearch.filter")); + this.resultsSearchEditBox.setHint(Component.translatable("seedMap.lootSearch.filterHint")); + this.addRenderableWidget(this.resultsSearchEditBox); + + this.updateTabVisibility(); + } + + @Override + public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + super.render(guiGraphics, mouseX, mouseY, partialTick); + if (this.activeTab == Tab.SEARCH) { + this.renderSearchTab(guiGraphics); + } else { + this.renderResultsTab(guiGraphics, mouseX, mouseY); + } + } + + private void renderSearchTab(GuiGraphics guiGraphics) { + guiGraphics.drawCenteredString(this.font, this.getTitle(), this.width / 2, this.height / 2 - FIELD_HEIGHT - ROW_SPACING - this.font.lineHeight - 4, 0xFF_FFFFFF); + if (!this.status.getString().isEmpty()) { + guiGraphics.drawCenteredString(this.font, this.status, this.width / 2, this.height / 2 + FIELD_HEIGHT + BUTTON_HEIGHT + ROW_SPACING * 2, 0xFF_FFFFFF); + } + } + + private void renderResultsTab(GuiGraphics guiGraphics, int mouseX, int mouseY) { + int listLeft = LIST_PADDING; + int listTop = RESULTS_TOP; + int listWidth = this.width / 2 - LIST_PADDING * 2; + int listBottom = this.height - LIST_PADDING - FIELD_HEIGHT - ROW_SPACING; + int listHeight = Math.max(0, listBottom - listTop); + + guiGraphics.drawString(this.font, Component.translatable("seedMap.lootSearch.results"), listLeft, listTop - this.font.lineHeight - 4, 0xFF_FFFFFF); + + List filteredResults = this.getFilteredResults(); + if (filteredResults.isEmpty()) { + guiGraphics.drawString(this.font, Component.translatable("seedMap.lootSearch.noResults"), listLeft, listTop, 0xFF_A0A0A0); + return; + } + + int visibleRows = Math.max(1, listHeight / LIST_ROW_HEIGHT); + int maxScroll = Math.max(0, filteredResults.size() - visibleRows); + this.resultsScroll = Mth.clamp(this.resultsScroll, 0, maxScroll); + + int startIndex = this.resultsScroll; + int endIndex = Math.min(filteredResults.size(), startIndex + visibleRows); + int y = listTop; + for (int i = startIndex; i < endIndex; i++) { + ItemResult result = filteredResults.get(i); + int color = Objects.equals(this.selectedItem, result) ? 0xFF_FFFFFF : 0xFF_C0C0C0; + Component line = Component.literal("%s x%d".formatted(result.displayName(this.version), result.totalCount)); + guiGraphics.drawString(this.font, line, listLeft, y, color); + y += LIST_ROW_HEIGHT; + } + + int gutterShift = Math.min(24, this.width / 20); + int detailsLeft = this.width / 2 + LIST_PADDING - gutterShift; + int detailsTop = listTop; + if (this.selectedItem == null) { + guiGraphics.drawString(this.font, Component.translatable("seedMap.lootSearch.selectItem"), detailsLeft, detailsTop, 0xFF_A0A0A0); + return; + } + + guiGraphics.drawString(this.font, Component.literal(this.selectedItem.displayName(this.version)), detailsLeft, detailsTop, 0xFF_FFFFFF); + guiGraphics.drawString(this.font, Component.translatable("seedMap.lootSearch.total", this.selectedItem.totalCount), detailsLeft, detailsTop + LIST_ROW_HEIGHT, 0xFF_C0C0C0); + + int structureListTop = detailsTop + LIST_ROW_HEIGHT * 3; + int structureListHeight = this.height - structureListTop - LIST_PADDING - TAB_HEIGHT - ROW_SPACING; + int structureListBottom = structureListTop + structureListHeight; + + List structures = this.selectedItem.sortedStructures(this.structureSortMode, this.playerPos); + if (structures.isEmpty()) { + guiGraphics.drawString(this.font, Component.translatable("seedMap.lootSearch.noStructures"), detailsLeft, structureListTop, 0xFF_A0A0A0); + return; + } + + int detailsRight = this.width - LIST_PADDING; + int detailsWidth = detailsRight - detailsLeft; + int countWidth = this.font.width("999999"); + int maxCoordWidth = this.font.width("-30000000"); + int minCoordWidth = this.font.width("0"); + int iconSize = 12; + int remainingForCoords = detailsWidth - countWidth - iconSize - COLUMN_GAP * 3; + int coordWidth = Mth.clamp(remainingForCoords / 2, minCoordWidth, maxCoordWidth); + + int xColX = detailsLeft; + int zColX = xColX + coordWidth + COLUMN_GAP; + int countColX = zColX + coordWidth + COLUMN_GAP; + int iconColX = countColX + countWidth + COLUMN_GAP; + + guiGraphics.drawString(this.font, Component.translatable("seedMap.lootSearch.column.x"), xColX, structureListTop - LIST_ROW_HEIGHT, 0xFF_C0C0C0); + guiGraphics.drawString(this.font, Component.translatable("seedMap.lootSearch.column.z"), zColX, structureListTop - LIST_ROW_HEIGHT, 0xFF_C0C0C0); + guiGraphics.drawString(this.font, Component.translatable("seedMap.lootSearch.column.count"), countColX, structureListTop - LIST_ROW_HEIGHT, 0xFF_C0C0C0); + guiGraphics.drawString(this.font, Component.translatable("seedMap.lootSearch.column.type"), iconColX, structureListTop - LIST_ROW_HEIGHT, 0xFF_C0C0C0); + + int structureVisibleRows = Math.max(1, structureListHeight / LIST_ROW_HEIGHT); + int structureMaxScroll = Math.max(0, structures.size() - structureVisibleRows); + this.structureScroll = Mth.clamp(this.structureScroll, 0, structureMaxScroll); + int structureStartIndex = this.structureScroll; + int structureEndIndex = Math.min(structures.size(), structureStartIndex + structureVisibleRows); + Component hoverTooltip = null; + int structureY = structureListTop; + for (int i = structureStartIndex; i < structureEndIndex; i++) { + StructureEntry entry = structures.get(i); + guiGraphics.drawString(this.font, Component.literal(Integer.toString(entry.pos.getX())), xColX, structureY, 0xFF_FFFFFF); + guiGraphics.drawString(this.font, Component.literal(Integer.toString(entry.pos.getZ())), zColX, structureY, 0xFF_FFFFFF); + guiGraphics.drawString(this.font, Component.literal(Integer.toString(entry.count)), countColX, structureY, 0xFF_FFFFFF); + MapFeature.Texture texture = this.getStructureTexture(entry.structureId); + int iconX = iconColX; + int iconY = structureY + (LIST_ROW_HEIGHT - iconSize) / 2; + if (texture != null) { + SeedMapScreen.drawIconStatic(guiGraphics, texture.identifier(), iconX, iconY, iconSize, iconSize, 0xFF_FFFFFF); + } else { + guiGraphics.drawString(this.font, Component.literal("?"), iconX, structureY, 0xFF_FFFFFF); + } + if (mouseX >= iconX && mouseX <= iconX + iconSize && mouseY >= iconY && mouseY <= iconY + iconSize) { + hoverTooltip = Component.literal(Cubiomes.struct2str(entry.structureId).getString(0)); + } + structureY += LIST_ROW_HEIGHT; + } + if (hoverTooltip != null) { + guiGraphics.renderTooltip(this.font, List.of(ClientTooltipComponent.create(hoverTooltip.getVisualOrderText())), mouseX, mouseY, DefaultTooltipPositioner.INSTANCE, null); + } + + } + + private void startSearch() { + if (this.searching || this.radiusEditBox == null) { + return; + } + int radius; + try { + radius = Integer.parseInt(this.radiusEditBox.getValue()); + } catch (NumberFormatException _) { + this.status = error(Component.translatable("seedMap.lootSearch.error.invalidRadius").getString()); + return; + } + if (radius <= 0 || radius > Level.MAX_LEVEL_SIZE) { + this.status = error(Component.translatable("seedMap.lootSearch.error.radiusRange").getString()); + return; + } + + this.searching = true; + this.status = base(Component.translatable("seedMap.lootSearch.searching").getString()); + if (this.searchButton != null) { + this.searchButton.active = false; + } + if (this.resultsTabButton != null) { + this.resultsTabButton.active = false; + } + + int clampedRadius = radius; + this.executor.submitCalculation(() -> this.searchLoot(clampedRadius)) + .thenAccept(result -> { + Minecraft.getInstance().schedule(() -> { + if (result == null) { + this.status = error(Component.translatable("seedMap.lootSearch.error.searchFailed").getString()); + this.itemResults = new ArrayList<>(); + this.selectedItem = null; + } else if (result.items.isEmpty()) { + this.status = warn(Component.translatable("seedMap.lootSearch.noLoot").getString()); + this.itemResults = new ArrayList<>(); + this.selectedItem = null; + } else { + this.status = base(Component.translatable("seedMap.lootSearch.foundTypes", result.items.size()).getString()); + this.itemResults = result.items.values().stream() + .sorted((a, b) -> Integer.compare(b.totalCount, a.totalCount)) + .toList(); + this.resultsScroll = 0; + this.structureScroll = 0; + this.selectedItem = this.itemResults.getFirst(); + if (this.resultsTabButton != null) { + this.resultsTabButton.active = true; + } + this.setActiveTab(Tab.RESULTS); + } + this.searching = false; + if (this.searchButton != null) { + this.searchButton.active = true; + } + if (this.resultsTabButton != null && !this.itemResults.isEmpty()) { + this.resultsTabButton.active = true; + } + }); + }); + } + + private @Nullable SearchResults searchLoot(int radiusBlocks) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment generator = Generator.allocate(arena); + Cubiomes.setupGenerator(generator, this.version, this.generatorFlags); + Cubiomes.applySeed(generator, this.dimension, this.seed); + + MemorySegment surfaceNoise = SurfaceNoise.allocate(arena); + Cubiomes.initSurfaceNoise(surfaceNoise, this.dimension, this.seed); + + SearchResults results = new SearchResults(); + MemorySegment structurePos = Pos.allocate(arena); + MemorySegment pieces = Piece.allocateArray(StructureChecks.MAX_END_CITY_AND_FORTRESS_PIECES, arena); + MemorySegment structureVariant = StructureVariant.allocate(arena); + MemorySegment structureSaltConfig = StructureSaltConfig.allocate(arena); + MemorySegment ltcPtr = arena.allocate(Cubiomes.C_POINTER); + + int centerX = this.playerPos.getX(); + int centerZ = this.playerPos.getZ(); + int radiusSq = radiusBlocks * radiusBlocks; + + for (int structure : LocateCommand.LOOT_SUPPORTED_STRUCTURES) { + if (!this.enabledStructureIds.contains(structure)) { + continue; + } + MemorySegment structureConfig = StructureConfig.allocate(arena); + if (Cubiomes.getStructureConfig(structure, this.version, structureConfig) == 0) { + continue; + } + if (StructureConfig.dim(structureConfig) != this.dimension) { + continue; + } + int regionSizeBlocks = StructureConfig.regionSize(structureConfig) << 4; + int minRegionX = Mth.floor((centerX - radiusBlocks) / (float) regionSizeBlocks); + int maxRegionX = Mth.floor((centerX + radiusBlocks) / (float) regionSizeBlocks); + int minRegionZ = Mth.floor((centerZ - radiusBlocks) / (float) regionSizeBlocks); + int maxRegionZ = Mth.floor((centerZ + radiusBlocks) / (float) regionSizeBlocks); + + StructureChecks.GenerationCheck generationCheck = StructureChecks.getGenerationCheck(structure); + List positions = new ArrayList<>(); + for (int regionX = minRegionX; regionX <= maxRegionX; regionX++) { + for (int regionZ = minRegionZ; regionZ <= maxRegionZ; regionZ++) { + if (Cubiomes.getStructurePos(structure, this.version, this.seed, regionX, regionZ, structurePos) == 0) { + continue; + } + int posX = Pos.x(structurePos); + int posZ = Pos.z(structurePos); + int dx = posX - centerX; + int dz = posZ - centerZ; + if (dx * dx + dz * dz > radiusSq) { + continue; + } + if (!generationCheck.check(generator, surfaceNoise, regionX, regionZ, structurePos)) { + continue; + } + int biome = Cubiomes.getBiomeAt(generator, 4, posX >> 2, 320 >> 2, posZ >> 2); + Cubiomes.getVariant(structureVariant, structure, this.version, this.seed, posX, posZ, biome); + biome = StructureVariant.biome(structureVariant) != -1 ? StructureVariant.biome(structureVariant) : biome; + if (Cubiomes.getStructureSaltConfig(structure, this.version, biome, structureSaltConfig) == 0) { + continue; + } + int numPieces = Cubiomes.getStructurePieces(pieces, StructureChecks.MAX_END_CITY_AND_FORTRESS_PIECES, structure, structureSaltConfig, structureVariant, this.version, this.seed, posX, posZ); + if (numPieces <= 0) { + continue; + } + for (int pieceIdx = 0; pieceIdx < numPieces; pieceIdx++) { + MemorySegment piece = Piece.asSlice(pieces, pieceIdx); + int chestCount = Piece.chestCount(piece); + if (chestCount <= 0) { + continue; + } + MemorySegment lootTables = Piece.lootTables(piece); + MemorySegment lootSeeds = Piece.lootSeeds(piece); + for (int chestIdx = 0; chestIdx < chestCount; chestIdx++) { + MemorySegment lootTable = lootTables.getAtIndex(ValueLayout.ADDRESS, chestIdx).reinterpret(Long.MAX_VALUE); + if (Cubiomes.init_loot_table_name(ltcPtr, lootTable, this.version) == 0) { + continue; + } + MemorySegment lootTableContext = ltcPtr.get(ValueLayout.ADDRESS, 0).reinterpret(LootTableContext.sizeof()); + Cubiomes.set_loot_seed(lootTableContext, lootSeeds.getAtIndex(Cubiomes.C_LONG_LONG, chestIdx)); + Cubiomes.generate_loot(lootTableContext); + int lootCount = LootTableContext.generated_item_count(lootTableContext); + for (int lootIdx = 0; lootIdx < lootCount; lootIdx++) { + MemorySegment itemStackInternal = ItemStack.asSlice(LootTableContext.generated_items(lootTableContext), lootIdx); + int itemId = Cubiomes.get_global_item_id(lootTableContext, ItemStack.item(itemStackInternal)); + int count = ItemStack.count(itemStackInternal); + results.addItem(itemId, count, new BlockPos(posX, 0, posZ), structure); + } + } + } + } + } + } + return results; + } catch (Throwable t) { + return null; + } + } + + @Override + public void onClose() { + super.onClose(); + this.executor.close(() -> {}); + if (this.minecraft != null) { + this.minecraft.setScreen(this.parent); + } + } + + @Override + public boolean mouseClicked(MouseButtonEvent event, boolean isDoubleClick) { + if (super.mouseClicked(event, isDoubleClick)) { + return true; + } + if (this.activeTab != Tab.RESULTS || isDoubleClick) { + return false; + } + int mouseX = (int)event.x(); + int mouseY = (int)event.y(); + int listLeft = LIST_PADDING; + int listTop = RESULTS_TOP; + int listWidth = this.width / 2 - LIST_PADDING * 2; + int listBottom = this.height - LIST_PADDING - FIELD_HEIGHT - ROW_SPACING; + int listHeight = Math.max(0, listBottom - listTop); + int listRight = listLeft + listWidth; + //int listBottom = listTop + listHeight; + if (mouseX < listLeft || mouseX > listRight || mouseY < listTop || mouseY > listBottom) { + return false; + } + List filteredResults = this.getFilteredResults(); + int index = this.resultsScroll + (int) ((mouseY - listTop) / LIST_ROW_HEIGHT); + if (index >= 0 && index < filteredResults.size()) { + this.selectedItem = filteredResults.get(index); + this.structureScroll = 0; + return true; + } + return false; + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) { + if (this.activeTab != Tab.RESULTS || this.itemResults.isEmpty()) { + return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY); + } + int listLeft = LIST_PADDING; + int listTop = RESULTS_TOP; + int listWidth = this.width / 2 - LIST_PADDING * 2; + int listBottom = this.height - LIST_PADDING - FIELD_HEIGHT - ROW_SPACING; + int listHeight = Math.max(0, listBottom - listTop); + int listRight = listLeft + listWidth; + //int listBottom = listTop + listHeight; + int delta = scrollY > 0 ? -1 : 1; + + if (mouseX >= listLeft && mouseX <= listRight && mouseY >= listTop && mouseY <= listBottom) { + int visibleRows = Math.max(1, listHeight / LIST_ROW_HEIGHT); + int maxScroll = Math.max(0, this.getFilteredResults().size() - visibleRows); + this.resultsScroll = Mth.clamp(this.resultsScroll + delta, 0, maxScroll); + return true; + } + + int gutterShift = Math.min(24, this.width / 20); + int detailsLeft = this.width / 2 + LIST_PADDING - gutterShift; + int structureListTop = listTop + LIST_ROW_HEIGHT * 3; + int structureListHeight = this.height - structureListTop - LIST_PADDING - TAB_HEIGHT - ROW_SPACING; + int structureListRight = this.width - LIST_PADDING; + int structureListBottom = structureListTop + structureListHeight; + if (mouseX >= detailsLeft && mouseX <= structureListRight && mouseY >= structureListTop && mouseY <= structureListBottom && this.selectedItem != null) { + List structures = this.selectedItem.sortedStructures(this.structureSortMode, this.playerPos); + int visibleRows = Math.max(1, structureListHeight / LIST_ROW_HEIGHT); + int maxScroll = Math.max(0, structures.size() - visibleRows); + this.structureScroll = Mth.clamp(this.structureScroll + delta, 0, maxScroll); + return true; + } + + return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY); + } + + private void setActiveTab(Tab tab) { + this.activeTab = tab; + this.updateTabVisibility(); + } + + private void updateTabVisibility() { + boolean searchVisible = this.activeTab == Tab.SEARCH; + if (this.radiusEditBox != null) { + this.radiusEditBox.visible = searchVisible; + } + if (this.searchButton != null) { + this.searchButton.visible = searchVisible; + } + if (this.searchTabButton != null) { + this.searchTabButton.active = this.activeTab != Tab.SEARCH; + } + if (this.resultsTabButton != null) { + boolean hasResults = !this.itemResults.isEmpty(); + this.resultsTabButton.active = this.activeTab != Tab.RESULTS && hasResults; + } + if (this.sortToggleButton != null) { + this.sortToggleButton.visible = this.activeTab == Tab.RESULTS; + this.sortToggleButton.active = this.activeTab == Tab.RESULTS && !this.itemResults.isEmpty(); + this.sortToggleButton.setMessage(this.sortButtonLabel()); + } + if (this.resultsSearchEditBox != null) { + this.resultsSearchEditBox.visible = this.activeTab == Tab.RESULTS; + this.resultsSearchEditBox.setValue(this.resultsSearchEditBox.getValue()); + } + boolean showSearchControls = this.activeTab == Tab.SEARCH; + for (StructureToggleButton button : this.structureToggleButtons) { + button.visible = showSearchControls; + button.active = showSearchControls; + } + } + + private static final class SearchResults { + private final Map items = new HashMap<>(); + + void addItem(int itemId, int count, BlockPos pos, int structureId) { + ItemResult result = this.items.computeIfAbsent(itemId, ItemResult::new); + result.totalCount += count; + result.addPosition(pos); + result.addStructureEntry(structureId, pos, count); + } + } + + private static final class ItemResult { + private final int itemId; + private int totalCount = 0; + private final List positions = new ArrayList<>(); + private final Map structureEntries = new HashMap<>(); + + private ItemResult(int itemId) { + this.itemId = itemId; + } + + private void addPosition(BlockPos pos) { + if (this.positions.isEmpty() || !this.positions.getLast().equals(pos)) { + this.positions.add(pos); + } + } + + private void addStructureEntry(int structureId, BlockPos pos, int count) { + StructureKey key = new StructureKey(structureId, pos); + StructureEntry entry = this.structureEntries.computeIfAbsent(key, StructureEntry::new); + entry.count += count; + } + + private List sortedStructures(SortMode sortMode, BlockPos playerPos) { + return this.structureEntries.values().stream() + .sorted((a, b) -> { + if (sortMode == SortMode.COUNT) { + int byCount = Integer.compare(b.count, a.count); + if (byCount != 0) { + return byCount; + } + return Long.compare(a.distanceSq(playerPos), b.distanceSq(playerPos)); + } + int byDistance = Long.compare(a.distanceSq(playerPos), b.distanceSq(playerPos)); + if (byDistance != 0) { + return byDistance; + } + return Integer.compare(b.count, a.count); + }) + .toList(); + } + + private String displayName(int version) { + String name = Cubiomes.global_id2item_name(this.itemId, version).getString(0); + return name.contains(":") ? name.substring(name.indexOf(':') + 1) : name; + } + } + + private void toggleSortMode() { + this.structureSortMode = this.structureSortMode == SortMode.COUNT ? SortMode.DISTANCE : SortMode.COUNT; + this.structureScroll = 0; + if (this.sortToggleButton != null) { + this.sortToggleButton.setMessage(this.sortButtonLabel()); + } + } + + private Component sortButtonLabel() { + return Component.translatable("seedMap.lootSearch.sort", Component.translatable(this.structureSortMode == SortMode.COUNT + ? "seedMap.lootSearch.sort.count" + : "seedMap.lootSearch.sort.distance")); + } + + private @Nullable MapFeature.Texture getStructureTexture(int structureId) { + for (MapFeature feature : MapFeature.values()) { + if (feature.getStructureId() == structureId) { + return feature.getDefaultTexture(); + } + } + return null; + } + + private List getFilteredResults() { + if (this.resultsSearchEditBox == null) { + return this.itemResults; + } + String query = this.resultsSearchEditBox.getValue().trim().toLowerCase(); + if (query.isEmpty()) { + return this.itemResults; + } + return this.itemResults.stream() + .filter(result -> result.displayName(this.version).toLowerCase().contains(query)) + .toList(); + } + + private record StructureKey(int structureId, BlockPos pos) {} + + private static final class StructureEntry { + private final int structureId; + private final BlockPos pos; + private int count = 0; + + private StructureEntry(StructureKey key) { + this.structureId = key.structureId(); + this.pos = key.pos(); + } + + private long distanceSq(BlockPos playerPos) { + long dx = (long) this.pos.getX() - playerPos.getX(); + long dz = (long) this.pos.getZ() - playerPos.getZ(); + return dx * dx + dz * dz; + } + } + + private final class StructureToggleButton extends Button { + private final MapFeature feature; + + private StructureToggleButton(int x, int y, MapFeature feature) { + super(x, y, STRUCTURE_ICON_SIZE, STRUCTURE_ICON_SIZE, Component.literal(feature.getName()), (Button button) -> { + if (!(button instanceof StructureToggleButton toggle)) return; + toggle.onPress(); + }, DEFAULT_NARRATION); + this.feature = feature; + this.setTooltip(Tooltip.create(Component.literal(this.feature.getName()))); + } + + private void onPress() { + int structureId = this.feature.getStructureId(); + if (!enabledStructureIds.remove(structureId)) { + enabledStructureIds.add(structureId); + } + } + + @Override + protected void renderContents(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + int color = enabledStructureIds.contains(this.feature.getStructureId()) ? 0xFF_FFFFFF : 0x80_FFFFFF; + MapFeature.Texture texture = this.feature.getDefaultTexture(); + SeedMapScreen.drawIconStatic(guiGraphics, texture.identifier(), this.getX(), this.getY(), this.getWidth(), this.getHeight(), color); + } + } +} diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java index 4614cb7c..d058bf07 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java @@ -160,6 +160,9 @@ public class SeedMapScreen extends Screen { private static final Identifier SEED_ICON_TEXTURE = Identifier.fromNamespaceAndPath("minecraft", "textures/item/wheat_seeds.png"); private static final int SEED_ICON_SIZE = 16; private static final int SEED_ICON_PADDING = 4; + // can't find the chest texture + private static final Identifier LOOT_SEARCH_ICON_TEXTURE = Identifier.fromNamespaceAndPath("minecraft", "textures/item/chest_minecart.png"); + private static final int LOOT_SEARCH_ICON_SIZE = 16; private static final float MIN_STRUCTURE_REGION_PIXELS = 8.0F; private static final float MIN_CHUNK_PIXELS = 4.0F; @@ -367,7 +370,9 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partia this.teleportEditBoxZ.setHint(Component.literal("Z: %d".formatted(QuartPos.toBlock(this.mouseQuart.z())))); } - if (this.isMouseOverMap(this.lastMouseX, this.lastMouseY) && !this.isMouseOverSeedWidget(this.lastMouseX, this.lastMouseY)) { + if (this.isMouseOverMap(this.lastMouseX, this.lastMouseY) + && !this.isMouseOverSeedWidget(this.lastMouseX, this.lastMouseY) + && !this.isMouseOverLootSearchWidget(this.lastMouseX, this.lastMouseY)) { OptionalInt optionalBiome = getBiome(this.mouseQuart); if (optionalBiome.isPresent()) { Component tooltip = Component.literal(Cubiomes.biome2str(this.version, optionalBiome.getAsInt()).getString(0)); @@ -378,7 +383,9 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partia super.render(guiGraphics, mouseX, mouseY, partialTick); + guiGraphics.nextStratum(); this.drawSeedWidget(guiGraphics); + this.drawLootSearchWidget(guiGraphics); } @Override @@ -720,6 +727,23 @@ protected void drawSeedWidget(GuiGraphics guiGraphics) { guiGraphics.renderTooltip(this.font, tooltips, this.lastMouseX, this.lastMouseY, DefaultTooltipPositioner.INSTANCE, null); } + protected void drawLootSearchWidget(GuiGraphics guiGraphics) { + int minX = this.getLootSearchWidgetMinX(); + int minY = this.getLootSearchWidgetMinY(); + int maxX = minX + LOOT_SEARCH_ICON_SIZE; + int maxY = minY + LOOT_SEARCH_ICON_SIZE; + + drawIconStatic(guiGraphics, LOOT_SEARCH_ICON_TEXTURE, minX, minY, LOOT_SEARCH_ICON_SIZE, LOOT_SEARCH_ICON_SIZE, 0xFF_FFFFFF); + + if (this.lastMouseX < minX || this.lastMouseX > maxX || this.lastMouseY < minY || this.lastMouseY > maxY) { + return; + } + + Component tooltip = Component.literal("Loot search"); + List tooltips = List.of(ClientTooltipComponent.create(tooltip.getVisualOrderText())); + guiGraphics.renderTooltip(this.font, tooltips, this.lastMouseX, this.lastMouseY, DefaultTooltipPositioner.INSTANCE, null); + } + private int getSeedWidgetMinX() { return this.width - SEED_ICON_SIZE - SEED_ICON_PADDING; } @@ -728,6 +752,14 @@ private int getSeedWidgetMinY() { return SEED_ICON_PADDING; } + private int getLootSearchWidgetMinX() { + return this.getSeedWidgetMinX() - LOOT_SEARCH_ICON_SIZE - SEED_ICON_PADDING; + } + + private int getLootSearchWidgetMinY() { + return this.getSeedWidgetMinY(); + } + private boolean isMouseOverMap(double mouseX, double mouseY) { return mouseX >= this.horizontalPadding() && mouseX <= this.horizontalPadding() + this.seedMapWidth @@ -743,6 +775,14 @@ private boolean isMouseOverSeedWidget(double mouseX, double mouseY) { return mouseX >= minX && mouseX <= maxX && mouseY >= minY && mouseY <= maxY; } + private boolean isMouseOverLootSearchWidget(double mouseX, double mouseY) { + int minX = this.getLootSearchWidgetMinX(); + int minY = this.getLootSearchWidgetMinY(); + int maxX = minX + LOOT_SEARCH_ICON_SIZE; + int maxY = minY + LOOT_SEARCH_ICON_SIZE; + return mouseX >= minX && mouseX <= maxX && mouseY >= minY && mouseY <= maxY; + } + private boolean shouldRenderFeature(MapFeature feature) { if (feature == MapFeature.WAYPOINT || feature == MapFeature.WORLD_SPAWN || feature == MapFeature.PLAYER_ICON) { return true; @@ -1081,6 +1121,10 @@ public boolean mouseClicked(MouseButtonEvent mouseButtonEvent, boolean doubleCli this.minecraft.keyboardHandler.setClipboard(Long.toString(this.seed)); return true; } + if (mouseButtonEvent.button() == InputConstants.MOUSE_BUTTON_LEFT && this.isMouseOverLootSearchWidget(mouseButtonEvent.x(), mouseButtonEvent.y())) { + this.minecraft.setScreen(new LootSearchScreen(this, this.seed, this.dimension, this.version, this.generatorFlags, this.playerPos)); + return true; + } int button = mouseButtonEvent.button(); if (this.chestLootWidget != null && this.chestLootWidget.mouseClicked(mouseButtonEvent, doubleClick)) { return true; diff --git a/src/main/resources/assets/seedmapper/lang/en_us.json b/src/main/resources/assets/seedmapper/lang/en_us.json index fcaf0c7e..16a8c6bf 100644 --- a/src/main/resources/assets/seedmapper/lang/en_us.json +++ b/src/main/resources/assets/seedmapper/lang/en_us.json @@ -112,6 +112,32 @@ "seedMap.teleportEditBoxX": "Edit box X coordinate", "seedMap.teleportEditBoxZ": "Edit box Z coordinate", "seedMap.waypointNameEditBox": "Waypoint name edit box", + "seedMap.lootSearch.title": "Loot Search", + "seedMap.lootSearch.tab.search": "Search", + "seedMap.lootSearch.tab.results": "Results", + "seedMap.lootSearch.radius": "Radius", + "seedMap.lootSearch.radiusHint": "Radius (blocks)", + "seedMap.lootSearch.search": "Search", + "seedMap.lootSearch.filter": "Search results", + "seedMap.lootSearch.filterHint": "Filter items", + "seedMap.lootSearch.results": "Results", + "seedMap.lootSearch.noResults": "No results yet.", + "seedMap.lootSearch.selectItem": "Select an item to view details.", + "seedMap.lootSearch.total": "Total: %d", + "seedMap.lootSearch.noStructures": "No structures found.", + "seedMap.lootSearch.column.x": "X", + "seedMap.lootSearch.column.z": "Z", + "seedMap.lootSearch.column.count": "Count", + "seedMap.lootSearch.column.type": "Type", + "seedMap.lootSearch.error.invalidRadius": "Invalid radius.", + "seedMap.lootSearch.error.radiusRange": "Radius out of range.", + "seedMap.lootSearch.searching": "Searching...", + "seedMap.lootSearch.error.searchFailed": "Search failed.", + "seedMap.lootSearch.noLoot": "No loot items found.", + "seedMap.lootSearch.foundTypes": "Found %d item types.", + "seedMap.lootSearch.sort": "Sort: %s", + "seedMap.lootSearch.sort.count": "Count", + "seedMap.lootSearch.sort.distance": "Distance" "seedMap.clickToCopy": "Click to copy" } From 267cfafe8f5165c1aa6a519b249d2dd1d5fb997c Mon Sep 17 00:00:00 2001 From: onion Date: Sun, 8 Feb 2026 08:23:37 -0500 Subject: [PATCH 6/9] fixed warnings --- .../dev/xpple/seedmapper/seedmap/LootSearchScreen.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java index 9e94b31e..5cb885cf 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java @@ -14,7 +14,6 @@ import dev.xpple.seedmapper.command.commands.LocateCommand; import dev.xpple.seedmapper.feature.StructureChecks; import dev.xpple.seedmapper.thread.SeedMapExecutor; -import dev.xpple.seedmapper.util.ComponentUtils; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; @@ -22,7 +21,6 @@ import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; -import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipPositioner; import net.minecraft.client.gui.screens.inventory.tooltip.DefaultTooltipPositioner; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.core.BlockPos; @@ -167,7 +165,6 @@ protected void init() { this.addRenderableWidget(this.sortToggleButton); int listLeft = LIST_PADDING; - int listTop = RESULTS_TOP; int listWidth = this.width / 2 - LIST_PADDING * 2; int searchY = this.height - LIST_PADDING - FIELD_HEIGHT; this.resultsSearchEditBox = new EditBox(this.font, listLeft, searchY, listWidth, FIELD_HEIGHT, Component.translatable("seedMap.lootSearch.filter")); @@ -197,7 +194,6 @@ private void renderSearchTab(GuiGraphics guiGraphics) { private void renderResultsTab(GuiGraphics guiGraphics, int mouseX, int mouseY) { int listLeft = LIST_PADDING; int listTop = RESULTS_TOP; - int listWidth = this.width / 2 - LIST_PADDING * 2; int listBottom = this.height - LIST_PADDING - FIELD_HEIGHT - ROW_SPACING; int listHeight = Math.max(0, listBottom - listTop); @@ -237,7 +233,6 @@ private void renderResultsTab(GuiGraphics guiGraphics, int mouseX, int mouseY) { int structureListTop = detailsTop + LIST_ROW_HEIGHT * 3; int structureListHeight = this.height - structureListTop - LIST_PADDING - TAB_HEIGHT - ROW_SPACING; - int structureListBottom = structureListTop + structureListHeight; List structures = this.selectedItem.sortedStructures(this.structureSortMode, this.playerPos); if (structures.isEmpty()) { @@ -394,7 +389,6 @@ private void startSearch() { int maxRegionZ = Mth.floor((centerZ + radiusBlocks) / (float) regionSizeBlocks); StructureChecks.GenerationCheck generationCheck = StructureChecks.getGenerationCheck(structure); - List positions = new ArrayList<>(); for (int regionX = minRegionX; regionX <= maxRegionX; regionX++) { for (int regionZ = minRegionZ; regionZ <= maxRegionZ; regionZ++) { if (Cubiomes.getStructurePos(structure, this.version, this.seed, regionX, regionZ, structurePos) == 0) { @@ -477,7 +471,6 @@ public boolean mouseClicked(MouseButtonEvent event, boolean isDoubleClick) { int listTop = RESULTS_TOP; int listWidth = this.width / 2 - LIST_PADDING * 2; int listBottom = this.height - LIST_PADDING - FIELD_HEIGHT - ROW_SPACING; - int listHeight = Math.max(0, listBottom - listTop); int listRight = listLeft + listWidth; //int listBottom = listTop + listHeight; if (mouseX < listLeft || mouseX > listRight || mouseY < listTop || mouseY > listBottom) { From 8aeba261dd817c6255c1e300c9fb662c03de0fd7 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Mon, 9 Feb 2026 15:26:01 +0100 Subject: [PATCH 7/9] Revert increased zoom + add back some padding --- .../dev/xpple/seedmapper/config/Configs.java | 7 +- .../seedmapper/seedmap/ItemIconButton.java | 25 ++ .../seedmapper/seedmap/LootSearchScreen.java | 59 ++-- .../seedmapper/seedmap/MinimapScreen.java | 2 +- .../seedmapper/seedmap/SeedMapScreen.java | 312 +++++------------- .../assets/seedmapper/lang/en_us.json | 4 +- 6 files changed, 138 insertions(+), 271 deletions(-) create mode 100644 src/main/java/dev/xpple/seedmapper/seedmap/ItemIconButton.java diff --git a/src/main/java/dev/xpple/seedmapper/config/Configs.java b/src/main/java/dev/xpple/seedmapper/config/Configs.java index 6e843f0a..e50d4bbe 100644 --- a/src/main/java/dev/xpple/seedmapper/config/Configs.java +++ b/src/main/java/dev/xpple/seedmapper/config/Configs.java @@ -16,7 +16,6 @@ import net.minecraft.client.Minecraft; import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.network.chat.Component; -import net.minecraft.util.Mth; import net.minecraft.util.Util; import java.time.Duration; @@ -81,10 +80,10 @@ private static void setSeedMapThreads(int seedMapThreads) { } @Config(setter = @Config.Setter("setPixelsPerBiome")) - public static float PixelsPerBiome = 4.0F; + public static int PixelsPerBiome = 4; - private static void setPixelsPerBiome(float pixelsPerBiome) { - PixelsPerBiome = Mth.clamp(pixelsPerBiome, SeedMapScreen.MIN_PIXELS_PER_BIOME, SeedMapScreen.MAX_PIXELS_PER_BIOME); + private static void setPixelsPerBiome(int pixelsPerBiome) { + PixelsPerBiome = Math.clamp(pixelsPerBiome, SeedMapScreen.MIN_PIXELS_PER_BIOME, SeedMapScreen.MAX_PIXELS_PER_BIOME); } @Config(setter = @Config.Setter("setMinimapOffsetX")) diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/ItemIconButton.java b/src/main/java/dev/xpple/seedmapper/seedmap/ItemIconButton.java new file mode 100644 index 00000000..5f6863a7 --- /dev/null +++ b/src/main/java/dev/xpple/seedmapper/seedmap/ItemIconButton.java @@ -0,0 +1,25 @@ +package dev.xpple.seedmapper.seedmap; + +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.Tooltip; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; + +public class ItemIconButton extends Button { + + public static final int ICON_SIZE = 16; + + private final ItemStack item; + + protected ItemIconButton(int x, int y, ItemStack item, Component message, OnPress onPress) { + super(x, y, ICON_SIZE, ICON_SIZE, message, onPress, DEFAULT_NARRATION); + this.item = item; + this.setTooltip(Tooltip.create(message)); + } + + @Override + protected void renderContents(GuiGraphics guiGraphics, int i, int j, float f) { + guiGraphics.renderItem(this.item, this.getX(), this.getY()); + } +} diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java index 22711595..746d8aee 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/LootSearchScreen.java @@ -38,6 +38,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Stream; import static dev.xpple.seedmapper.util.ChatBuilder.*; @@ -58,13 +59,8 @@ public class LootSearchScreen extends Screen { private static final int STRUCTURE_ICON_GAP = 4; private static final int STRUCTURE_BUTTON_ROWS = 2; - private final Screen parent; + private final SeedMapScreen parent; private final SeedMapExecutor executor = new SeedMapExecutor(); - private final long seed; - private final int dimension; - private final int version; - private final int generatorFlags; - private final BlockPos playerPos; private enum Tab { SEARCH, @@ -96,17 +92,12 @@ private enum SortMode { private @Nullable ItemResult selectedItem = null; private int structureScroll = 0; - public LootSearchScreen(Screen parent, long seed, int dimension, int version, int generatorFlags, BlockPos playerPos) { + public LootSearchScreen(SeedMapScreen parent) { super(Component.translatable("seedMap.lootSearch.title")); this.parent = parent; - this.seed = seed; - this.dimension = dimension; - this.version = version; - this.generatorFlags = generatorFlags; - this.playerPos = playerPos; - this.lootFeatures = List.of(MapFeature.values()).stream() + this.lootFeatures = Stream.of(MapFeature.values()) .filter(feature -> LocateCommand.LOOT_SUPPORTED_STRUCTURES.contains(feature.getStructureId())) - .filter(feature -> feature.getDimension() == this.dimension) + .filter(feature -> feature.getDimension() == this.parent.getDimension()) .toList(); this.lootFeatures.forEach(feature -> this.enabledStructureIds.add(feature.getStructureId())); } @@ -213,7 +204,7 @@ private void renderResultsTab(GuiGraphics guiGraphics, int mouseX, int mouseY) { for (int i = startIndex; i < endIndex; i++) { ItemResult result = filteredResults.get(i); int color = Objects.equals(this.selectedItem, result) ? 0xFF_FFFFFF : 0xFF_C0C0C0; - Component line = Component.literal("%s x%d".formatted(result.displayName(this.version), result.totalCount)); + Component line = Component.literal("%s x%d".formatted(result.displayName(this.parent.getVersion()), result.totalCount)); guiGraphics.drawString(this.font, line, listLeft, y, color); y += LIST_ROW_HEIGHT; } @@ -226,13 +217,13 @@ private void renderResultsTab(GuiGraphics guiGraphics, int mouseX, int mouseY) { return; } - guiGraphics.drawString(this.font, Component.literal(this.selectedItem.displayName(this.version)), detailsLeft, detailsTop, 0xFF_FFFFFF); + guiGraphics.drawString(this.font, Component.literal(this.selectedItem.displayName(this.parent.getVersion())), detailsLeft, detailsTop, 0xFF_FFFFFF); guiGraphics.drawString(this.font, Component.translatable("seedMap.lootSearch.total", this.selectedItem.totalCount), detailsLeft, detailsTop + LIST_ROW_HEIGHT, 0xFF_C0C0C0); int structureListTop = detailsTop + LIST_ROW_HEIGHT * 3; int structureListHeight = this.height - structureListTop - LIST_PADDING - TAB_HEIGHT - ROW_SPACING; - List structures = this.selectedItem.sortedStructures(this.structureSortMode, this.playerPos); + List structures = this.selectedItem.sortedStructures(this.structureSortMode, this.parent.getPlayerPos()); if (structures.isEmpty()) { guiGraphics.drawString(this.font, Component.translatable("seedMap.lootSearch.noStructures"), detailsLeft, structureListTop, 0xFF_A0A0A0); return; @@ -352,11 +343,11 @@ private void startSearch() { private @Nullable SearchResults searchLoot(int radiusBlocks) { try (Arena arena = Arena.ofConfined()) { MemorySegment generator = Generator.allocate(arena); - Cubiomes.setupGenerator(generator, this.version, this.generatorFlags); - Cubiomes.applySeed(generator, this.dimension, this.seed); + Cubiomes.setupGenerator(generator, this.parent.getVersion(), this.parent.getGeneratorFlags()); + Cubiomes.applySeed(generator, this.parent.getDimension(), this.parent.getSeed()); MemorySegment surfaceNoise = SurfaceNoise.allocate(arena); - Cubiomes.initSurfaceNoise(surfaceNoise, this.dimension, this.seed); + Cubiomes.initSurfaceNoise(surfaceNoise, this.parent.getDimension(), this.parent.getSeed()); SearchResults results = new SearchResults(); MemorySegment structurePos = Pos.allocate(arena); @@ -365,8 +356,8 @@ private void startSearch() { MemorySegment structureSaltConfig = StructureSaltConfig.allocate(arena); MemorySegment ltcPtr = arena.allocate(Cubiomes.C_POINTER); - int centerX = this.playerPos.getX(); - int centerZ = this.playerPos.getZ(); + int centerX = this.parent.getPlayerPos().getX(); + int centerZ = this.parent.getPlayerPos().getZ(); int radiusSq = radiusBlocks * radiusBlocks; for (int structure : LocateCommand.LOOT_SUPPORTED_STRUCTURES) { @@ -374,10 +365,10 @@ private void startSearch() { continue; } MemorySegment structureConfig = StructureConfig.allocate(arena); - if (Cubiomes.getStructureConfig(structure, this.version, structureConfig) == 0) { + if (Cubiomes.getStructureConfig(structure, this.parent.getVersion(), structureConfig) == 0) { continue; } - if (StructureConfig.dim(structureConfig) != this.dimension) { + if (StructureConfig.dim(structureConfig) != this.parent.getDimension()) { continue; } int regionSizeBlocks = StructureConfig.regionSize(structureConfig) << 4; @@ -389,7 +380,7 @@ private void startSearch() { StructureChecks.GenerationCheck generationCheck = StructureChecks.getGenerationCheck(structure); for (int regionX = minRegionX; regionX <= maxRegionX; regionX++) { for (int regionZ = minRegionZ; regionZ <= maxRegionZ; regionZ++) { - if (Cubiomes.getStructurePos(structure, this.version, this.seed, regionX, regionZ, structurePos) == 0) { + if (Cubiomes.getStructurePos(structure, this.parent.getVersion(), this.parent.getSeed(), regionX, regionZ, structurePos) == 0) { continue; } int posX = Pos.x(structurePos); @@ -403,12 +394,12 @@ private void startSearch() { continue; } int biome = Cubiomes.getBiomeAt(generator, 4, posX >> 2, 320 >> 2, posZ >> 2); - Cubiomes.getVariant(structureVariant, structure, this.version, this.seed, posX, posZ, biome); + Cubiomes.getVariant(structureVariant, structure, this.parent.getVersion(), this.parent.getSeed(), posX, posZ, biome); biome = StructureVariant.biome(structureVariant) != -1 ? StructureVariant.biome(structureVariant) : biome; - if (Cubiomes.getStructureSaltConfig(structure, this.version, biome, structureSaltConfig) == 0) { + if (Cubiomes.getStructureSaltConfig(structure, this.parent.getVersion(), biome, structureSaltConfig) == 0) { continue; } - int numPieces = Cubiomes.getStructurePieces(pieces, StructureChecks.MAX_END_CITY_AND_FORTRESS_PIECES, structure, structureSaltConfig, structureVariant, this.version, this.seed, posX, posZ); + int numPieces = Cubiomes.getStructurePieces(pieces, StructureChecks.MAX_END_CITY_AND_FORTRESS_PIECES, structure, structureSaltConfig, structureVariant, this.parent.getVersion(), this.parent.getSeed(), posX, posZ); if (numPieces <= 0) { continue; } @@ -422,7 +413,7 @@ private void startSearch() { MemorySegment lootSeeds = Piece.lootSeeds(piece); for (int chestIdx = 0; chestIdx < chestCount; chestIdx++) { MemorySegment lootTable = lootTables.getAtIndex(ValueLayout.ADDRESS, chestIdx).reinterpret(Long.MAX_VALUE); - if (Cubiomes.init_loot_table_name(ltcPtr, lootTable, this.version) == 0) { + if (Cubiomes.init_loot_table_name(ltcPtr, lootTable, this.parent.getVersion()) == 0) { continue; } MemorySegment lootTableContext = ltcPtr.get(ValueLayout.ADDRESS, 0).reinterpret(LootTableContext.sizeof()); @@ -450,9 +441,7 @@ private void startSearch() { public void onClose() { super.onClose(); this.executor.close(() -> {}); - if (this.minecraft != null) { - this.minecraft.setScreen(this.parent); - } + this.minecraft.setScreen(this.parent); } @Override @@ -475,7 +464,7 @@ public boolean mouseClicked(MouseButtonEvent event, boolean isDoubleClick) { return false; } List filteredResults = this.getFilteredResults(); - int index = this.resultsScroll + (int) ((mouseY - listTop) / LIST_ROW_HEIGHT); + int index = this.resultsScroll + (mouseY - listTop) / LIST_ROW_HEIGHT; if (index >= 0 && index < filteredResults.size()) { this.selectedItem = filteredResults.get(index); this.structureScroll = 0; @@ -512,7 +501,7 @@ public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, doubl int structureListRight = this.width - LIST_PADDING; int structureListBottom = structureListTop + structureListHeight; if (mouseX >= detailsLeft && mouseX <= structureListRight && mouseY >= structureListTop && mouseY <= structureListBottom && this.selectedItem != null) { - List structures = this.selectedItem.sortedStructures(this.structureSortMode, this.playerPos); + List structures = this.selectedItem.sortedStructures(this.structureSortMode, this.parent.getPlayerPos()); int visibleRows = Math.max(1, structureListHeight / LIST_ROW_HEIGHT); int maxScroll = Math.max(0, structures.size() - visibleRows); this.structureScroll = Mth.clamp(this.structureScroll + delta, 0, maxScroll); @@ -648,7 +637,7 @@ private List getFilteredResults() { return this.itemResults; } return this.itemResults.stream() - .filter(result -> result.displayName(this.version).toLowerCase().contains(query)) + .filter(result -> result.displayName(this.parent.getVersion()).toLowerCase().contains(query)) .toList(); } diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java index 55625889..7dc4ef3b 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java @@ -104,7 +104,7 @@ protected int horizontalPadding() { return Configs.MinimapOffsetX; } - //@Override + @Override protected int verticalPadding() { return Configs.MinimapOffsetY; } diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java index d861a332..733919ca 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java @@ -28,6 +28,7 @@ import dev.xpple.seedmapper.feature.StructureChecks; import dev.xpple.seedmapper.thread.SeedMapCache; import dev.xpple.seedmapper.thread.SeedMapExecutor; +import dev.xpple.seedmapper.util.ComponentUtils; import dev.xpple.seedmapper.util.QuartPos2; import dev.xpple.seedmapper.util.QuartPos2f; import dev.xpple.seedmapper.util.RegionPos; @@ -54,8 +55,6 @@ import net.minecraft.client.gui.render.TextureSetup; import net.minecraft.client.gui.render.state.BlitRenderState; import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; -import net.minecraft.client.gui.screens.inventory.tooltip.DefaultTooltipPositioner; import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; import net.minecraft.client.player.LocalPlayer; @@ -78,6 +77,7 @@ import net.minecraft.util.RandomSource; import net.minecraft.world.SimpleContainer; import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; import net.minecraft.world.item.alchemy.PotionContents; import net.minecraft.world.item.component.ItemLore; import net.minecraft.world.item.component.SuspiciousStewEffects; @@ -152,29 +152,17 @@ public class SeedMapScreen extends Screen { public static final int BIOME_SCALE = 4; public static final int SCALED_CHUNK_SIZE = LevelChunkSection.SECTION_WIDTH / BIOME_SCALE; - private static final int HORIZONTAL_PADDING = 50; - //private static final int VERTICAL_PADDING = 0; - public static final float MIN_PIXELS_PER_BIOME = 0.25F; - public static final float MAX_PIXELS_PER_BIOME = 100.0F; - - private static final int VERTICAL_LOWER_PADDING = 20; + private static final int LEFT_HORIZONTAL_PADDING = 50; + private static final int TOP_VERTICAL_PADDING = 20; + public static final int MIN_PIXELS_PER_BIOME = 1; + public static final int MAX_PIXELS_PER_BIOME = 100; private static final int HORIZONTAL_FEATURE_TOGGLE_SPACING = 1; private static final int VERTICAL_FEATURE_TOGGLE_SPACING = 1; - private static final Identifier SEED_ICON_TEXTURE = Identifier.fromNamespaceAndPath("minecraft", "textures/item/wheat_seeds.png"); - private static final int SEED_ICON_SIZE = 16; - private static final int SEED_ICON_PADDING = 4; - // can't find the chest texture - private static final Identifier LOOT_SEARCH_ICON_TEXTURE = Identifier.fromNamespaceAndPath("minecraft", "textures/item/chest_minecart.png"); - private static final int LOOT_SEARCH_ICON_SIZE = 16; - private static final float MIN_STRUCTURE_REGION_PIXELS = 8.0F; - private static final float MIN_CHUNK_PIXELS = 4.0F; - - //private static final int TELEPORT_FIELD_WIDTH = 70; - //private static final int WAYPOINT_NAME_FIELD_WIDTH = 100; + private static final net.minecraft.world.item.ItemStack LOOT_SEARCH_ICON_ITEM = Items.CHEST.getDefaultInstance(); - private static float tileSizePixels() { + private static int tileSizePixels() { return TilePos.TILE_SIZE_CHUNKS * SCALED_CHUNK_SIZE * Configs.PixelsPerBiome; } @@ -230,9 +218,9 @@ private static float tileSizePixels() { private int seedMapWidth; private int seedMapHeight; - // A list of all features that can have their appearance on the map toggled on or off. + /// A list of all features that can have their appearance on the map toggled on or off. private final List toggleableFeatures; - // The combined height of the images of *all* toggleable features. Includes padding. + /// The combined height of the icons of _all_ toggleable features. Includes padding. private final int featureIconsCombinedHeight; private final ObjectSet featureWidgets = new ObjectOpenHashSet<>(); @@ -241,9 +229,6 @@ private static float tileSizePixels() { private int displayCoordinatesCopiedTicks = 0; - private int lastMouseX = 0; - private int lastMouseY = 0; - private EditBox teleportEditBoxX; private EditBox teleportEditBoxZ; @@ -343,67 +328,56 @@ protected void init() { this.centerY = this.height / 2; this.seedMapWidth = this.width - this.horizontalPadding(); - this.seedMapHeight = this.height - VERTICAL_LOWER_PADDING; + this.seedMapHeight = this.height - this.verticalPadding(); if (!this.isMinimap()) { this.createFeatureToggles(); this.createTeleportField(); this.createWaypointNameField(); + ItemIconButton lootSearchButton = new ItemIconButton(this.width - ItemIconButton.ICON_SIZE - 1, this.verticalPadding() - ItemIconButton.ICON_SIZE - 1, LOOT_SEARCH_ICON_ITEM, Component.literal("Loot Search"), button -> { + this.minecraft.setScreen(new LootSearchScreen(SeedMapScreen.this)); + }); + this.addRenderableWidget(lootSearchButton); + this.enchantmentsRegistry = this.minecraft.player.registryAccess().lookupOrThrow(Registries.ENCHANTMENT); this.mobEffectRegistry = this.minecraft.player.registryAccess().lookupOrThrow(Registries.MOB_EFFECT); } - } + } @Override public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { - this.lastMouseX = mouseX; - this.lastMouseY = mouseY; // draw title - //Component seedComponent = Component.translatable("seedMap.seed", accent(Long.toString(this.seed)), Cubiomes.mc2str(this.version).getString(0), ComponentUtils.formatGeneratorFlags(this.generatorFlags)); - //guiGraphics.drawString(this.font, seedComponent, this.horizontalPadding(), this.verticalPadding() - this.font.lineHeight - 1, -1); + Component seedComponent = Component.translatable("seedMap.seed", accent(Long.toString(this.seed)), Cubiomes.mc2str(this.version).getString(0), ComponentUtils.formatGeneratorFlags(this.generatorFlags)); + guiGraphics.drawString(this.font, seedComponent, this.horizontalPadding(), this.verticalPadding() - this.font.lineHeight - 1, -1); this.renderBiomes(guiGraphics, mouseX, mouseY, partialTick); guiGraphics.nextStratum(); this.renderFeatures(guiGraphics, mouseX, mouseY, partialTick); - // draw hovered coordinates - //MutableComponent coordinates = accent("x: %d, z: %d".formatted(QuartPos.toBlock(this.mouseQuart.x()), QuartPos.toBlock(this.mouseQuart.z()))); - //if (this.displayCoordinatesCopiedTicks > 0) { - // coordinates = Component.translatable("seedMap.coordinatesCopied", coordinates); - //} - //guiGraphics.drawString(this.font, coordinates, this.horizontalPadding(), this.verticalPadding() + this.seedMapHeight + 1, -1); - if (!this.isMinimap()) { - this.teleportEditBoxX.setHint(Component.literal("X: %d".formatted(QuartPos.toBlock(this.mouseQuart.x())))); - this.teleportEditBoxZ.setHint(Component.literal("Z: %d".formatted(QuartPos.toBlock(this.mouseQuart.z())))); - } + this.teleportEditBoxX.setHint(Component.literal("X: %d".formatted(QuartPos.toBlock(this.mouseQuart.x())))); + this.teleportEditBoxZ.setHint(Component.literal("Z: %d".formatted(QuartPos.toBlock(this.mouseQuart.z())))); - if (this.isMouseOverMap(this.lastMouseX, this.lastMouseY) - && !this.isMouseOverSeedWidget(this.lastMouseX, this.lastMouseY) - && !this.isMouseOverLootSearchWidget(this.lastMouseX, this.lastMouseY)) { - OptionalInt optionalBiome = getBiome(this.mouseQuart); + if (this.displayCoordinatesCopiedTicks > 0) { + guiGraphics.setTooltipForNextFrame(Component.translatable("seedMap.coordinatesCopied"), mouseX, mouseY); + } else if (this.isMouseOverMap(mouseX, mouseY)) { + OptionalInt optionalBiome = this.getBiome(this.mouseQuart); if (optionalBiome.isPresent()) { - Component tooltip = Component.literal(Cubiomes.biome2str(this.version, optionalBiome.getAsInt()).getString(0)); - List tooltips = List.of(ClientTooltipComponent.create(tooltip.getVisualOrderText())); - guiGraphics.renderTooltip(this.font, tooltips, this.lastMouseX, this.lastMouseY, DefaultTooltipPositioner.INSTANCE, null); + guiGraphics.setTooltipForNextFrame(Component.literal(Cubiomes.biome2str(this.version, optionalBiome.getAsInt()).getString(0)), mouseX, mouseY); } } super.render(guiGraphics, mouseX, mouseY, partialTick); - - guiGraphics.nextStratum(); - this.drawSeedWidget(guiGraphics); - this.drawLootSearchWidget(guiGraphics); } @Override public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { - // No background; the map fills the screen. + // no background } protected void renderBiomes(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { - float tileSizePixels = tileSizePixels(); - int horTileRadius = Mth.ceil(this.seedMapWidth / tileSizePixels) + 1; - int verTileRadius = Mth.ceil(this.seedMapHeight / tileSizePixels) + 1; + int tileSizePixels = tileSizePixels(); + int horTileRadius = Math.ceilDiv(this.seedMapWidth, tileSizePixels) + 1; + int verTileRadius = Math.ceilDiv(this.seedMapHeight, tileSizePixels) + 1; TilePos centerTile = TilePos.fromQuartPos(QuartPos2.fromQuartPos2f(this.centerQuart)); for (int relTileX = -horTileRadius; relTileX <= horTileRadius; relTileX++) { @@ -418,9 +392,7 @@ protected void renderBiomes(GuiGraphics guiGraphics, int mouseX, int mouseY, flo } // compute slime chunks and store in texture - if (this.shouldRenderFeature(MapFeature.SLIME_CHUNK) - && this.toggleableFeatures.contains(MapFeature.SLIME_CHUNK) - && Configs.ToggledFeatures.contains(MapFeature.SLIME_CHUNK)) { + if (this.toggleableFeatures.contains(MapFeature.SLIME_CHUNK) && Configs.ToggledFeatures.contains(MapFeature.SLIME_CHUNK)) { BitSet slimeChunkData = this.slimeChunkCache.computeIfAbsent(tilePos, this::calculateSlimeChunkData); if (slimeChunkData != null) { Tile tile = this.slimeChunkTileCache.computeIfAbsent(tilePos, _ -> this.createSlimeChunkTile(tilePos, slimeChunkData)); @@ -433,19 +405,18 @@ protected void renderBiomes(GuiGraphics guiGraphics, int mouseX, int mouseY, flo protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { this.featureWidgets.clear(); - float tileSizePixels = tileSizePixels(); - int horTileRadius = Mth.ceil(this.seedMapWidth / tileSizePixels) + 1; - int verTileRadius = Mth.ceil(this.seedMapHeight / tileSizePixels) + 1; + int tileSizePixels = tileSizePixels(); + int horTileRadius = Math.ceilDiv(this.seedMapWidth, tileSizePixels) + 1; + int verTileRadius = Math.ceilDiv(this.seedMapHeight, tileSizePixels) + 1; TilePos centerTile = TilePos.fromQuartPos(QuartPos2.fromQuartPos2f(this.centerQuart)); - int horChunkRadius = Mth.ceil((this.seedMapWidth / 2.0F) / (SCALED_CHUNK_SIZE * Configs.PixelsPerBiome)); - int verChunkRadius = Mth.ceil((this.seedMapHeight / 2.0F) / (SCALED_CHUNK_SIZE * Configs.PixelsPerBiome)); + int horChunkRadius = Math.ceilDiv(this.seedMapWidth / 2, SCALED_CHUNK_SIZE * Configs.PixelsPerBiome); + int verChunkRadius = Math.ceilDiv(this.seedMapHeight / 2, SCALED_CHUNK_SIZE * Configs.PixelsPerBiome); // compute structures Configs.ToggledFeatures.stream() .filter(this.toggleableFeatures::contains) - .filter(this::shouldRenderFeature) .filter(f -> f.getStructureId() != -1) .forEach(feature -> { int structure = feature.getStructureId(); @@ -480,9 +451,7 @@ protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, f guiGraphics.nextStratum(); // draw strongholds - if (this.shouldRenderFeature(MapFeature.STRONGHOLD) - && this.toggleableFeatures.contains(MapFeature.STRONGHOLD) - && Configs.ToggledFeatures.contains(MapFeature.STRONGHOLD)) { + if (this.toggleableFeatures.contains(MapFeature.STRONGHOLD) && Configs.ToggledFeatures.contains(MapFeature.STRONGHOLD)) { TwoDTree tree = strongholdDataCache.get(this.worldIdentifier); if (tree != null) { for (BlockPos strongholdPos : tree) { @@ -492,8 +461,7 @@ protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, f } // compute ore veins - if ((this.shouldRenderFeature(MapFeature.COPPER_ORE_VEIN) || this.shouldRenderFeature(MapFeature.IRON_ORE_VEIN)) - && (this.toggleableFeatures.contains(MapFeature.COPPER_ORE_VEIN) || this.toggleableFeatures.contains(MapFeature.IRON_ORE_VEIN)) + if ((this.toggleableFeatures.contains(MapFeature.COPPER_ORE_VEIN) || this.toggleableFeatures.contains(MapFeature.IRON_ORE_VEIN)) && (Configs.ToggledFeatures.contains(MapFeature.COPPER_ORE_VEIN) || Configs.ToggledFeatures.contains(MapFeature.IRON_ORE_VEIN))) { for (int relTileX = -horTileRadius; relTileX <= horTileRadius; relTileX++) { for (int relTileZ = -verTileRadius; relTileZ <= verTileRadius; relTileZ++) { @@ -510,8 +478,7 @@ protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, f } // compute canyons - if (this.shouldRenderFeature(MapFeature.CANYON) - && (this.toggleableFeatures.contains(MapFeature.CANYON)) && Configs.ToggledFeatures.contains(MapFeature.CANYON)) { + if ((this.toggleableFeatures.contains(MapFeature.CANYON)) && Configs.ToggledFeatures.contains(MapFeature.CANYON)) { for (int relTileX = -horTileRadius; relTileX <= horTileRadius; relTileX++) { for (int relTileZ = -verTileRadius; relTileZ <= verTileRadius; relTileZ++) { TilePos tilePos = new TilePos(centerTile.x() + relTileX, centerTile.z() + relTileZ); @@ -529,9 +496,7 @@ protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, f } // draw waypoints - if (this.shouldRenderFeature(MapFeature.WAYPOINT) - && this.toggleableFeatures.contains(MapFeature.WAYPOINT) - && Configs.ToggledFeatures.contains(MapFeature.WAYPOINT)) { + if (this.toggleableFeatures.contains(MapFeature.WAYPOINT) && Configs.ToggledFeatures.contains(MapFeature.WAYPOINT)) { SimpleWaypointsAPI waypointsApi = SimpleWaypointsAPI.getInstance(); String identifier = waypointsApi.getWorldIdentifier(this.minecraft); Map worldWaypoints = waypointsApi.getWorldWaypoints(identifier); @@ -587,38 +552,37 @@ protected void renderFeatures(GuiGraphics guiGraphics, int mouseX, int mouseY, f private void drawTile(GuiGraphics guiGraphics, Tile tile) { TilePos tilePos = tile.pos(); QuartPos2f relTileQuart = QuartPos2f.fromQuartPos(QuartPos2.fromTilePos(tilePos)).subtract(this.centerQuart); - float tileSizePixels = tileSizePixels(); + int tileSizePixels = tileSizePixels(); int minX = this.centerX + Mth.floor(Configs.PixelsPerBiome * relTileQuart.x()); int minY = this.centerY + Mth.floor(Configs.PixelsPerBiome * relTileQuart.z()); - int maxX = minX + Mth.ceil(tileSizePixels); - int maxY = minY + Mth.ceil(tileSizePixels); + int maxX = minX + tileSizePixels; + int maxY = minY + tileSizePixels; if (maxX < this.horizontalPadding() || minX > this.horizontalPadding() + this.seedMapWidth) { return; } - if (maxY < 0 || minY > this.seedMapHeight()) { + if (maxY < this.verticalPadding() || minY > this.verticalPadding() + this.seedMapHeight) { return; } float u0, u1, v0, v1; if (minX < this.horizontalPadding()) { - u0 = (this.horizontalPadding() - minX) / tileSizePixels; + u0 = (float) (this.horizontalPadding() - minX) / tileSizePixels; minX = this.horizontalPadding(); } else u0 = 0; if (maxX > this.horizontalPadding() + this.seedMapWidth) { - u1 = 1 - ((maxX - this.horizontalPadding() - this.seedMapWidth) / tileSizePixels); + u1 = 1 - ((float) (maxX - this.horizontalPadding() - this.seedMapWidth) / tileSizePixels); maxX = this.horizontalPadding() + this.seedMapWidth; } else u1 = 1; - if (minY < 0) { - v0 = (0 - minY) / tileSizePixels; - minY = 0; + if (minY < this.verticalPadding()) { + v0 = (float) (this.verticalPadding() - minY) / tileSizePixels; + minY = this.verticalPadding(); } else v0 = 0; - if (maxY > this.seedMapHeight()) { - v1 = 1 - ((maxY - this.seedMapHeight()) / tileSizePixels); - maxY = this.seedMapHeight(); + if (maxY > this.verticalPadding() + this.seedMapHeight) { + v1 = 1 - ((float) (maxY - this.verticalPadding() - this.seedMapHeight) / tileSizePixels); + maxY = this.verticalPadding() + this.seedMapHeight; } else v1 = 1; - //tile.texture().setFilter(Configs.PixelsPerBiome < 1.0F, false); guiGraphics.submitBlit(RenderPipelines.GUI_TEXTURED, tile.texture().getTextureView(), tile.texture().getSampler(), minX, minY, maxX, maxY, u0, u1, v0, v1, 0xFF_FFFFFF); } @@ -679,9 +643,7 @@ private void drawFeatureIcons(GuiGraphics guiGraphics) { } protected void drawPlayerIndicator(GuiGraphics guiGraphics) { - if (!this.shouldRenderFeature(MapFeature.PLAYER_ICON) - || !this.toggleableFeatures.contains(MapFeature.PLAYER_ICON) - || !Configs.ToggledFeatures.contains(MapFeature.PLAYER_ICON)) { + if (!this.toggleableFeatures.contains(MapFeature.PLAYER_ICON) || !Configs.ToggledFeatures.contains(MapFeature.PLAYER_ICON)) { return; } QuartPos2f relPlayerQuart = QuartPos2f.fromQuartPos(QuartPos2.fromBlockPos(this.playerPos)).subtract(this.centerQuart); @@ -689,7 +651,7 @@ protected void drawPlayerIndicator(GuiGraphics guiGraphics) { int playerMinY = this.centerY + Mth.floor(Configs.PixelsPerBiome * relPlayerQuart.z()) - 10; int playerMaxX = playerMinX + 20; int playerMaxY = playerMinY + 20; - if (playerMinX < this.horizontalPadding() || playerMaxX > this.horizontalPadding() + this.seedMapWidth || playerMinY < 0 || playerMaxY > this.seedMapHeight()) { + if (playerMinX < this.horizontalPadding() || playerMaxX > this.horizontalPadding() + this.seedMapWidth || playerMinY < this.verticalPadding() || playerMaxY > this.verticalPadding() + this.seedMapHeight) { return; } PlayerFaceRenderer.draw(guiGraphics, this.minecraft.player.getSkin(), playerMinX, playerMinY, 20); @@ -708,106 +670,16 @@ protected void drawDirectionArrow(GuiGraphics guiGraphics, int playerMinX, int p ; boolean withinBounds = Stream.of(new Vector2f(20, 0), new Vector2f(20, 20), new Vector2f(0, 20), new Vector2f(0, 0)) .map(transform::transformPosition) - .allMatch(v -> v.x >= this.horizontalPadding() && v.x <= this.horizontalPadding() + this.seedMapWidth && - v.y >= 0 && v.y <= this.seedMapHeight()); + .allMatch(v -> isMouseOverMap(v.x, v.y)); if (withinBounds) { drawIconStatic(guiGraphics, DIRECTION_ARROW_TEXTURE, 0, 0, 20, 20, 0xFF_FFFFFF); } guiGraphics.pose().popMatrix(); } - // Draw a seed icon in the bottom-right which shows the seed in a tooltip when hovered. - protected void drawSeedWidget(GuiGraphics guiGraphics) { - int minX = this.getSeedWidgetMinX(); - int minY = this.getSeedWidgetMinY(); - int maxX = minX + SEED_ICON_SIZE; - int maxY = minY + SEED_ICON_SIZE; - - drawIconStatic(guiGraphics, SEED_ICON_TEXTURE, minX, minY, SEED_ICON_SIZE, SEED_ICON_SIZE, 0xFF_FFFFFF); - - if (this.lastMouseX < minX || this.lastMouseX > maxX || this.lastMouseY < minY || this.lastMouseY > maxY) { - return; - } - - Component tooltip = Component.literal(Long.toString(this.seed)); - List tooltips = List.of(ClientTooltipComponent.create(tooltip.getVisualOrderText()), ClientTooltipComponent.create(Component.translatable("seedMap.clickToCopy").getVisualOrderText())); - guiGraphics.renderTooltip(this.font, tooltips, this.lastMouseX, this.lastMouseY, DefaultTooltipPositioner.INSTANCE, null); - } - - protected void drawLootSearchWidget(GuiGraphics guiGraphics) { - int minX = this.getLootSearchWidgetMinX(); - int minY = this.getLootSearchWidgetMinY(); - int maxX = minX + LOOT_SEARCH_ICON_SIZE; - int maxY = minY + LOOT_SEARCH_ICON_SIZE; - - drawIconStatic(guiGraphics, LOOT_SEARCH_ICON_TEXTURE, minX, minY, LOOT_SEARCH_ICON_SIZE, LOOT_SEARCH_ICON_SIZE, 0xFF_FFFFFF); - - if (this.lastMouseX < minX || this.lastMouseX > maxX || this.lastMouseY < minY || this.lastMouseY > maxY) { - return; - } - - Component tooltip = Component.literal("Loot search"); - List tooltips = List.of(ClientTooltipComponent.create(tooltip.getVisualOrderText())); - guiGraphics.renderTooltip(this.font, tooltips, this.lastMouseX, this.lastMouseY, DefaultTooltipPositioner.INSTANCE, null); - } - - private int getSeedWidgetMinX() { - return this.width - SEED_ICON_SIZE - SEED_ICON_PADDING; - } - - private int getSeedWidgetMinY() { - return SEED_ICON_PADDING; - } - - private int getLootSearchWidgetMinX() { - return this.getSeedWidgetMinX() - LOOT_SEARCH_ICON_SIZE - SEED_ICON_PADDING; - } - - private int getLootSearchWidgetMinY() { - return this.getSeedWidgetMinY(); - } - private boolean isMouseOverMap(double mouseX, double mouseY) { - return mouseX >= this.horizontalPadding() - && mouseX <= this.horizontalPadding() + this.seedMapWidth - && mouseY >= 0 - && mouseY <= this.seedMapHeight(); - } - - private boolean isMouseOverSeedWidget(double mouseX, double mouseY) { - int minX = this.getSeedWidgetMinX(); - int minY = this.getSeedWidgetMinY(); - int maxX = minX + SEED_ICON_SIZE; - int maxY = minY + SEED_ICON_SIZE; - return mouseX >= minX && mouseX <= maxX && mouseY >= minY && mouseY <= maxY; - } - - private boolean isMouseOverLootSearchWidget(double mouseX, double mouseY) { - int minX = this.getLootSearchWidgetMinX(); - int minY = this.getLootSearchWidgetMinY(); - int maxX = minX + LOOT_SEARCH_ICON_SIZE; - int maxY = minY + LOOT_SEARCH_ICON_SIZE; - return mouseX >= minX && mouseX <= maxX && mouseY >= minY && mouseY <= maxY; - } - - private boolean shouldRenderFeature(MapFeature feature) { - if (feature == MapFeature.WAYPOINT || feature == MapFeature.WORLD_SPAWN || feature == MapFeature.PLAYER_ICON) { - return true; - } - if (feature == MapFeature.SLIME_CHUNK || feature == MapFeature.CANYON || feature == MapFeature.COPPER_ORE_VEIN || feature == MapFeature.IRON_ORE_VEIN) { - float pixelsPerChunk = SCALED_CHUNK_SIZE * Configs.PixelsPerBiome; - return pixelsPerChunk >= MIN_CHUNK_PIXELS; - } - int structureId = feature.getStructureId(); - if (structureId != -1 && this.structureConfigs != null) { - MemorySegment structureConfig = this.structureConfigs[structureId]; - if (structureConfig != null) { - int regionSize = StructureConfig.regionSize(structureConfig); - float pixelsPerRegion = regionSize * SCALED_CHUNK_SIZE * Configs.PixelsPerBiome; - return pixelsPerRegion >= MIN_STRUCTURE_REGION_PIXELS; - } - } - return true; + return mouseX >= this.horizontalPadding() && mouseX <= this.horizontalPadding() + this.seedMapWidth + && mouseY >= this.verticalPadding() && mouseY <= this.verticalPadding() + this.seedMapHeight; } // Formerly, this laid the feature toggles out in rows above the map. @@ -823,13 +695,13 @@ private void createFeatureToggles() { int togglesPerColumn = Math.ceilDiv(this.toggleableFeatures.size(), columns); int row = 0; - int iconLeftX = 0, iconTopY = 0; + int iconLeftX = 0, iconTopY = this.verticalPadding(); int maxIconWidth = 0; for (MapFeature toggleableFeature : this.toggleableFeatures) { // Draw the icon. int iconHeight = (int)Math.ceil(scale*toggleableFeature.getDefaultTexture().height()); this.addRenderableWidget(new FeatureToggleWidget(toggleableFeature, iconLeftX, iconTopY, scale)); - + // Set up the position for where to draw the next icon. iconTopY += iconHeight + (int)Math.ceil(scale*VERTICAL_FEATURE_TOGGLE_SPACING); maxIconWidth = Math.max(maxIconWidth, (int)Math.ceil(scale*toggleableFeature.getDefaultTexture().width())); @@ -838,7 +710,7 @@ private void createFeatureToggles() { if (row >= togglesPerColumn) { // Begin a new column. row = 0; - iconTopY = 0; + iconTopY = this.verticalPadding(); iconLeftX += maxIconWidth + Math.ceil(scale*HORIZONTAL_FEATURE_TOGGLE_SPACING); maxIconWidth = 0; } @@ -858,7 +730,7 @@ private Pair computeFeatureToggleScale() { /// TODO: enforce textures being squares of the same size. int maxColumnHeight = this.featureIconsCombinedHeight / columns; - double scaleX = (double)HORIZONTAL_PADDING / (columns * (baseColumnWidth + HORIZONTAL_FEATURE_TOGGLE_SPACING)); + double scaleX = (double)LEFT_HORIZONTAL_PADDING / (columns * (baseColumnWidth + HORIZONTAL_FEATURE_TOGGLE_SPACING)); double scaleY = (double)this.height * FEATURE_TOGGLE_LOWER_PADDING_FACTOR / maxColumnHeight; double scale = Math.min(scaleX, scaleY); @@ -914,7 +786,7 @@ private BitSet calculateSlimeChunkData(TilePos tilePos) { } BlockPos pos = new BlockPos(Pos.x(structurePos), 0, Pos.z(structurePos)); - OptionalInt optionalBiome = getBiome(QuartPos2.fromBlockPos(pos)); + OptionalInt optionalBiome = this.getBiome(QuartPos2.fromBlockPos(pos)); MapFeature.Texture texture; if (optionalBiome.isEmpty()) { texture = feature.getDefaultTexture(); @@ -957,7 +829,7 @@ private BitSet calculateSlimeChunkData(TilePos tilePos) { private BitSet calculateCanyonData(TilePos tilePos) { ToIntBiFunction biomeFunction; if (this.version <= Cubiomes.MC_1_17()) { - biomeFunction = (chunkX, chunkZ) -> getBiome(new QuartPos2(QuartPos.fromSection(chunkX), QuartPos.fromSection(chunkZ))).orElseGet(() -> Cubiomes.getBiomeAt(this.biomeGenerator, 4, chunkX << 2, 0, chunkZ << 2)); + biomeFunction = (chunkX, chunkZ) -> this.getBiome(new QuartPos2(QuartPos.fromSection(chunkX), QuartPos.fromSection(chunkZ))).orElseGet(() -> Cubiomes.getBiomeAt(this.biomeGenerator, 4, chunkX << 2, 0, chunkZ << 2)); } else { biomeFunction = (_, _) -> -1; } @@ -1074,28 +946,18 @@ public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, doubl return true; } - float oldPixelsPerBiome = Configs.PixelsPerBiome; - float currentScroll = Mth.clamp(oldPixelsPerBiome / MAX_PIXELS_PER_BIOME, MIN_PIXELS_PER_BIOME / MAX_PIXELS_PER_BIOME, 1.0F); - currentScroll = Mth.clamp(currentScroll - (float) (-scrollY / MAX_PIXELS_PER_BIOME), MIN_PIXELS_PER_BIOME / MAX_PIXELS_PER_BIOME, 1.0F); - float newPixelsPerBiome = currentScroll * MAX_PIXELS_PER_BIOME; - Configs.PixelsPerBiome = newPixelsPerBiome; + float currentScroll = Mth.clamp((float) Configs.PixelsPerBiome / MAX_PIXELS_PER_BIOME, 0.0F, 1.0F); + currentScroll = Mth.clamp(currentScroll - (float) (-scrollY / MAX_PIXELS_PER_BIOME), 0.0F, 1.0F); - if (this.isMouseOverMap(mouseX, mouseY)) { - float relXQuartOld = (float) ((mouseX - this.centerX) / oldPixelsPerBiome); - float relZQuartOld = (float) ((mouseY - this.centerY) / oldPixelsPerBiome); - QuartPos2f worldQuart = this.centerQuart.add(relXQuartOld, relZQuartOld); - float relXQuartNew = (float) ((mouseX - this.centerX) / newPixelsPerBiome); - float relZQuartNew = (float) ((mouseY - this.centerY) / newPixelsPerBiome); - this.moveCenter(worldQuart.subtract(new QuartPos2f(relXQuartNew, relZQuartNew))); - } else { - this.featureWidgets.removeIf(widget -> { - widget.updatePosition(); - return !widget.withinBounds(); - }); + Configs.PixelsPerBiome = Math.max((int) (currentScroll * MAX_PIXELS_PER_BIOME + 0.5), MIN_PIXELS_PER_BIOME); - if (this.markerWidget != null) { - this.markerWidget.updatePosition(); - } + this.featureWidgets.removeIf(widget -> { + widget.updatePosition(); + return !widget.withinBounds(); + }); + + if (this.markerWidget != null) { + this.markerWidget.updatePosition(); } return true; } @@ -1124,14 +986,6 @@ public boolean mouseClicked(MouseButtonEvent mouseButtonEvent, boolean doubleCli if (super.mouseClicked(mouseButtonEvent, doubleClick)) { return true; } - if (mouseButtonEvent.button() == InputConstants.MOUSE_BUTTON_LEFT && this.isMouseOverSeedWidget(mouseButtonEvent.x(), mouseButtonEvent.y())) { - this.minecraft.keyboardHandler.setClipboard(Long.toString(this.seed)); - return true; - } - if (mouseButtonEvent.button() == InputConstants.MOUSE_BUTTON_LEFT && this.isMouseOverLootSearchWidget(mouseButtonEvent.x(), mouseButtonEvent.y())) { - this.minecraft.setScreen(new LootSearchScreen(this, this.seed, this.dimension, this.version, this.generatorFlags, this.playerPos)); - return true; - } int button = mouseButtonEvent.button(); if (this.chestLootWidget != null && this.chestLootWidget.mouseClicked(mouseButtonEvent, doubleClick)) { return true; @@ -1157,7 +1011,7 @@ private boolean handleMapFeatureLeftClicked(MouseButtonEvent mouseButtonEvent, b } double mouseX = mouseButtonEvent.x(); double mouseY = mouseButtonEvent.y(); - if (mouseX < this.horizontalPadding() || mouseX > this.horizontalPadding() + this.seedMapWidth || mouseY < 0 || mouseY > this.seedMapHeight()) { + if (!this.isMouseOverMap(mouseX, mouseY)) { return false; } Optional optionalFeatureWidget = this.featureWidgets.stream() @@ -1429,10 +1283,10 @@ public boolean withinBounds() { int maxX = minX + this.width(); int maxY = minY + this.height(); - if (maxX >= horizontalPadding() + seedMapWidth || maxY >= seedMapHeight()) { + if (maxX >= horizontalPadding() + seedMapWidth || maxY >= verticalPadding() + seedMapHeight) { return false; } - if (minX < horizontalPadding() || minY < 0) { + if (minX < horizontalPadding() || minY < verticalPadding()) { return false; } return true; @@ -1498,20 +1352,20 @@ protected void updatePlayerRotation(Vec2 vec2) { this.playerRotation = vec2; } + public BlockPos getPlayerPos() { + return this.playerPos; + } + public Vec2 getPlayerRotation() { return this.playerRotation; } protected int horizontalPadding() { - return HORIZONTAL_PADDING; + return LEFT_HORIZONTAL_PADDING; } - //protected int verticalPadding() { - // return VERTICAL_PADDING; - //} - - protected int seedMapHeight() { - return this.height - VERTICAL_LOWER_PADDING; + protected int verticalPadding() { + return TOP_VERTICAL_PADDING; } protected long getSeed() { diff --git a/src/main/resources/assets/seedmapper/lang/en_us.json b/src/main/resources/assets/seedmapper/lang/en_us.json index ced08325..d69a3c42 100644 --- a/src/main/resources/assets/seedmapper/lang/en_us.json +++ b/src/main/resources/assets/seedmapper/lang/en_us.json @@ -110,7 +110,7 @@ "seedMap.chestLoot.extraInfo.lootTable": "Loot table: %s", "seedMap.chestLoot.extraInfo.lootSeed": "Loot seed: %s", "seedMap.chestLoot.stewEffect": "%s (%s seconds)", - "seedMap.coordinatesCopied": "%s (coordinates copied!)", + "seedMap.coordinatesCopied": "Coordinates copied!", "seedMap.teleportEditBoxX": "Edit box X coordinate", "seedMap.teleportEditBoxZ": "Edit box Z coordinate", "seedMap.waypointNameEditBox": "Waypoint name edit box", @@ -139,7 +139,7 @@ "seedMap.lootSearch.foundTypes": "Found %d item types.", "seedMap.lootSearch.sort": "Sort: %s", "seedMap.lootSearch.sort.count": "Count", - "seedMap.lootSearch.sort.distance": "Distance" + "seedMap.lootSearch.sort.distance": "Distance", "seedMap.clickToCopy": "Click to copy" } From 3f2d55ff78b811be7573f197b912956915f803b8 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Mon, 9 Feb 2026 16:24:36 +0100 Subject: [PATCH 8/9] Properly compute vertical size of map + store rendered features in list instead of hash set --- .../seedmapper/seedmap/MinimapScreen.java | 14 +-- .../seedmapper/seedmap/SeedMapScreen.java | 116 ++++++++---------- 2 files changed, 58 insertions(+), 72 deletions(-) diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java index 7dc4ef3b..d600fae8 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java @@ -42,18 +42,18 @@ public void renderToHud(GuiGraphics guiGraphics, float partialTick) { renderContentHeight = diagonal; } // ensures super.seedMapWidth == renderContentWidth - int renderWidth = renderContentWidth + 2 * this.horizontalPadding(); + int renderWidth = renderContentWidth + 2 * this.leftHorizontalPadding(); // ensures super.seedMapHeight == renderContentHeight - int renderHeight = renderContentHeight + 2 * this.verticalPadding(); + int renderHeight = renderContentHeight + 2 * this.topVerticalPadding(); this.initForOverlay(renderWidth, renderHeight); - guiGraphics.enableScissor(this.horizontalPadding(), this.verticalPadding(), this.horizontalPadding() + contentWidth, this.verticalPadding() + contentHeight); + guiGraphics.enableScissor(this.leftHorizontalPadding(), this.topVerticalPadding(), this.leftHorizontalPadding() + contentWidth, this.topVerticalPadding() + contentHeight); var pose = guiGraphics.pose(); pose.pushMatrix(); if (rotateMinimap) { - pose.translate(-this.centerX + (float) (this.horizontalPadding() + contentWidth / 2), -this.centerY + (float) (this.verticalPadding() + contentHeight / 2)); + pose.translate(-this.centerX + (float) (this.leftHorizontalPadding() + contentWidth / 2), -this.centerY + (float) (this.topVerticalPadding() + contentHeight / 2)); pose.translate(this.centerX, this.centerY); pose.rotate((float) (-Math.toRadians(this.getPlayerRotation().y) + Math.PI)); pose.translate(-this.centerX, -this.centerY); @@ -64,7 +64,7 @@ public void renderToHud(GuiGraphics guiGraphics, float partialTick) { pose.popMatrix(); if (Configs.RotateMinimap) { - this.drawCenterCross(guiGraphics, this.horizontalPadding() + contentWidth / 2, this.verticalPadding() + contentHeight / 2); + this.drawCenterCross(guiGraphics, this.leftHorizontalPadding() + contentWidth / 2, this.topVerticalPadding() + contentHeight / 2); } guiGraphics.disableScissor(); @@ -100,12 +100,12 @@ protected boolean isMinimap() { } @Override - protected int horizontalPadding() { + protected int leftHorizontalPadding() { return Configs.MinimapOffsetX; } @Override - protected int verticalPadding() { + protected int topVerticalPadding() { return Configs.MinimapOffsetY; } } diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java index 733919ca..23ccc97c 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java @@ -44,9 +44,8 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectIterator; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectSet; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectList; import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; @@ -154,6 +153,8 @@ public class SeedMapScreen extends Screen { private static final int LEFT_HORIZONTAL_PADDING = 50; private static final int TOP_VERTICAL_PADDING = 20; + private static final int BOTTOM_VERTICAL_PADDING = 20; + public static final int MIN_PIXELS_PER_BIOME = 1; public static final int MAX_PIXELS_PER_BIOME = 100; @@ -223,7 +224,8 @@ private static int tileSizePixels() { /// The combined height of the icons of _all_ toggleable features. Includes padding. private final int featureIconsCombinedHeight; - private final ObjectSet featureWidgets = new ObjectOpenHashSet<>(); + /// A list of all features currently on rendered screen. Used for click detection. + private final ObjectList featureWidgets = new ObjectArrayList<>(); private QuartPos2 mouseQuart; @@ -327,15 +329,15 @@ protected void init() { this.centerX = this.width / 2; this.centerY = this.height / 2; - this.seedMapWidth = this.width - this.horizontalPadding(); - this.seedMapHeight = this.height - this.verticalPadding(); + this.seedMapWidth = this.width - this.leftHorizontalPadding(); + this.seedMapHeight = this.height - this.topVerticalPadding() - BOTTOM_VERTICAL_PADDING - 1; if (!this.isMinimap()) { this.createFeatureToggles(); this.createTeleportField(); this.createWaypointNameField(); - ItemIconButton lootSearchButton = new ItemIconButton(this.width - ItemIconButton.ICON_SIZE - 1, this.verticalPadding() - ItemIconButton.ICON_SIZE - 1, LOOT_SEARCH_ICON_ITEM, Component.literal("Loot Search"), button -> { + ItemIconButton lootSearchButton = new ItemIconButton(this.width - ItemIconButton.ICON_SIZE - 1, this.topVerticalPadding() - ItemIconButton.ICON_SIZE - 1, LOOT_SEARCH_ICON_ITEM, Component.literal("Loot Search"), button -> { this.minecraft.setScreen(new LootSearchScreen(SeedMapScreen.this)); }); this.addRenderableWidget(lootSearchButton); @@ -345,11 +347,17 @@ protected void init() { } } + private boolean isOverMap(double mouseX, double mouseY) { + return mouseX >= this.leftHorizontalPadding() && mouseX <= this.leftHorizontalPadding() + this.seedMapWidth + && mouseY >= this.topVerticalPadding() && mouseY <= this.topVerticalPadding() + this.seedMapHeight; + } + @Override public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + super.render(guiGraphics, mouseX, mouseY, partialTick); // draw title Component seedComponent = Component.translatable("seedMap.seed", accent(Long.toString(this.seed)), Cubiomes.mc2str(this.version).getString(0), ComponentUtils.formatGeneratorFlags(this.generatorFlags)); - guiGraphics.drawString(this.font, seedComponent, this.horizontalPadding(), this.verticalPadding() - this.font.lineHeight - 1, -1); + guiGraphics.drawString(this.font, seedComponent, this.leftHorizontalPadding(), this.topVerticalPadding() - this.font.lineHeight - 1, -1); this.renderBiomes(guiGraphics, mouseX, mouseY, partialTick); guiGraphics.nextStratum(); this.renderFeatures(guiGraphics, mouseX, mouseY, partialTick); @@ -359,14 +367,12 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partia if (this.displayCoordinatesCopiedTicks > 0) { guiGraphics.setTooltipForNextFrame(Component.translatable("seedMap.coordinatesCopied"), mouseX, mouseY); - } else if (this.isMouseOverMap(mouseX, mouseY)) { + } else if (this.isOverMap(mouseX, mouseY)) { OptionalInt optionalBiome = this.getBiome(this.mouseQuart); if (optionalBiome.isPresent()) { guiGraphics.setTooltipForNextFrame(Component.literal(Cubiomes.biome2str(this.version, optionalBiome.getAsInt()).getString(0)), mouseX, mouseY); } } - - super.render(guiGraphics, mouseX, mouseY, partialTick); } @Override @@ -558,29 +564,29 @@ private void drawTile(GuiGraphics guiGraphics, Tile tile) { int maxX = minX + tileSizePixels; int maxY = minY + tileSizePixels; - if (maxX < this.horizontalPadding() || minX > this.horizontalPadding() + this.seedMapWidth) { + if (maxX < this.leftHorizontalPadding() || minX > this.leftHorizontalPadding() + this.seedMapWidth) { return; } - if (maxY < this.verticalPadding() || minY > this.verticalPadding() + this.seedMapHeight) { + if (maxY < this.topVerticalPadding() || minY > this.topVerticalPadding() + this.seedMapHeight) { return; } float u0, u1, v0, v1; - if (minX < this.horizontalPadding()) { - u0 = (float) (this.horizontalPadding() - minX) / tileSizePixels; - minX = this.horizontalPadding(); + if (minX < this.leftHorizontalPadding()) { + u0 = (float) (this.leftHorizontalPadding() - minX) / tileSizePixels; + minX = this.leftHorizontalPadding(); } else u0 = 0; - if (maxX > this.horizontalPadding() + this.seedMapWidth) { - u1 = 1 - ((float) (maxX - this.horizontalPadding() - this.seedMapWidth) / tileSizePixels); - maxX = this.horizontalPadding() + this.seedMapWidth; + if (maxX > this.leftHorizontalPadding() + this.seedMapWidth) { + u1 = 1 - ((float) (maxX - this.leftHorizontalPadding() - this.seedMapWidth) / tileSizePixels); + maxX = this.leftHorizontalPadding() + this.seedMapWidth; } else u1 = 1; - if (minY < this.verticalPadding()) { - v0 = (float) (this.verticalPadding() - minY) / tileSizePixels; - minY = this.verticalPadding(); + if (minY < this.topVerticalPadding()) { + v0 = (float) (this.topVerticalPadding() - minY) / tileSizePixels; + minY = this.topVerticalPadding(); } else v0 = 0; - if (maxY > this.verticalPadding() + this.seedMapHeight) { - v1 = 1 - ((float) (maxY - this.verticalPadding() - this.seedMapHeight) / tileSizePixels); - maxY = this.verticalPadding() + this.seedMapHeight; + if (maxY > this.topVerticalPadding() + this.seedMapHeight) { + v1 = 1 - ((float) (maxY - this.topVerticalPadding() - this.seedMapHeight) / tileSizePixels); + maxY = this.topVerticalPadding() + this.seedMapHeight; } else v1 = 1; guiGraphics.submitBlit(RenderPipelines.GUI_TEXTURED, tile.texture().getTextureView(), tile.texture().getSampler(), minX, minY, maxX, maxY, u0, u1, v0, v1, 0xFF_FFFFFF); @@ -631,14 +637,9 @@ private Tile createSlimeChunkTile(TilePos tilePos, BitSet slimeChunkData) { } private void drawFeatureIcons(GuiGraphics guiGraphics) { - for (ObjectIterator iterator = this.featureWidgets.iterator(); iterator.hasNext();) { - FeatureWidget widget = iterator.next(); - if (Configs.ToggledFeatures.contains(widget.feature)) { - MapFeature.Texture texture = widget.texture(); - this.drawIcon(guiGraphics, texture.identifier(), widget.x, widget.y, texture.width(), texture.height(), 0xFF_FFFFFF); - } else { - iterator.remove(); - } + for (FeatureWidget widget : this.featureWidgets) { + MapFeature.Texture texture = widget.texture(); + this.drawIcon(guiGraphics, texture.identifier(), widget.x, widget.y, texture.width(), texture.height(), 0xFF_FFFFFF); } } @@ -651,7 +652,7 @@ protected void drawPlayerIndicator(GuiGraphics guiGraphics) { int playerMinY = this.centerY + Mth.floor(Configs.PixelsPerBiome * relPlayerQuart.z()) - 10; int playerMaxX = playerMinX + 20; int playerMaxY = playerMinY + 20; - if (playerMinX < this.horizontalPadding() || playerMaxX > this.horizontalPadding() + this.seedMapWidth || playerMinY < this.verticalPadding() || playerMaxY > this.verticalPadding() + this.seedMapHeight) { + if (playerMinX < this.leftHorizontalPadding() || playerMaxX > this.leftHorizontalPadding() + this.seedMapWidth || playerMinY < this.topVerticalPadding() || playerMaxY > this.topVerticalPadding() + this.seedMapHeight) { return; } PlayerFaceRenderer.draw(guiGraphics, this.minecraft.player.getSkin(), playerMinX, playerMinY, 20); @@ -670,18 +671,13 @@ protected void drawDirectionArrow(GuiGraphics guiGraphics, int playerMinX, int p ; boolean withinBounds = Stream.of(new Vector2f(20, 0), new Vector2f(20, 20), new Vector2f(0, 20), new Vector2f(0, 0)) .map(transform::transformPosition) - .allMatch(v -> isMouseOverMap(v.x, v.y)); + .allMatch(v -> isOverMap(v.x, v.y)); if (withinBounds) { drawIconStatic(guiGraphics, DIRECTION_ARROW_TEXTURE, 0, 0, 20, 20, 0xFF_FFFFFF); } guiGraphics.pose().popMatrix(); } - private boolean isMouseOverMap(double mouseX, double mouseY) { - return mouseX >= this.horizontalPadding() && mouseX <= this.horizontalPadding() + this.seedMapWidth - && mouseY >= this.verticalPadding() && mouseY <= this.verticalPadding() + this.seedMapHeight; - } - // Formerly, this laid the feature toggles out in rows above the map. // Now they are laid out in columns besides the map. private void createFeatureToggles() { @@ -695,7 +691,7 @@ private void createFeatureToggles() { int togglesPerColumn = Math.ceilDiv(this.toggleableFeatures.size(), columns); int row = 0; - int iconLeftX = 0, iconTopY = this.verticalPadding(); + int iconLeftX = 0, iconTopY = this.topVerticalPadding(); int maxIconWidth = 0; for (MapFeature toggleableFeature : this.toggleableFeatures) { // Draw the icon. @@ -710,8 +706,8 @@ private void createFeatureToggles() { if (row >= togglesPerColumn) { // Begin a new column. row = 0; - iconTopY = this.verticalPadding(); - iconLeftX += maxIconWidth + Math.ceil(scale*HORIZONTAL_FEATURE_TOGGLE_SPACING); + iconTopY = this.topVerticalPadding(); + iconLeftX += maxIconWidth + (int)Math.ceil(scale*HORIZONTAL_FEATURE_TOGGLE_SPACING); maxIconWidth = 0; } } @@ -879,18 +875,18 @@ private BlockPos calculateSpawnData() { } private void createTeleportField() { - this.teleportEditBoxX = new EditBox(this.font, this.horizontalPadding(), this.height - 20, this.seedMapWidth / 4, 20, Component.translatable("seedMap.teleportEditBoxX")); + this.teleportEditBoxX = new EditBox(this.font, this.leftHorizontalPadding(), this.height - BOTTOM_VERTICAL_PADDING, this.seedMapWidth / 4, BOTTOM_VERTICAL_PADDING, Component.translatable("seedMap.teleportEditBoxX")); this.teleportEditBoxX.setHint(Component.literal("x")); this.teleportEditBoxX.setMaxLength(9); this.addRenderableWidget(this.teleportEditBoxX); - this.teleportEditBoxZ = new EditBox(this.font, this.horizontalPadding() + this.seedMapWidth / 4, this.height - 20, this.seedMapWidth / 4, 20, Component.translatable("seedMap.teleportEditBoxZ")); + this.teleportEditBoxZ = new EditBox(this.font, this.leftHorizontalPadding() + this.seedMapWidth / 4, this.height - BOTTOM_VERTICAL_PADDING, this.seedMapWidth / 4, BOTTOM_VERTICAL_PADDING, Component.translatable("seedMap.teleportEditBoxZ")); this.teleportEditBoxZ.setHint(Component.literal("z")); this.teleportEditBoxZ.setMaxLength(9); this.addRenderableWidget(this.teleportEditBoxZ); } private void createWaypointNameField() { - this.waypointNameEditBox = new EditBox(this.font, this.horizontalPadding() + this.seedMapWidth / 2, this.height - 20, this.seedMapWidth / 2, 20, Component.translatable("seedMap.waypointNameEditBox")); + this.waypointNameEditBox = new EditBox(this.font, this.leftHorizontalPadding() + this.seedMapWidth / 2, this.height - BOTTOM_VERTICAL_PADDING, this.seedMapWidth / 2, BOTTOM_VERTICAL_PADDING, Component.translatable("seedMap.waypointNameEditBox")); this.waypointNameEditBox.setHint(Component.literal("Waypoint name")); this.addRenderableWidget(this.waypointNameEditBox); } @@ -898,11 +894,6 @@ private void createWaypointNameField() { protected void moveCenter(QuartPos2f newCenter) { this.centerQuart = newCenter; - this.featureWidgets.removeIf(widget -> { - widget.updatePosition(); - return !widget.withinBounds(); - }); - if (this.markerWidget != null) { this.markerWidget.updatePosition(); } @@ -930,7 +921,7 @@ public void mouseMoved(double mouseX, double mouseY) { } private void handleMapMouseMoved(double mouseX, double mouseY) { - if (!this.isMouseOverMap(mouseX, mouseY)) { + if (!this.isOverMap(mouseX, mouseY)) { return; } @@ -951,11 +942,6 @@ public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, doubl Configs.PixelsPerBiome = Math.max((int) (currentScroll * MAX_PIXELS_PER_BIOME + 0.5), MIN_PIXELS_PER_BIOME); - this.featureWidgets.removeIf(widget -> { - widget.updatePosition(); - return !widget.withinBounds(); - }); - if (this.markerWidget != null) { this.markerWidget.updatePosition(); } @@ -970,7 +956,7 @@ public boolean mouseDragged(MouseButtonEvent mouseButtonEvent, double dragX, dou } double mouseX = mouseButtonEvent.x(); double mouseY = mouseButtonEvent.y(); - if (!this.isMouseOverMap(mouseX, mouseY)) { + if (!this.isOverMap(mouseX, mouseY)) { return false; } @@ -1011,7 +997,7 @@ private boolean handleMapFeatureLeftClicked(MouseButtonEvent mouseButtonEvent, b } double mouseX = mouseButtonEvent.x(); double mouseY = mouseButtonEvent.y(); - if (!this.isMouseOverMap(mouseX, mouseY)) { + if (!this.isOverMap(mouseX, mouseY)) { return false; } Optional optionalFeatureWidget = this.featureWidgets.stream() @@ -1117,7 +1103,7 @@ private boolean handleMapMiddleClicked(MouseButtonEvent mouseButtonEvent, boolea } double mouseX = mouseButtonEvent.x(); double mouseY = mouseButtonEvent.y(); - if (!this.isMouseOverMap(mouseX, mouseY)) { + if (!this.isOverMap(mouseX, mouseY)) { return false; } this.minecraft.keyboardHandler.setClipboard("%d ~ %d".formatted(QuartPos.toBlock(this.mouseQuart.x()), QuartPos.toBlock(this.mouseQuart.z()))); @@ -1132,7 +1118,7 @@ private boolean handleMapRightClicked(MouseButtonEvent mouseButtonEvent, boolean } double mouseX = mouseButtonEvent.x(); double mouseY = mouseButtonEvent.y(); - if (!this.isMouseOverMap(mouseX, mouseY)) { + if (!this.isOverMap(mouseX, mouseY)) { return false; } @@ -1283,10 +1269,10 @@ public boolean withinBounds() { int maxX = minX + this.width(); int maxY = minY + this.height(); - if (maxX >= horizontalPadding() + seedMapWidth || maxY >= verticalPadding() + seedMapHeight) { + if (maxX >= leftHorizontalPadding() + seedMapWidth || maxY >= topVerticalPadding() + seedMapHeight) { return false; } - if (minX < horizontalPadding() || minY < verticalPadding()) { + if (minX < leftHorizontalPadding() || minY < topVerticalPadding()) { return false; } return true; @@ -1360,11 +1346,11 @@ public Vec2 getPlayerRotation() { return this.playerRotation; } - protected int horizontalPadding() { + protected int leftHorizontalPadding() { return LEFT_HORIZONTAL_PADDING; } - protected int verticalPadding() { + protected int topVerticalPadding() { return TOP_VERTICAL_PADDING; } From 97d29701a95c463a7a1fec6a33b206ee8f921302 Mon Sep 17 00:00:00 2001 From: Frederik van der Els Date: Mon, 9 Feb 2026 17:55:38 +0100 Subject: [PATCH 9/9] Create more columns instead of scaling --- .../seedmap/FeatureToggleWidget.java | 8 +- .../seedmapper/seedmap/MinimapScreen.java | 14 +- .../seedmapper/seedmap/SeedMapScreen.java | 151 +++++++----------- 3 files changed, 66 insertions(+), 107 deletions(-) diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/FeatureToggleWidget.java b/src/main/java/dev/xpple/seedmapper/seedmap/FeatureToggleWidget.java index b0570021..ced23eb4 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/FeatureToggleWidget.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/FeatureToggleWidget.java @@ -11,10 +11,10 @@ public class FeatureToggleWidget extends Button { private final MapFeature feature; - public FeatureToggleWidget(MapFeature feature, int x, int y, double scale) { - super(x, y, (int)Math.floor(scale * feature.getDefaultTexture().width()), (int)Math.floor(scale * feature.getDefaultTexture().height()), Component.literal(feature.getName()), FeatureToggleWidget::onButtonPress, DEFAULT_NARRATION); + public FeatureToggleWidget(MapFeature feature, int x, int y) { + super(x, y, feature.getDefaultTexture().width(), feature.getDefaultTexture().height(), Component.literal(feature.getName()), FeatureToggleWidget::onButtonPress, DEFAULT_NARRATION); this.feature = feature; - this.setTooltip(Tooltip.create(Component.literal(this.feature.getName()))); + this.setTooltip(Tooltip.create(this.message)); } @Override @@ -23,7 +23,7 @@ protected void renderContents(GuiGraphics guiGraphics, int mouseX, int mouseY, f if (!Configs.ToggledFeatures.contains(this.feature)) { colour = ARGB.color(255 >> 1, 255, 255, 255); } - SeedMapScreen.drawIconStatic(guiGraphics, this.feature.getDefaultTexture().identifier(), this.getX(), this.getY(), this.getWidth(), this.getHeight(), colour); + SeedMapScreen.FeatureWidget.drawFeatureIcon(guiGraphics, this.feature.getDefaultTexture(), this.getX(), this.getY(), colour); } private static void onButtonPress(Button button) { diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java index d600fae8..89250d91 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/MinimapScreen.java @@ -42,18 +42,18 @@ public void renderToHud(GuiGraphics guiGraphics, float partialTick) { renderContentHeight = diagonal; } // ensures super.seedMapWidth == renderContentWidth - int renderWidth = renderContentWidth + 2 * this.leftHorizontalPadding(); + int renderWidth = renderContentWidth + 2 * this.leftPadding(); // ensures super.seedMapHeight == renderContentHeight - int renderHeight = renderContentHeight + 2 * this.topVerticalPadding(); + int renderHeight = renderContentHeight + 2 * this.topPadding(); this.initForOverlay(renderWidth, renderHeight); - guiGraphics.enableScissor(this.leftHorizontalPadding(), this.topVerticalPadding(), this.leftHorizontalPadding() + contentWidth, this.topVerticalPadding() + contentHeight); + guiGraphics.enableScissor(this.leftPadding(), this.topPadding(), this.leftPadding() + contentWidth, this.topPadding() + contentHeight); var pose = guiGraphics.pose(); pose.pushMatrix(); if (rotateMinimap) { - pose.translate(-this.centerX + (float) (this.leftHorizontalPadding() + contentWidth / 2), -this.centerY + (float) (this.topVerticalPadding() + contentHeight / 2)); + pose.translate(-this.centerX + (float) (this.leftPadding() + contentWidth / 2), -this.centerY + (float) (this.topPadding() + contentHeight / 2)); pose.translate(this.centerX, this.centerY); pose.rotate((float) (-Math.toRadians(this.getPlayerRotation().y) + Math.PI)); pose.translate(-this.centerX, -this.centerY); @@ -64,7 +64,7 @@ public void renderToHud(GuiGraphics guiGraphics, float partialTick) { pose.popMatrix(); if (Configs.RotateMinimap) { - this.drawCenterCross(guiGraphics, this.leftHorizontalPadding() + contentWidth / 2, this.topVerticalPadding() + contentHeight / 2); + this.drawCenterCross(guiGraphics, this.leftPadding() + contentWidth / 2, this.topPadding() + contentHeight / 2); } guiGraphics.disableScissor(); @@ -100,12 +100,12 @@ protected boolean isMinimap() { } @Override - protected int leftHorizontalPadding() { + protected int leftPadding() { return Configs.MinimapOffsetX; } @Override - protected int topVerticalPadding() { + protected int topPadding() { return Configs.MinimapOffsetY; } } diff --git a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java index 23ccc97c..1b2c5126 100644 --- a/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java +++ b/src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java @@ -36,7 +36,6 @@ import dev.xpple.seedmapper.util.WorldIdentifier; import dev.xpple.simplewaypoints.api.SimpleWaypointsAPI; import dev.xpple.simplewaypoints.api.Waypoint; -import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.ints.AbstractIntCollection; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.IntArraySet; @@ -151,9 +150,8 @@ public class SeedMapScreen extends Screen { public static final int BIOME_SCALE = 4; public static final int SCALED_CHUNK_SIZE = LevelChunkSection.SECTION_WIDTH / BIOME_SCALE; - private static final int LEFT_HORIZONTAL_PADDING = 50; - private static final int TOP_VERTICAL_PADDING = 20; - private static final int BOTTOM_VERTICAL_PADDING = 20; + private static final int TOP_PADDING = 20; + private static final int BOTTOM_PADDING = 20; public static final int MIN_PIXELS_PER_BIOME = 1; public static final int MAX_PIXELS_PER_BIOME = 100; @@ -216,6 +214,7 @@ private static int tileSizePixels() { protected int centerX; protected int centerY; + private int leftPadding; private int seedMapWidth; private int seedMapHeight; @@ -329,15 +328,17 @@ protected void init() { this.centerX = this.width / 2; this.centerY = this.height / 2; - this.seedMapWidth = this.width - this.leftHorizontalPadding(); - this.seedMapHeight = this.height - this.topVerticalPadding() - BOTTOM_VERTICAL_PADDING - 1; + this.seedMapHeight = this.height - this.topPadding() - BOTTOM_PADDING - 1; + if (!this.isMinimap()) { + this.leftPadding = this.createFeatureToggles(); + } + this.seedMapWidth = this.width - this.leftPadding(); if (!this.isMinimap()) { - this.createFeatureToggles(); this.createTeleportField(); this.createWaypointNameField(); - ItemIconButton lootSearchButton = new ItemIconButton(this.width - ItemIconButton.ICON_SIZE - 1, this.topVerticalPadding() - ItemIconButton.ICON_SIZE - 1, LOOT_SEARCH_ICON_ITEM, Component.literal("Loot Search"), button -> { + ItemIconButton lootSearchButton = new ItemIconButton(this.width - ItemIconButton.ICON_SIZE - 1, this.topPadding() - ItemIconButton.ICON_SIZE - 1, LOOT_SEARCH_ICON_ITEM, Component.literal("Loot Search"), button -> { this.minecraft.setScreen(new LootSearchScreen(SeedMapScreen.this)); }); this.addRenderableWidget(lootSearchButton); @@ -348,8 +349,8 @@ protected void init() { } private boolean isOverMap(double mouseX, double mouseY) { - return mouseX >= this.leftHorizontalPadding() && mouseX <= this.leftHorizontalPadding() + this.seedMapWidth - && mouseY >= this.topVerticalPadding() && mouseY <= this.topVerticalPadding() + this.seedMapHeight; + return mouseX >= this.leftPadding() && mouseX <= this.leftPadding() + this.seedMapWidth + && mouseY >= this.topPadding() && mouseY <= this.topPadding() + this.seedMapHeight; } @Override @@ -357,7 +358,7 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partia super.render(guiGraphics, mouseX, mouseY, partialTick); // draw title Component seedComponent = Component.translatable("seedMap.seed", accent(Long.toString(this.seed)), Cubiomes.mc2str(this.version).getString(0), ComponentUtils.formatGeneratorFlags(this.generatorFlags)); - guiGraphics.drawString(this.font, seedComponent, this.leftHorizontalPadding(), this.topVerticalPadding() - this.font.lineHeight - 1, -1); + guiGraphics.drawString(this.font, seedComponent, this.leftPadding(), this.topPadding() - this.font.lineHeight - 1, -1); this.renderBiomes(guiGraphics, mouseX, mouseY, partialTick); guiGraphics.nextStratum(); this.renderFeatures(guiGraphics, mouseX, mouseY, partialTick); @@ -375,11 +376,6 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partia } } - @Override - public void renderBackground(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { - // no background - } - protected void renderBiomes(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { int tileSizePixels = tileSizePixels(); int horTileRadius = Math.ceilDiv(this.seedMapWidth, tileSizePixels) + 1; @@ -564,29 +560,29 @@ private void drawTile(GuiGraphics guiGraphics, Tile tile) { int maxX = minX + tileSizePixels; int maxY = minY + tileSizePixels; - if (maxX < this.leftHorizontalPadding() || minX > this.leftHorizontalPadding() + this.seedMapWidth) { + if (maxX < this.leftPadding() || minX > this.leftPadding() + this.seedMapWidth) { return; } - if (maxY < this.topVerticalPadding() || minY > this.topVerticalPadding() + this.seedMapHeight) { + if (maxY < this.topPadding() || minY > this.topPadding() + this.seedMapHeight) { return; } float u0, u1, v0, v1; - if (minX < this.leftHorizontalPadding()) { - u0 = (float) (this.leftHorizontalPadding() - minX) / tileSizePixels; - minX = this.leftHorizontalPadding(); + if (minX < this.leftPadding()) { + u0 = (float) (this.leftPadding() - minX) / tileSizePixels; + minX = this.leftPadding(); } else u0 = 0; - if (maxX > this.leftHorizontalPadding() + this.seedMapWidth) { - u1 = 1 - ((float) (maxX - this.leftHorizontalPadding() - this.seedMapWidth) / tileSizePixels); - maxX = this.leftHorizontalPadding() + this.seedMapWidth; + if (maxX > this.leftPadding() + this.seedMapWidth) { + u1 = 1 - ((float) (maxX - this.leftPadding() - this.seedMapWidth) / tileSizePixels); + maxX = this.leftPadding() + this.seedMapWidth; } else u1 = 1; - if (minY < this.topVerticalPadding()) { - v0 = (float) (this.topVerticalPadding() - minY) / tileSizePixels; - minY = this.topVerticalPadding(); + if (minY < this.topPadding()) { + v0 = (float) (this.topPadding() - minY) / tileSizePixels; + minY = this.topPadding(); } else v0 = 0; - if (maxY > this.topVerticalPadding() + this.seedMapHeight) { - v1 = 1 - ((float) (maxY - this.topVerticalPadding() - this.seedMapHeight) / tileSizePixels); - maxY = this.topVerticalPadding() + this.seedMapHeight; + if (maxY > this.topPadding() + this.seedMapHeight) { + v1 = 1 - ((float) (maxY - this.topPadding() - this.seedMapHeight) / tileSizePixels); + maxY = this.topPadding() + this.seedMapHeight; } else v1 = 1; guiGraphics.submitBlit(RenderPipelines.GUI_TEXTURED, tile.texture().getTextureView(), tile.texture().getSampler(), minX, minY, maxX, maxY, u0, u1, v0, v1, 0xFF_FFFFFF); @@ -652,7 +648,7 @@ protected void drawPlayerIndicator(GuiGraphics guiGraphics) { int playerMinY = this.centerY + Mth.floor(Configs.PixelsPerBiome * relPlayerQuart.z()) - 10; int playerMaxX = playerMinX + 20; int playerMaxY = playerMinY + 20; - if (playerMinX < this.leftHorizontalPadding() || playerMaxX > this.leftHorizontalPadding() + this.seedMapWidth || playerMinY < this.topVerticalPadding() || playerMaxY > this.topVerticalPadding() + this.seedMapHeight) { + if (playerMinX < this.leftPadding() || playerMaxX > this.leftPadding() + this.seedMapWidth || playerMinY < this.topPadding() || playerMaxY > this.topPadding() + this.seedMapHeight) { return; } PlayerFaceRenderer.draw(guiGraphics, this.minecraft.player.getSkin(), playerMinX, playerMinY, 20); @@ -678,65 +674,30 @@ protected void drawDirectionArrow(GuiGraphics guiGraphics, int playerMinX, int p guiGraphics.pose().popMatrix(); } - // Formerly, this laid the feature toggles out in rows above the map. - // Now they are laid out in columns besides the map. - private void createFeatureToggles() { + /// @return the amount of vertical pixels that are reserved for the feature toggles + private int createFeatureToggles() { // TODO: replace with Gatherers API? // TODO: only calculate on resize? - - Pair columnCountAndScale = this.computeFeatureToggleScale(); - int columns = columnCountAndScale.left(); - double scale = columnCountAndScale.right(); - + int columns = Math.ceilDiv(this.featureIconsCombinedHeight, this.seedMapHeight); int togglesPerColumn = Math.ceilDiv(this.toggleableFeatures.size(), columns); - - int row = 0; - int iconLeftX = 0, iconTopY = this.topVerticalPadding(); - int maxIconWidth = 0; - for (MapFeature toggleableFeature : this.toggleableFeatures) { - // Draw the icon. - int iconHeight = (int)Math.ceil(scale*toggleableFeature.getDefaultTexture().height()); - this.addRenderableWidget(new FeatureToggleWidget(toggleableFeature, iconLeftX, iconTopY, scale)); - - // Set up the position for where to draw the next icon. - iconTopY += iconHeight + (int)Math.ceil(scale*VERTICAL_FEATURE_TOGGLE_SPACING); - maxIconWidth = Math.max(maxIconWidth, (int)Math.ceil(scale*toggleableFeature.getDefaultTexture().width())); - - ++row; - if (row >= togglesPerColumn) { - // Begin a new column. - row = 0; - iconTopY = this.topVerticalPadding(); - iconLeftX += maxIconWidth + (int)Math.ceil(scale*HORIZONTAL_FEATURE_TOGGLE_SPACING); - maxIconWidth = 0; - } - } - } - - private Pair computeFeatureToggleScale() { - int baseColumnWidth = 0; - for (MapFeature toggleableFeature : this.toggleableFeatures) { - baseColumnWidth = Math.max(baseColumnWidth, toggleableFeature.getDefaultTexture().width()); + int toggleMinX = 1; + for (int column = 0; column < columns - 1; column++) { + this.createFeatureTogglesInner(column, togglesPerColumn, togglesPerColumn, toggleMinX, this.topPadding()); + toggleMinX += 20 + HORIZONTAL_FEATURE_TOGGLE_SPACING; } + int togglesInLastColumn = this.toggleableFeatures.size() - togglesPerColumn * (columns - 1); + this.createFeatureTogglesInner(columns - 1, togglesPerColumn, togglesInLastColumn, toggleMinX, this.topPadding()); - int n = this.toggleableFeatures.size(); - Pair bestResult = null; - for (int columns = 1; columns <= n; ++columns) { - // Approximate the maximum column height. This doesn't work if the textures have very different heights. - /// TODO: enforce textures being squares of the same size. - int maxColumnHeight = this.featureIconsCombinedHeight / columns; - - double scaleX = (double)LEFT_HORIZONTAL_PADDING / (columns * (baseColumnWidth + HORIZONTAL_FEATURE_TOGGLE_SPACING)); - double scaleY = (double)this.height * FEATURE_TOGGLE_LOWER_PADDING_FACTOR / maxColumnHeight; - double scale = Math.min(scaleX, scaleY); - - if (scale <= 0) continue; + return toggleMinX + 20 + HORIZONTAL_FEATURE_TOGGLE_SPACING; + } - if (bestResult == null || scale > bestResult.right()) - bestResult = Pair.of(columns, scale); + private void createFeatureTogglesInner(int column, int togglesPerColumn, int maxToggles, int toggleMinX, int toggleMinY) { + for (int toggle = 0; toggle < maxToggles; toggle++) { + MapFeature feature = this.toggleableFeatures.get(column * togglesPerColumn + toggle); + MapFeature.Texture featureIcon = feature.getDefaultTexture(); + this.addRenderableWidget(new FeatureToggleWidget(feature, toggleMinX, toggleMinY)); + toggleMinY += featureIcon.height() + VERTICAL_FEATURE_TOGGLE_SPACING; } - - return bestResult; } private int[] calculateBiomeData(TilePos tilePos) { @@ -875,18 +836,18 @@ private BlockPos calculateSpawnData() { } private void createTeleportField() { - this.teleportEditBoxX = new EditBox(this.font, this.leftHorizontalPadding(), this.height - BOTTOM_VERTICAL_PADDING, this.seedMapWidth / 4, BOTTOM_VERTICAL_PADDING, Component.translatable("seedMap.teleportEditBoxX")); - this.teleportEditBoxX.setHint(Component.literal("x")); + this.teleportEditBoxX = new EditBox(this.font, this.leftPadding(), this.height - BOTTOM_PADDING, this.seedMapWidth / 4, BOTTOM_PADDING, Component.translatable("seedMap.teleportEditBoxX")); + this.teleportEditBoxX.setHint(Component.literal("X")); this.teleportEditBoxX.setMaxLength(9); this.addRenderableWidget(this.teleportEditBoxX); - this.teleportEditBoxZ = new EditBox(this.font, this.leftHorizontalPadding() + this.seedMapWidth / 4, this.height - BOTTOM_VERTICAL_PADDING, this.seedMapWidth / 4, BOTTOM_VERTICAL_PADDING, Component.translatable("seedMap.teleportEditBoxZ")); - this.teleportEditBoxZ.setHint(Component.literal("z")); + this.teleportEditBoxZ = new EditBox(this.font, this.leftPadding() + this.seedMapWidth / 4, this.height - BOTTOM_PADDING, this.seedMapWidth / 4, BOTTOM_PADDING, Component.translatable("seedMap.teleportEditBoxZ")); + this.teleportEditBoxZ.setHint(Component.literal("Z")); this.teleportEditBoxZ.setMaxLength(9); this.addRenderableWidget(this.teleportEditBoxZ); } private void createWaypointNameField() { - this.waypointNameEditBox = new EditBox(this.font, this.leftHorizontalPadding() + this.seedMapWidth / 2, this.height - BOTTOM_VERTICAL_PADDING, this.seedMapWidth / 2, BOTTOM_VERTICAL_PADDING, Component.translatable("seedMap.waypointNameEditBox")); + this.waypointNameEditBox = new EditBox(this.font, this.leftPadding() + this.seedMapWidth / 2, this.height - BOTTOM_PADDING, this.seedMapWidth / 2, BOTTOM_PADDING, Component.translatable("seedMap.waypointNameEditBox")); this.waypointNameEditBox.setHint(Component.literal("Waypoint name")); this.addRenderableWidget(this.waypointNameEditBox); } @@ -1169,8 +1130,6 @@ private boolean handleTeleportFieldEnter(KeyEvent keyEvent) { return false; } this.moveCenter(new QuartPos2f(QuartPos.fromBlock(x), QuartPos.fromBlock(z))); - this.teleportEditBoxX.setValue(""); - this.teleportEditBoxZ.setValue(""); this.clearEntryBoxFocus(); return true; } @@ -1269,10 +1228,10 @@ public boolean withinBounds() { int maxX = minX + this.width(); int maxY = minY + this.height(); - if (maxX >= leftHorizontalPadding() + seedMapWidth || maxY >= topVerticalPadding() + seedMapHeight) { + if (maxX >= leftPadding() + seedMapWidth || maxY >= topPadding() + seedMapHeight) { return false; } - if (minX < leftHorizontalPadding() || minY < topVerticalPadding()) { + if (minX < leftPadding() || minY < topPadding()) { return false; } return true; @@ -1346,12 +1305,12 @@ public Vec2 getPlayerRotation() { return this.playerRotation; } - protected int leftHorizontalPadding() { - return LEFT_HORIZONTAL_PADDING; + protected int leftPadding() { + return this.leftPadding; } - protected int topVerticalPadding() { - return TOP_VERTICAL_PADDING; + protected int topPadding() { + return TOP_PADDING; } protected long getSeed() {