Skip to content

Commit 61460e2

Browse files
committed
Improved Map Zoom
1 parent 449a198 commit 61460e2

File tree

3 files changed

+142
-44
lines changed

3 files changed

+142
-44
lines changed

src/main/java/dev/xpple/seedmapper/command/commands/EspConfigCommand.java

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
1313
import com.mojang.brigadier.suggestion.Suggestions;
1414
import com.mojang.brigadier.tree.CommandNode;
15+
import dev.xpple.seedmapper.SeedMapper;
1516
import dev.xpple.seedmapper.command.CustomClientCommandSource;
1617
import dev.xpple.seedmapper.config.Configs;
17-
import dev.xpple.seedmapper.render.esp.EspStyle;
1818
import dev.xpple.seedmapper.render.RenderManager;
19-
import dev.xpple.seedmapper.SeedMapper;
19+
import dev.xpple.seedmapper.render.esp.EspStyle;
20+
import dev.xpple.seedmapper.seedmap.SeedMapScreen;
2021
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
22+
import net.minecraft.client.Minecraft;
2123
import net.minecraft.commands.SharedSuggestionProvider;
2224
import net.minecraft.network.chat.Component;
2325

@@ -46,9 +48,12 @@ private EspConfigCommand() {
4648
private static final SimpleCommandExceptionType INVALID_COLOR = new SimpleCommandExceptionType(Component.literal("Invalid color. Use hex, e.g. #RRGGBB or #AARRGGBB."));
4749
private static final DynamicCommandExceptionType UNKNOWN_PROPERTY = new DynamicCommandExceptionType(value -> Component.literal("Unknown ESP property \"" + value + "\"."));
4850
private static final DynamicCommandExceptionType UNKNOWN_TARGET = new DynamicCommandExceptionType(value -> Component.literal("Unknown ESP target \"" + value + "\"."));
51+
private static final SimpleCommandExceptionType MAP_SIZE_UNAVAILABLE = new SimpleCommandExceptionType(Component.literal("Unable to determine seed map size. Make sure the game window is open."));
4952
private static final List<String> PROPERTY_SUGGESTIONS = Arrays.stream(EspProperty.values())
5053
.map(EspProperty::displayName)
5154
.toList();
55+
private static final double MIN_ZOOM_BLOCKS = 128.0D;
56+
private static final double MAX_ZOOM_BLOCKS = 100_000.0D;
5257

5358
public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
5459
CommandNode<FabricClientCommandSource> cconfigRoot = dispatcher.getRoot().getChild("cconfig");
@@ -78,6 +83,7 @@ public static void register(CommandDispatcher<FabricClientCommandSource> dispatc
7883
targetArgNode.then(literal("reset")
7984
.executes(ctx -> executeReset(ctx, getTargetArgument(ctx, "target"))));
8085
modRoot.addChild(targetArgNode.build());
86+
modRoot.addChild(buildZoomLiteral().build());
8187
}
8288

8389
private static void registerDirectSmConfig(CommandDispatcher<FabricClientCommandSource> dispatcher) {
@@ -96,6 +102,7 @@ private static void registerDirectSmConfig(CommandDispatcher<FabricClientCommand
96102
targetArgNode.then(literal("reset")
97103
.executes(ctx -> executeReset(ctx, getTargetArgument(ctx, "target"))));
98104
smRoot.then(targetArgNode);
105+
smRoot.then(buildZoomLiteral());
99106
// esptimeout top-level alias
100107
smRoot.then(literal("esptimeout")
101108
.executes(ctx -> {
@@ -115,6 +122,74 @@ private static void registerDirectSmConfig(CommandDispatcher<FabricClientCommand
115122
dispatcher.register(smRoot);
116123
}
117124

125+
private static LiteralArgumentBuilder<FabricClientCommandSource> buildZoomLiteral() {
126+
LiteralArgumentBuilder<FabricClientCommandSource> zoom = literal("Zoom");
127+
zoom.then(literal("get")
128+
.executes(EspConfigCommand::executeZoomGet));
129+
zoom.then(literal("set")
130+
.then(argument("blocks", DoubleArgumentType.doubleArg(MIN_ZOOM_BLOCKS, MAX_ZOOM_BLOCKS))
131+
.executes(ctx -> executeZoomSet(ctx, DoubleArgumentType.getDouble(ctx, "blocks")))));
132+
zoom.then(literal("default")
133+
.executes(EspConfigCommand::executeZoomDefault));
134+
return zoom;
135+
}
136+
137+
private static int executeZoomGet(CommandContext<FabricClientCommandSource> ctx) throws CommandSyntaxException {
138+
CustomClientCommandSource source = CustomClientCommandSource.of(ctx.getSource());
139+
double minPixels = Math.max(SeedMapScreen.MIN_PIXELS_PER_BIOME, Configs.SeedMapMinPixelsPerBiome);
140+
double blocks = computeBlocksForMinPixels(minPixels);
141+
source.sendFeedback(Component.literal(String.format(Locale.ROOT, "Max zoom-out ≈ %,.0f blocks at current GUI scale (min pixels per biome %.4f).", blocks, minPixels)));
142+
return Command.SINGLE_SUCCESS;
143+
}
144+
145+
private static int executeZoomSet(CommandContext<FabricClientCommandSource> ctx, double requestedBlocks) throws CommandSyntaxException {
146+
CustomClientCommandSource source = CustomClientCommandSource.of(ctx.getSource());
147+
double minPixels = convertBlocksToMinPixels(requestedBlocks);
148+
double clamped = Math.clamp(minPixels, SeedMapScreen.MIN_PIXELS_PER_BIOME, SeedMapScreen.MAX_PIXELS_PER_BIOME);
149+
Configs.SeedMapMinPixelsPerBiome = clamped;
150+
Configs.PixelsPerBiome = Math.max(Configs.PixelsPerBiome, clamped);
151+
Configs.save();
152+
double blocks = computeBlocksForMinPixels(clamped);
153+
source.sendFeedback(Component.literal(String.format(Locale.ROOT, "Max zoom-out updated to ≈ %,.0f blocks at current GUI scale (min pixels per biome %.4f).", blocks, clamped)));
154+
return Command.SINGLE_SUCCESS;
155+
}
156+
157+
private static int executeZoomDefault(CommandContext<FabricClientCommandSource> ctx) throws CommandSyntaxException {
158+
CustomClientCommandSource source = CustomClientCommandSource.of(ctx.getSource());
159+
double defaultMin = SeedMapScreen.DEFAULT_MIN_PIXELS_PER_BIOME;
160+
Configs.SeedMapMinPixelsPerBiome = defaultMin;
161+
Configs.PixelsPerBiome = Math.max(Configs.PixelsPerBiome, defaultMin);
162+
Configs.save();
163+
double blocks = computeBlocksForMinPixels(defaultMin);
164+
source.sendFeedback(Component.literal(String.format(Locale.ROOT, "Max zoom-out reset to ≈ %,.0f blocks at current GUI scale (min pixels per biome %.4f).", blocks, defaultMin)));
165+
return Command.SINGLE_SUCCESS;
166+
}
167+
168+
private static double convertBlocksToMinPixels(double blocks) throws CommandSyntaxException {
169+
int widthPixels = currentSeedMapWidthPixels();
170+
if (widthPixels <= 0) {
171+
throw MAP_SIZE_UNAVAILABLE.create();
172+
}
173+
return (widthPixels * SeedMapScreen.BIOME_SCALE) / blocks;
174+
}
175+
176+
private static double computeBlocksForMinPixels(double minPixelsPerBiome) throws CommandSyntaxException {
177+
int widthPixels = currentSeedMapWidthPixels();
178+
if (widthPixels <= 0) {
179+
throw MAP_SIZE_UNAVAILABLE.create();
180+
}
181+
double safeMin = Math.max(minPixelsPerBiome, SeedMapScreen.MIN_PIXELS_PER_BIOME);
182+
return (widthPixels * SeedMapScreen.BIOME_SCALE) / safeMin;
183+
}
184+
185+
private static int currentSeedMapWidthPixels() {
186+
Minecraft minecraft = Minecraft.getInstance();
187+
if (minecraft == null || minecraft.getWindow() == null) {
188+
return 0;
189+
}
190+
return SeedMapScreen.computeSeedMapWidth(minecraft.getWindow().getGuiScaledWidth());
191+
}
192+
118193
private static int executeGet(CommandContext<FabricClientCommandSource> ctx, EspTarget target, EspProperty property) {
119194
CustomClientCommandSource source = CustomClientCommandSource.of(ctx.getSource());
120195
EspStyle style = target.style();

src/main/java/dev/xpple/seedmapper/config/Configs.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,23 @@ private static void setSeedMapThreads(int seedMapThreads) {
5757
}
5858

5959
@Config(setter = @Config.Setter("setPixelsPerBiome"))
60-
public static int PixelsPerBiome = 4;
60+
public static double PixelsPerBiome = 4.0D;
6161

62-
private static void setPixelsPerBiome(int pixelsPerBiome) {
63-
PixelsPerBiome = Math.clamp(pixelsPerBiome, SeedMapScreen.MIN_PIXELS_PER_BIOME, SeedMapScreen.MAX_PIXELS_PER_BIOME);
62+
private static void setPixelsPerBiome(double pixelsPerBiome) {
63+
PixelsPerBiome = clampSeedMapZoom(pixelsPerBiome);
64+
}
65+
66+
@Config(setter = @Config.Setter("setSeedMapMinPixelsPerBiome"))
67+
public static double SeedMapMinPixelsPerBiome = SeedMapScreen.DEFAULT_MIN_PIXELS_PER_BIOME;
68+
69+
private static void setSeedMapMinPixelsPerBiome(double minPixelsPerBiome) {
70+
SeedMapMinPixelsPerBiome = Math.clamp(minPixelsPerBiome, SeedMapScreen.MIN_PIXELS_PER_BIOME, SeedMapScreen.MAX_PIXELS_PER_BIOME);
71+
PixelsPerBiome = clampSeedMapZoom(PixelsPerBiome);
72+
}
73+
74+
private static double clampSeedMapZoom(double pixelsPerBiome) {
75+
double min = Math.max(SeedMapScreen.MIN_PIXELS_PER_BIOME, SeedMapMinPixelsPerBiome);
76+
return Math.clamp(pixelsPerBiome, min, SeedMapScreen.MAX_PIXELS_PER_BIOME);
6477
}
6578

6679
@Config(setter = @Config.Setter("setMinimapOffsetX"))

src/main/java/dev/xpple/seedmapper/seedmap/SeedMapScreen.java

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@
129129
import java.util.Objects;
130130
import java.util.Optional;
131131
import java.util.OptionalInt;
132-
import java.util.function.IntSupplier;
133132
import java.util.function.ToIntBiFunction;
134133
import java.util.stream.IntStream;
135134
import java.util.stream.Stream;
@@ -181,8 +180,10 @@ public class SeedMapScreen extends Screen {
181180
private static final int HORIZONTAL_PADDING = 50;
182181
private static final int VERTICAL_PADDING = 50;
183182

184-
public static final int MIN_PIXELS_PER_BIOME = 1;
185-
public static final int MAX_PIXELS_PER_BIOME = 100;
183+
public static final double MIN_PIXELS_PER_BIOME = 0.01D;
184+
public static final double DEFAULT_MIN_PIXELS_PER_BIOME = 1.0D;
185+
public static final double MAX_PIXELS_PER_BIOME = 100.0D;
186+
private static final double ZOOM_SCROLL_SENSITIVITY = 0.2D;
186187

187188
private static final int HORIZONTAL_FEATURE_TOGGLE_SPACING = 5;
188189
private static final int VERTICAL_FEATURE_TOGGLE_SPACING = 1;
@@ -191,7 +192,9 @@ public class SeedMapScreen extends Screen {
191192
private static final int TELEPORT_FIELD_WIDTH = 70;
192193
private static final int WAYPOINT_NAME_FIELD_WIDTH = 100;
193194

194-
private static final IntSupplier TILE_SIZE_PIXELS = () -> TilePos.TILE_SIZE_CHUNKS * SCALED_CHUNK_SIZE * Configs.PixelsPerBiome;
195+
private static double tileSizePixels() {
196+
return TilePos.TILE_SIZE_CHUNKS * SCALED_CHUNK_SIZE * Configs.PixelsPerBiome;
197+
}
195198

196199
private static final Object2ObjectMap<WorldIdentifier, Object2ObjectMap<TilePos, int[]>> biomeDataCache = new Object2ObjectOpenHashMap<>();
197200
private static final Object2ObjectMap<WorldIdentifier, Object2ObjectMap<ChunkPos, ChunkStructureData>> structureDataCache = new Object2ObjectOpenHashMap<>();
@@ -363,7 +366,7 @@ protected void init() {
363366
this.centerX = this.width / 2;
364367
this.centerY = this.height / 2;
365368

366-
this.seedMapWidth = 2 * (this.centerX - HORIZONTAL_PADDING);
369+
this.seedMapWidth = computeSeedMapWidth(this.width);
367370
this.seedMapHeight = 2 * (this.centerY - VERTICAL_PADDING);
368371

369372
this.createFeatureToggles();
@@ -389,11 +392,11 @@ public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partia
389392
private void drawTile(GuiGraphics guiGraphics, Tile tile) {
390393
TilePos tilePos = tile.pos();
391394
QuartPos2f relTileQuart = QuartPos2f.fromQuartPos(QuartPos2.fromTilePos(tilePos)).subtract(this.centerQuart);
392-
int tileSizePixels = TILE_SIZE_PIXELS.getAsInt();
393-
int minX = this.centerX + Mth.floor(Configs.PixelsPerBiome * relTileQuart.x());
394-
int minY = this.centerY + Mth.floor(Configs.PixelsPerBiome * relTileQuart.z());
395-
int maxX = minX + tileSizePixels;
396-
int maxY = minY + tileSizePixels;
395+
double tileSizePixels = tileSizePixels();
396+
double minX = this.centerX + Configs.PixelsPerBiome * relTileQuart.x();
397+
double minY = this.centerY + Configs.PixelsPerBiome * relTileQuart.z();
398+
double maxX = minX + tileSizePixels;
399+
double maxY = minY + tileSizePixels;
397400

398401
if (maxX < HORIZONTAL_PADDING || minX > HORIZONTAL_PADDING + this.seedMapWidth) {
399402
return;
@@ -402,25 +405,25 @@ private void drawTile(GuiGraphics guiGraphics, Tile tile) {
402405
return;
403406
}
404407

405-
float u0, u1, v0, v1;
406-
if (minX < HORIZONTAL_PADDING) {
407-
u0 = (float) (HORIZONTAL_PADDING - minX) / tileSizePixels;
408-
minX = HORIZONTAL_PADDING;
409-
} else u0 = 0;
410-
if (maxX > HORIZONTAL_PADDING + this.seedMapWidth) {
411-
u1 = 1 - ((float) (maxX - HORIZONTAL_PADDING - this.seedMapWidth) / tileSizePixels);
412-
maxX = HORIZONTAL_PADDING + this.seedMapWidth;
413-
} else u1 = 1;
414-
if (minY < VERTICAL_PADDING) {
415-
v0 = (float) (VERTICAL_PADDING - minY) / tileSizePixels;
416-
minY = VERTICAL_PADDING;
417-
} else v0 = 0;
418-
if (maxY > VERTICAL_PADDING + this.seedMapHeight) {
419-
v1 = 1 - ((float) (maxY - VERTICAL_PADDING - this.seedMapHeight) / tileSizePixels);
420-
maxY = VERTICAL_PADDING + this.seedMapHeight;
421-
} else v1 = 1;
408+
double clampedMinX = Math.max(minX, HORIZONTAL_PADDING);
409+
double clampedMaxX = Math.min(maxX, HORIZONTAL_PADDING + this.seedMapWidth);
410+
double clampedMinY = Math.max(minY, VERTICAL_PADDING);
411+
double clampedMaxY = Math.min(maxY, VERTICAL_PADDING + this.seedMapHeight);
412+
if (clampedMinX >= clampedMaxX || clampedMinY >= clampedMaxY) {
413+
return;
414+
}
415+
416+
float u0 = (float) ((clampedMinX - minX) / tileSizePixels);
417+
float u1 = (float) ((clampedMaxX - minX) / tileSizePixels);
418+
float v0 = (float) ((clampedMinY - minY) / tileSizePixels);
419+
float v1 = (float) ((clampedMaxY - minY) / tileSizePixels);
420+
421+
int drawMinX = (int) Math.floor(clampedMinX);
422+
int drawMaxX = (int) Math.ceil(clampedMaxX);
423+
int drawMinY = (int) Math.floor(clampedMinY);
424+
int drawMaxY = (int) Math.ceil(clampedMaxY);
422425

423-
guiGraphics.submitBlit(RenderPipelines.GUI_TEXTURED, tile.texture().getTextureView(), tile.texture().getSampler(), minX, minY, maxX, maxY, u0, u1, v0, v1, this.getMapBackgroundTint());
426+
guiGraphics.submitBlit(RenderPipelines.GUI_TEXTURED, tile.texture().getTextureView(), tile.texture().getSampler(), drawMinX, drawMinY, drawMaxX, drawMaxY, u0, u1, v0, v1, this.getMapBackgroundTint());
424427
}
425428

426429
private Tile createBiomeTile(TilePos tilePos, int[] biomeData) {
@@ -1317,10 +1320,12 @@ public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, doubl
13171320
return true;
13181321
}
13191322

1320-
float currentScroll = Mth.clamp((float) Configs.PixelsPerBiome / MAX_PIXELS_PER_BIOME, 0.0F, 1.0F);
1321-
currentScroll = Mth.clamp(currentScroll - (float) (-scrollY / MAX_PIXELS_PER_BIOME), 0.0F, 1.0F);
1322-
1323-
int newPixels = Math.max((int) (currentScroll * MAX_PIXELS_PER_BIOME + 0.5), MIN_PIXELS_PER_BIOME);
1323+
double minPixels = Math.max(MIN_PIXELS_PER_BIOME, Configs.SeedMapMinPixelsPerBiome);
1324+
double scale = Math.pow(2.0D, scrollY * ZOOM_SCROLL_SENSITIVITY);
1325+
double newPixels = Math.clamp(Configs.PixelsPerBiome * scale, minPixels, MAX_PIXELS_PER_BIOME);
1326+
if (Math.abs(newPixels - Configs.PixelsPerBiome) < 1.0E-6D) {
1327+
return false;
1328+
}
13241329
// use setter so all feature/widget positions are updated when zooming
13251330
this.setPixelsPerBiome(newPixels);
13261331
return true;
@@ -2258,9 +2263,9 @@ protected void renderSeedMap(GuiGraphics guiGraphics, int mouseX, int mouseY, fl
22582263
Component seedComponent = Component.translatable("seedMap.seedAndVersion", accent(Long.toString(this.seed)), Cubiomes.mc2str(this.version).getString(0));
22592264
guiGraphics.drawString(this.font, seedComponent, HORIZONTAL_PADDING, VERTICAL_PADDING - this.font.lineHeight - 1, -1);
22602265

2261-
int tileSizePixels = TILE_SIZE_PIXELS.getAsInt();
2262-
int horTileRadius = Math.ceilDiv(this.seedMapWidth, tileSizePixels) + 1;
2263-
int verTileRadius = Math.ceilDiv(this.seedMapHeight, tileSizePixels) + 1;
2266+
double tileSizePixels = tileSizePixels();
2267+
int horTileRadius = (int) Math.ceil(this.seedMapWidth / tileSizePixels) + 1;
2268+
int verTileRadius = (int) Math.ceil(this.seedMapHeight / tileSizePixels) + 1;
22642269

22652270
TilePos centerTile = TilePos.fromQuartPos(QuartPos2.fromQuartPos2f(this.centerQuart));
22662271
for (int relTileX = -horTileRadius; relTileX <= horTileRadius; relTileX++) {
@@ -2287,8 +2292,8 @@ protected void renderSeedMap(GuiGraphics guiGraphics, int mouseX, int mouseY, fl
22872292

22882293
guiGraphics.nextStratum();
22892294

2290-
int horChunkRadius = Math.ceilDiv(this.seedMapWidth / 2, SCALED_CHUNK_SIZE * Configs.PixelsPerBiome);
2291-
int verChunkRadius = Math.ceilDiv(this.seedMapHeight / 2, SCALED_CHUNK_SIZE * Configs.PixelsPerBiome);
2295+
int horChunkRadius = (int) Math.ceil((this.seedMapWidth / 2.0D) / (SCALED_CHUNK_SIZE * Configs.PixelsPerBiome));
2296+
int verChunkRadius = (int) Math.ceil((this.seedMapHeight / 2.0D) / (SCALED_CHUNK_SIZE * Configs.PixelsPerBiome));
22922297

22932298
// compute structures
22942299
Configs.ToggledFeatures.stream()
@@ -2528,7 +2533,8 @@ protected double getPixelsPerBiome() {
25282533
}
25292534

25302535
protected void setPixelsPerBiome(double pixelsPerBiome) {
2531-
int p = (int) Math.round(Math.max(MIN_PIXELS_PER_BIOME, Math.min(MAX_PIXELS_PER_BIOME, pixelsPerBiome)));
2536+
double min = Math.max(MIN_PIXELS_PER_BIOME, Configs.SeedMapMinPixelsPerBiome);
2537+
double p = Math.clamp(pixelsPerBiome, min, MAX_PIXELS_PER_BIOME);
25322538
Configs.PixelsPerBiome = p;
25332539
// update widget positions so icons move when zoom changes
25342540
try {
@@ -2552,4 +2558,8 @@ protected void updateAllFeatureWidgetPositions() {
25522558
protected boolean showCoordinateOverlay() { return true; }
25532559
protected boolean showFeatureToggleTooltips() { return true; }
25542560
protected boolean showSeedLabel() { return true; }
2555-
}
2561+
2562+
public static int computeSeedMapWidth(int screenWidth) {
2563+
return Math.max(1, screenWidth - 2 * HORIZONTAL_PADDING);
2564+
}
2565+
}

0 commit comments

Comments
 (0)