diff --git a/src/main/java/dev/enjarai/minitardis/component/BiomeScanner.java b/src/main/java/dev/enjarai/minitardis/component/BiomeScanner.java new file mode 100644 index 0000000..5054fec --- /dev/null +++ b/src/main/java/dev/enjarai/minitardis/component/BiomeScanner.java @@ -0,0 +1,139 @@ +package dev.enjarai.minitardis.component; + +import java.util.Iterator; +import java.util.function.BiConsumer; + +import org.joml.Vector2i; + +import dev.enjarai.minitardis.block.ModBlocks; +import net.minecraft.block.BlockState; +import net.minecraft.block.MapColor; +import net.minecraft.block.MapColor.Brightness; +import net.minecraft.fluid.Fluids; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; + +public class BiomeScanner { + public static final int RANGE = 9216; + public static final int TOTAL_BLOCKS = RANGE * RANGE; + + private final int maxPerTick; + private final byte[] biomeMap = new byte[TOTAL_BLOCKS]; + private Iterator iterator = newIterator(); + private boolean shouldScanNextTick; + private boolean isZAxis; + + public BiomeScanner(int maxPerTick) { + this.maxPerTick = maxPerTick; + } + + public void tick() { + if (shouldScanNextTick) { + tardis.getDestinationWorld().ifPresent(world -> { + @SuppressWarnings("OptionalGetWithoutIsPresent") + var location = tardis.getDestination().get(); + + iterator = update(world, biomeMap, iterator, (pos3, pos2) -> pos3.set(location.pos()).move(pos2.x, 0, pos2.y)); + }); + + shouldScanNextTick = false; + } else { + iterator = newIterator(); + } + } + + public byte getFor(int x, int y) { + return getFor(getIndex(x, y)); + } + + public byte getFor(int pos) { + return biomeMap[pos]; + } + + public void shouldScanNextTick() { + shouldScanNextTick = true; + } + + private Iterator update(ServerWorld world, byte[] map, Iterator iterator, BiConsumer posApplier) { + var pos3 = new BlockPos.Mutable(); + var pos2 = new Vector2i(); + + for (int i = 0; i < maxPerTick; i++) { + if (!iterator.hasNext()) return newIterator(); + + pos2.set(iterator.next()); + posApplier.accept(pos3, pos2); + pos2.add(RANGE / 2 - 1, RANGE / 2 - 1); + + byte value = getValue(world, pos3); + map[getIndex(pos2)] = value; + } + + return iterator; + } + + private byte getValue(ServerWorld world, BlockPos pos) { + switch (world.getBiome(pos).getKey().get().getValue().toTranslationKey()) { + case "biome.minecraft.ocean": + return (byte) MapColor.DARK_AQUA.id; + default: + return (byte) MapColor.CYAN.id; + } + } + + public static int getIndex(Vector2i pos) { + return getIndex(pos.x, pos.y); + } + + public static int getIndex(int x, int y) { + return (x + y * RANGE) % TOTAL_BLOCKS; + } + + public static Vector2i getPos(int index) { + return new Vector2i(index % RANGE - RANGE / 2, index / RANGE - RANGE / 2); + } + + // https://stackoverflow.com/questions/3706219/algorithm-for-iterating-over-an-outward-spiral-on-a-discrete-2d-grid-from-the-or + private static Iterator newIterator() { + return new Iterator<>() { + // direction in which we move right now + final Vector2i direction = new Vector2i(1, 0); + // length of current segment + int segment_length = 1; + + // current position and how much of current segment we passed + final Vector2i current = new Vector2i(0, 0); + int segment_passed = 0; + int k = 0; + + @Override + public boolean hasNext() { + return k < TOTAL_BLOCKS; + } + + @Override + public Vector2i next() { + k++; + // make a step, add direction vector to current position + current.add(direction); + ++segment_passed; + + if (segment_passed == segment_length) { + // done with current segment + segment_passed = 0; + + // 'rotate' directions + //noinspection SuspiciousNameCombination + direction.set(-direction.y, direction.x); + + // increase segment length if necessary + if (direction.y == 0) { + ++segment_length; + } + } + + return current; + } + }; + } +} diff --git a/src/main/java/dev/enjarai/minitardis/component/Tardis.java b/src/main/java/dev/enjarai/minitardis/component/Tardis.java index b8d972b..e0ea569 100644 --- a/src/main/java/dev/enjarai/minitardis/component/Tardis.java +++ b/src/main/java/dev/enjarai/minitardis/component/Tardis.java @@ -69,6 +69,7 @@ public class Tardis { @Nullable RuntimeWorldHandle interiorWorld; DestinationScanner destinationScanner = new DestinationScanner(this, 128); + BiomeScanner biomeScanner = new BiomeScanner(this, 128); private int sparksQueued; private final UUID uuid; @@ -170,6 +171,7 @@ public void tick() { } destinationScanner.tick(); + biomeScanner.tick(); } public ServerWorld getInteriorWorld() { @@ -194,6 +196,10 @@ public Optional getDestinationWorld() { public DestinationScanner getDestinationScanner() { return destinationScanner; } + + public BiomeScanner getBiomeScanner() { + return biomeScanner; + } private void initializeInteriorWorld() { var server = holder.getServer(); diff --git a/src/main/java/dev/enjarai/minitardis/component/screen/app/BiomeScannerApp.java b/src/main/java/dev/enjarai/minitardis/component/screen/app/BiomeScannerApp.java new file mode 100644 index 0000000..3532633 --- /dev/null +++ b/src/main/java/dev/enjarai/minitardis/component/screen/app/BiomeScannerApp.java @@ -0,0 +1,91 @@ +package dev.enjarai.minitardis.component.screen.app; + +import java.util.List; +import java.util.NoSuchElementException; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import dev.enjarai.minitardis.block.console.ScreenBlockEntity; +import dev.enjarai.minitardis.canvas.TardisCanvasUtils; +import dev.enjarai.minitardis.component.BiomeScanner; +import dev.enjarai.minitardis.component.DestinationScanner; +import dev.enjarai.minitardis.component.TardisControl; +import dev.enjarai.minitardis.component.screen.element.SmallButtonElement; +import eu.pb4.mapcanvas.api.core.CanvasColor; +import eu.pb4.mapcanvas.api.core.DrawableCanvas; +import eu.pb4.mapcanvas.api.utils.CanvasUtils; +import eu.pb4.mapcanvas.impl.view.Rotate90ClockwiseView; +import net.minecraft.block.MapColor; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.ClickType; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.source.BiomeCoords; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.ChunkStatus; + +public class BiomeScannerApp implements ScreenApp { + //public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + // h.optionalFieldOf("biome_scanner", List.of()).forGetter(app -> app.scanner) + //).apply(instance, BiomeScannerApp::new)); + + private final BiomeScanner scanner; + + @Override + public AppView getView(TardisControl controls) { + return new ElementHoldingView(controls) { + //{ + //addElement(new SmallButtonElement(96 + 2, 2 + 14, "XAxis", controls -> controls.getTardis().getDestinationScanner().useXAxis())); + //addElement(new SmallButtonElement(96 + 2, 2 + 14 + 14, "ZAxis", controls -> controls.getTardis().getDestinationScanner().useZAxis())); + //} + + @Override + public void draw(ScreenBlockEntity blockEntity, DrawableCanvas canvas) { + for (int x = 0; x < BiomeScanner.RANGE; x++) { + for (int y = 0; y < BiomeScanner.RANGE; y++) { + byte value = controls.getTardis().getBiomeScanner().getFor(x, y); + var color = CanvasColor.from(MapColor.get(value), MapColor.Brightness.NORMAL); + canvas.set(x, -y - 1 + BiomeScanner.RANGE, color); + } + } + + CanvasUtils.draw(canvas, 96, 64, TardisCanvasUtils.getSprite("coord_widget_x")); + controls.getTardis().getDestination().ifPresent(destination -> { + var rotation = destination.facing().getHorizontal(); + //if (isZ) { + // rotation = (rotation + 3) % 4; + //} + + //DrawableCanvas view = TardisCanvasUtils.getSprite("destination_facing_widget"); + //for (int i = 0; i < rotation; i++) { + // view = new Rotate90ClockwiseView(view); + //} + //CanvasUtils.draw(canvas, 96, 64, view); + }); + + super.draw(blockEntity, canvas); + } + + @Override + public void screenTick(ScreenBlockEntity blockEntity) { + controls.getTardis().getBiomeScanner().shouldScanNextTick(); + } + }; + } + + + @Override + public void drawIcon(TardisControl controls, ScreenBlockEntity blockEntity, DrawableCanvas canvas) { + CanvasUtils.draw(canvas, 0, 0, TardisCanvasUtils.getSprite("app/dummy")); + } + + @Override + public ScreenAppType getType() { + return ScreenAppTypes.BIOME_SCANNER; + } +} \ No newline at end of file diff --git a/src/main/java/dev/enjarai/minitardis/component/screen/app/ScreenAppTypes.java b/src/main/java/dev/enjarai/minitardis/component/screen/app/ScreenAppTypes.java index 0d719fe..28b7dbe 100644 --- a/src/main/java/dev/enjarai/minitardis/component/screen/app/ScreenAppTypes.java +++ b/src/main/java/dev/enjarai/minitardis/component/screen/app/ScreenAppTypes.java @@ -9,6 +9,7 @@ public class ScreenAppTypes { public static final ScreenAppType DUMMY = register("dummy", DummyApp.CODEC, DummyApp::new); public static final ScreenAppType SCANNER = register("scanner", ScannerApp.CODEC, ScannerApp::new, true); + public static final ScreenAppType BIOME_SCANNER = register("biome_scanner", BiomeScannerApp.CODEC, BiomeScannerApp::new, true); public static final ScreenAppType GPS = register("gps", GpsApp.CODEC, GpsApp::new); public static final ScreenAppType BAD_APPLE = register("bad_apple", BadAppleApp.CODEC, BadAppleApp::new, true); public static final ScreenAppType STATUS = register("status", StatusApp.CODEC, StatusApp::new);