From e4bbd8e016ffa2e13c7242117a27cc9070f354ed Mon Sep 17 00:00:00 2001 From: tazer Date: Sat, 6 Jun 2026 02:36:01 +0300 Subject: [PATCH 1/2] ambient discovery and lenses --- .../java/com/evandev/fieldguide/ModTags.java | 1 + .../render/DiscoveryOverlayRenderer.java | 552 ++++++++++++++++++ .../client/render/ScanOverlayRenderer.java | 10 +- .../scan/manager/FieldGuideScanManager.java | 55 +- .../config/ClothConfigIntegration.java | 62 ++ .../evandev/fieldguide/config/ModConfig.java | 9 + .../fieldguide/config/ServerConfig.java | 12 + .../com/evandev/fieldguide/item/ModItems.java | 5 + .../mixin/client/LevelRendererMixin.java | 15 + .../assets/fieldguide/lang/en_us.json | 28 + .../assets/fieldguide/models/item/lens.json | 6 + .../assets/fieldguide/textures/item/lens.png | Bin 0 -> 491 bytes .../data/fieldguide/tags/item/lenses.json | 6 + 13 files changed, 751 insertions(+), 10 deletions(-) create mode 100644 common/src/main/java/com/evandev/fieldguide/client/render/DiscoveryOverlayRenderer.java create mode 100644 common/src/main/resources/assets/fieldguide/models/item/lens.json create mode 100644 common/src/main/resources/assets/fieldguide/textures/item/lens.png create mode 100644 common/src/main/resources/data/fieldguide/tags/item/lenses.json diff --git a/common/src/main/java/com/evandev/fieldguide/ModTags.java b/common/src/main/java/com/evandev/fieldguide/ModTags.java index 28ed247e..d220bfc3 100644 --- a/common/src/main/java/com/evandev/fieldguide/ModTags.java +++ b/common/src/main/java/com/evandev/fieldguide/ModTags.java @@ -21,6 +21,7 @@ private static TagKey tag(String name) { public static class Items { public static final TagKey SPYGLASSES = tag("spyglasses"); + public static final TagKey LENSES = tag("lenses"); public static final TagKey BLACKLISTED = tag("blacklisted"); private static TagKey tag(String name) { diff --git a/common/src/main/java/com/evandev/fieldguide/client/render/DiscoveryOverlayRenderer.java b/common/src/main/java/com/evandev/fieldguide/client/render/DiscoveryOverlayRenderer.java new file mode 100644 index 00000000..e9866d2f --- /dev/null +++ b/common/src/main/java/com/evandev/fieldguide/client/render/DiscoveryOverlayRenderer.java @@ -0,0 +1,552 @@ +package com.evandev.fieldguide.client.render; + +import com.evandev.fieldguide.api.GuideEntry; +import com.evandev.fieldguide.client.ClientFieldGuideManager; +import com.evandev.fieldguide.client.ModRenderTypes; +import com.evandev.fieldguide.client.progress.ProgressManager; +import com.evandev.fieldguide.client.scan.FieldGuideScanner; +import com.evandev.fieldguide.client.scan.manager.FieldGuideRaytracer; +import com.evandev.fieldguide.client.scan.manager.FieldGuideScanManager; +import com.evandev.fieldguide.compat.etf.EtfCompat; +import com.evandev.fieldguide.config.ClientConfig; +import com.evandev.fieldguide.platform.Services; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ItemBlockRenderTypes; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.ClipContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import org.joml.Matrix4f; +import org.lwjgl.opengl.GL11; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public final class DiscoveryOverlayRenderer { + + private static final int RECOMPUTE_INTERVAL = 10; + private static final int MAX_FOUND = 8000; + private static final int MAX_RENDERED = 1500; + private static final float FULL_FILL = 10000.0F; + private static final float SUBTLE_FACTOR = 0.55F; + private static final float PULSE_MIN = 0.4F; + private static final double PULSE_SPEED = 350.0; + private static final float FADE_MS = 400.0F; + + private static final double[][] SAMPLE_OFFSETS = { + {0.5, 0.5, 0.5}, + {0.1, 0.1, 0.1}, {0.9, 0.1, 0.1}, {0.1, 0.9, 0.1}, {0.9, 0.9, 0.1}, + {0.1, 0.1, 0.9}, {0.9, 0.1, 0.9}, {0.1, 0.9, 0.9}, {0.9, 0.9, 0.9} + }; + + private static final List blockTargets = new ArrayList<>(); + private static final Map blockFade = new HashMap<>(); + private static final Map entityFade = new HashMap<>(); + private static Level cachedLevel; + private static long lastRecomputeTime = Long.MIN_VALUE; + private static long lastRenderMs = 0L; + + private DiscoveryOverlayRenderer() { + } + + public static void render(PoseStack poseStack, float partialTick, Camera camera, Frustum frustum, MultiBufferSource.BufferSource bufferSource) { + Minecraft mc = Minecraft.getInstance(); + Player player = mc.player; + if (player == null || mc.level == null) { + blockFade.clear(); + entityFade.clear(); + return; + } + + double activeRange = FieldGuideScanManager.getInstance().getDiscoveryDistance(mc); + boolean active = activeRange > 0; + + Set currentBlocks; + Set currentEntities; + if (active) { + refreshBlockTargets(mc, player, activeRange, frustum); + FieldGuideScanner scanner = FieldGuideScanner.getInstance(); + BlockPos activePos = scanner.getScanningPos() != null ? scanner.getScanningPos() : scanner.getFadingPos(); + Entity activeEntity = scanner.getScanningEntity() != null ? scanner.getScanningEntity() : scanner.getFadingEntity(); + currentBlocks = new HashSet<>(blockTargets); + if (activePos != null) { + currentBlocks.remove(activePos); + } + currentEntities = new HashSet<>(gatherEntityTargets(mc, player, activeRange, activeEntity, frustum)); + } else { + currentBlocks = Collections.emptySet(); + currentEntities = Collections.emptySet(); + } + + long nowMs = System.currentTimeMillis(); + float step = lastRenderMs == 0L ? 0.0F : Math.min(1.0F, (nowMs - lastRenderMs) / FADE_MS); + lastRenderMs = nowMs; + advanceFade(blockFade, currentBlocks, step); + advanceEntityFade(entityFade, currentEntities, step); + + if (blockFade.isEmpty() && entityFade.isEmpty()) { + return; + } + + int color = ClientConfig.get().getScanOverlayColorInt(); + float r = ((color >> 16) & 0xFF) / 255.0F; + float g = ((color >> 8) & 0xFF) / 255.0F; + float b = (color & 0xFF) / 255.0F; + double base = ClientConfig.get().scanOverlayAlpha; + float wave = (float) (0.5 + 0.5 * Math.sin(nowMs / PULSE_SPEED)); + float pulsed = (float) (base * base * SUBTLE_FACTOR) * (PULSE_MIN + (1.0F - PULSE_MIN) * wave); + + Vec3 camPos = camera.getPosition(); + + RenderSystem.clear(GL11.GL_DEPTH_BUFFER_BIT, Minecraft.ON_OSX); + + renderBlocks(mc, poseStack, camPos, bufferSource, r, g, b, pulsed); + + for (Map.Entry e : entityFade.entrySet()) { + Entity entity = e.getKey(); + if (entity.isRemoved()) { + continue; + } + renderEntity(mc, poseStack, partialTick, camPos, bufferSource, entity, r, g, b, pulsed * e.getValue()); + } + } + + private static void advanceFade(Map map, Set current, float step) { + Iterator> it = map.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry e = it.next(); + if (current.contains(e.getKey())) { + e.setValue(Math.min(1.0F, e.getValue() + step)); + } else { + float v = e.getValue() - step; + if (v <= 0.0F) { + it.remove(); + } else { + e.setValue(v); + } + } + } + for (BlockPos pos : current) { + map.putIfAbsent(pos, step); + } + } + + private static void advanceEntityFade(Map map, Set current, float step) { + Iterator> it = map.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry e = it.next(); + if (e.getKey().isRemoved()) { + it.remove(); + continue; + } + if (current.contains(e.getKey())) { + e.setValue(Math.min(1.0F, e.getValue() + step)); + } else { + float v = e.getValue() - step; + if (v <= 0.0F) { + it.remove(); + } else { + e.setValue(v); + } + } + } + for (Entity entity : current) { + map.putIfAbsent(entity, step); + } + } + + private static List gatherEntityTargets(Minecraft mc, Player player, double searchRange, Entity activeEntity, Frustum frustum) { + List result = new ArrayList<>(); + AABB box = player.getBoundingBox().inflate(searchRange); + double maxSq = searchRange * searchRange; + Vec3 eye = player.getEyePosition(1.0F); + for (Entity raw : mc.level.getEntities(player, box, e -> true)) { + Entity entity = ScanOverlayRenderer.resolveEntity(raw); + if (entity == null || entity == player || entity == activeEntity) { + continue; + } + if (entity.distanceToSqr(eye.x, eye.y, eye.z) > maxSq) { + continue; + } + if (!frustum.isVisible(entity.getBoundingBox())) { + continue; + } + if (!isScannableEntity(entity)) { + continue; + } + if (!isEntityPartiallyVisible(mc.level, player, eye, entity)) { + continue; + } + result.add(entity); + } + return result; + } + + private static void refreshBlockTargets(Minecraft mc, Player player, double searchRange, Frustum frustum) { + Level level = mc.level; + if (level != cachedLevel) { + cachedLevel = level; + blockTargets.clear(); + lastRecomputeTime = Long.MIN_VALUE; + } + long now = level.getGameTime(); + if (now == lastRecomputeTime) { + return; + } + if (lastRecomputeTime != Long.MIN_VALUE && now - lastRecomputeTime < RECOMPUTE_INTERVAL) { + return; + } + lastRecomputeTime = now; + blockTargets.clear(); + + BlockPos center = player.blockPosition(); + int radius = (int) Math.ceil(searchRange); + double maxSq = searchRange * searchRange; + double px = player.getX(); + double py = player.getEyeY(); + double pz = player.getZ(); + int minY = Math.max(level.getMinBuildHeight(), center.getY() - radius); + int maxY = Math.min(level.getMaxBuildHeight() - 1, center.getY() + radius); + int chunkRadius = (radius >> 4) + 1; + int pcx = center.getX() >> 4; + int pcz = center.getZ() >> 4; + + Map typeCode = new IdentityHashMap<>(); + List found = new ArrayList<>(); + + List chunks = new ArrayList<>(); + for (int cx = pcx - chunkRadius; cx <= pcx + chunkRadius; cx++) { + for (int cz = pcz - chunkRadius; cz <= pcz + chunkRadius; cz++) { + chunks.add(new long[]{cx, cz}); + } + } + chunks.sort(Comparator.comparingDouble(c -> { + double ccx = (c[0] << 4) + 8 - px; + double ccz = (c[1] << 4) + 8 - pz; + return ccx * ccx + ccz * ccz; + })); + + outer: + for (long[] c : chunks) { + int cx = (int) c[0]; + int cz = (int) c[1]; + LevelChunk chunk = level.getChunk(cx, cz); + LevelChunkSection[] sections = chunk.getSections(); + int minSection = chunk.getMinSection(); + for (int si = 0; si < sections.length; si++) { + LevelChunkSection section = sections[si]; + if (section == null || section.hasOnlyAir()) { + continue; + } + int baseY = (minSection + si) << 4; + if (baseY + 15 < minY || baseY > maxY) { + continue; + } + for (int ly = 0; ly < 16; ly++) { + int wy = baseY + ly; + if (wy < minY || wy > maxY) { + continue; + } + for (int lx = 0; lx < 16; lx++) { + for (int lz = 0; lz < 16; lz++) { + BlockState state = section.getBlockState(lx, ly, lz); + if (state.isAir()) { + continue; + } + int wx = (cx << 4) + lx; + int wz = (cz << 4) + lz; + double dx = wx + 0.5 - px; + double dy = wy + 0.5 - py; + double dz = wz + 0.5 - pz; + if (dx * dx + dy * dy + dz * dz > maxSq) { + continue; + } + Block block = state.getBlock(); + Integer code = typeCode.get(block); + if (code == null) { + code = classifyBlock(block); + typeCode.put(block, code); + } + if (code == 0) { + continue; + } + if (!frustum.isVisible(new AABB(wx, wy, wz, wx + 1, wy + 1, wz + 1))) { + continue; + } + found.add(new BlockPos(wx, wy, wz)); + if (found.size() >= MAX_FOUND) { + break outer; + } + } + } + } + } + } + + found.sort(Comparator.comparingDouble(p -> p.distToCenterSqr(px, py, pz))); + Vec3 eye = player.getEyePosition(1.0F); + for (BlockPos pos : found) { + if (blockTargets.size() >= MAX_RENDERED) { + break; + } + if (!isBlockPartiallyVisible(level, player, eye, pos)) { + continue; + } + Integer code = typeCode.get(level.getBlockState(pos).getBlock()); + if (code != null && (code == 1 || (code == 2 && isCompositeScannableAt(mc, pos)))) { + blockTargets.add(pos); + } + } + } + + private static boolean isBlockPartiallyVisible(Level level, Player player, Vec3 eye, BlockPos pos) { + for (double[] o : SAMPLE_OFFSETS) { + Vec3 point = new Vec3(pos.getX() + o[0], pos.getY() + o[1], pos.getZ() + o[2]); + if (sightClear(level, player, eye, point, pos)) { + return true; + } + } + return false; + } + + private static boolean isEntityPartiallyVisible(Level level, Player player, Vec3 eye, Entity entity) { + AABB box = entity.getBoundingBox(); + Vec3[] points = { + box.getCenter(), + new Vec3(box.minX, box.minY, box.minZ), new Vec3(box.maxX, box.minY, box.minZ), + new Vec3(box.minX, box.maxY, box.minZ), new Vec3(box.maxX, box.maxY, box.minZ), + new Vec3(box.minX, box.minY, box.maxZ), new Vec3(box.maxX, box.minY, box.maxZ), + new Vec3(box.minX, box.maxY, box.maxZ), new Vec3(box.maxX, box.maxY, box.maxZ) + }; + for (Vec3 point : points) { + if (sightClear(level, player, eye, point, null)) { + return true; + } + } + return false; + } + + private static boolean sightClear(Level level, Player player, Vec3 from, Vec3 to, BlockPos targetPos) { + Vec3 dir = to.subtract(from); + if (dir.lengthSqr() < 1.0E-6) { + return true; + } + Vec3 step = dir.normalize().scale(0.05); + double toDistSq = from.distanceToSqr(to); + Vec3 start = from; + for (int i = 0; i < 80; i++) { + BlockHitResult hit = level.clip(new ClipContext(start, to, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, player)); + if (hit.getType() == HitResult.Type.MISS) { + return true; + } + BlockPos hp = hit.getBlockPos(); + if (targetPos != null && hp.equals(targetPos)) { + return true; + } + if (level.getBlockState(hp).canOcclude()) { + return false; + } + Vec3 next = hit.getLocation().add(step); + if (from.distanceToSqr(next) >= toDistSq) { + return true; + } + start = next; + } + return true; + } + + private static int classifyBlock(Block block) { + try { + ClientFieldGuideManager manager = ClientFieldGuideManager.getInstance(); + Object entry = manager.getEntryForTarget(block); + if (entry == null || manager.getCategoryForEntry(entry) == null) { + return 0; + } + ResourceLocation id = BuiltInRegistries.BLOCK.getKey(block); + if (!FieldGuideScanManager.needsVariantScan(entry, id)) { + return 0; + } + ResourceLocation entryId = ClientFieldGuideManager.getEntryId(entry); + if (entryId != null && !ProgressManager.getInstance().canScanToUnlock(entryId)) { + return 0; + } + return entry instanceof GuideEntry ge && ge.isComposite() ? 2 : 1; + } catch (Exception e) { + return 0; + } + } + + private static boolean isCompositeScannableAt(Minecraft mc, BlockPos pos) { + try { + Block block = mc.level.getBlockState(pos).getBlock(); + Object entry = FieldGuideRaytracer.getInstance().getContextAwareEntry(block, mc, pos); + if (entry == null) { + return false; + } + ClientFieldGuideManager manager = ClientFieldGuideManager.getInstance(); + if (manager.getCategoryForEntry(entry) == null) { + return false; + } + ResourceLocation rawId = BuiltInRegistries.BLOCK.getKey(block); + if (!FieldGuideScanManager.needsVariantScan(entry, rawId)) { + return false; + } + ResourceLocation entryId = ClientFieldGuideManager.getEntryId(entry); + return entryId == null || ProgressManager.getInstance().canScanToUnlock(entryId); + } catch (Exception e) { + return false; + } + } + + private static boolean isScannableEntity(Entity entity) { + try { + Minecraft mc = Minecraft.getInstance(); + EntityType type = entity.getType(); + Object key = entity instanceof ItemEntity itemEntity ? itemEntity.getItem().getItem() : type; + ClientFieldGuideManager manager = ClientFieldGuideManager.getInstance(); + Object entry = FieldGuideRaytracer.getInstance().getContextAwareEntry(key, mc, entity.blockPosition()); + if (entry == null || manager.getCategoryForEntry(entry) == null) { + return false; + } + ResourceLocation id = entity instanceof ItemEntity itemEntity + ? BuiltInRegistries.ITEM.getKey(itemEntity.getItem().getItem()) + : BuiltInRegistries.ENTITY_TYPE.getKey(type); + if (!FieldGuideScanManager.needsVariantScan(entry, id)) { + return false; + } + ResourceLocation entryId = ClientFieldGuideManager.getEntryId(entry); + return entryId == null || ProgressManager.getInstance().canScanToUnlock(entryId); + } catch (Exception e) { + return false; + } + } + + private static void renderBlocks(Minecraft mc, PoseStack poseStack, Vec3 camPos, MultiBufferSource.BufferSource bufferSource, + float r, float g, float b, float pulsed) { + setupBlockShaderUniforms(1.0F, 1.0F, 1.0F); + for (Map.Entry e : blockFade.entrySet()) { + emitBlock(mc, e.getKey(), poseStack, camPos, bufferSource, true, 1.0F, 1.0F, 1.0F, 1.0F); + } + bufferSource.endBatch(); + + setupBlockShaderUniforms(r, g, b); + for (Map.Entry e : blockFade.entrySet()) { + emitBlock(mc, e.getKey(), poseStack, camPos, bufferSource, false, r, g, b, pulsed * e.getValue()); + } + bufferSource.endBatch(); + } + + private static void setupBlockShaderUniforms(float r, float g, float b) { + if (ModRenderTypes.SCAN_BLOCK_SHADER != null) { + ModRenderTypes.SCAN_BLOCK_SHADER.getUniform("ScanLimitY").set(FULL_FILL); + ModRenderTypes.SCAN_BLOCK_SHADER.getUniform("InverseModelViewMat").set(new Matrix4f()); + if (ModRenderTypes.SCAN_BLOCK_SHADER.getUniform("ColorModulator") != null) { + ModRenderTypes.SCAN_BLOCK_SHADER.getUniform("ColorModulator").set(r, g, b, 1.0F); + } + } + } + + private static void emitBlock(Minecraft mc, BlockPos pos, PoseStack poseStack, Vec3 camPos, MultiBufferSource.BufferSource bufferSource, boolean depth, float r, float g, float b, float a) { + BlockState state = mc.level.getBlockState(pos); + if (state.isAir()) { + return; + } + Vec3 offset = state.getOffset(mc.level, pos); + double x = pos.getX() - camPos.x + offset.x; + double y = pos.getY() - camPos.y + offset.y; + double z = pos.getZ() - camPos.z + offset.z; + poseStack.pushPose(); + poseStack.translate(x, y, z); + drawBlock(mc, state, pos, poseStack, bufferSource, depth, r, g, b, a); + poseStack.popPose(); + } + + private static void drawBlock(Minecraft mc, BlockState state, BlockPos pos, PoseStack poseStack, + MultiBufferSource.BufferSource bufferSource, boolean depth, float r, float g, float b, float a) { + if (state.getRenderShape() == RenderShape.MODEL) { + RenderType type = ItemBlockRenderTypes.getRenderType(state, false); + RenderType wrapped = depth ? ModRenderTypes.wrapForDepth(type, false) : ModRenderTypes.wrapForScan(type, false); + VertexConsumer consumer = ScanOverlayRenderer.createTintedConsumer(bufferSource.getBuffer(wrapped), bufferSource, r, g, b, a); + ScanOverlayRenderer.renderBlockModelAsShell(mc, state, pos, poseStack, consumer, blockFade.keySet()); + } else { + MultiBufferSource source = requestedType -> ScanOverlayRenderer.createTintedConsumer( + bufferSource.getBuffer(depth ? ModRenderTypes.wrapForDepth(requestedType, false) : ModRenderTypes.wrapForScan(requestedType, false)), + bufferSource, r, g, b, a); + mc.getBlockRenderer().renderSingleBlock(state, poseStack, source, 15728880, OverlayTexture.pack(0, 10)); + } + } + + private static void renderEntity(Minecraft mc, PoseStack poseStack, float partialTick, Vec3 camPos, + MultiBufferSource.BufferSource bufferSource, Entity entity, float r, float g, float b, float a) { + double x = Mth.lerp(partialTick, entity.xOld, entity.getX()) - camPos.x; + double y = Mth.lerp(partialTick, entity.yOld, entity.getY()) - camPos.y; + double z = Mth.lerp(partialTick, entity.zOld, entity.getZ()) - camPos.z; + poseStack.pushPose(); + poseStack.translate(x, y, z); + + if (ModRenderTypes.SCAN_ENTITY_SHADER != null) { + ModRenderTypes.SCAN_ENTITY_SHADER.getUniform("ScanLimitY").set(FULL_FILL); + Matrix4f modelView = new Matrix4f(poseStack.last().pose()); + ModRenderTypes.SCAN_ENTITY_SHADER.getUniform("InverseModelViewMat").set(modelView.invert()); + if (ModRenderTypes.SCAN_ENTITY_SHADER.getUniform("ColorModulator") != null) { + ModRenderTypes.SCAN_ENTITY_SHADER.getUniform("ColorModulator").set(1.0F, 1.0F, 1.0F, 1.0F); + } + } + + float yaw = Mth.lerp(partialTick, entity.yRotO, entity.getYRot()); + + boolean isEtfLoaded = Services.PLATFORM.isModLoaded("entity_texture_features"); + if (isEtfLoaded) { + EtfCompat.preventRenderLayerTextureModify(); + } + + MultiBufferSource depthSource = new ScanOverlayRenderer.ScanBufferSourceWrapper(bufferSource, 1.0F, 1.0F, 1.0F, 1.0F, true); + mc.getEntityRenderDispatcher().render(entity, 0.0, 0.0, 0.0, yaw, partialTick, poseStack, depthSource, 15728880); + bufferSource.endBatch(); + + if (ModRenderTypes.SCAN_ENTITY_SHADER != null && ModRenderTypes.SCAN_ENTITY_SHADER.getUniform("ColorModulator") != null) { + ModRenderTypes.SCAN_ENTITY_SHADER.getUniform("ColorModulator").set(r, g, b, 1.0F); + } + + MultiBufferSource colorSource = new ScanOverlayRenderer.ScanBufferSourceWrapper(bufferSource, r, g, b, a, false); + mc.getEntityRenderDispatcher().render(entity, 0.0, 0.0, 0.0, yaw, partialTick, poseStack, colorSource, 15728880); + bufferSource.endBatch(); + + if (isEtfLoaded) { + EtfCompat.allowRenderLayerTextureModify(); + } + + poseStack.popPose(); + } +} \ No newline at end of file diff --git a/common/src/main/java/com/evandev/fieldguide/client/render/ScanOverlayRenderer.java b/common/src/main/java/com/evandev/fieldguide/client/render/ScanOverlayRenderer.java index 0d1812a0..29568d95 100644 --- a/common/src/main/java/com/evandev/fieldguide/client/render/ScanOverlayRenderer.java +++ b/common/src/main/java/com/evandev/fieldguide/client/render/ScanOverlayRenderer.java @@ -44,7 +44,7 @@ public class ScanOverlayRenderer { private static final float VERTICAL_BUFFER = 1.3f; - private static final VertexConsumer DUMMY_CONSUMER = new VertexConsumer() { + protected static final VertexConsumer DUMMY_CONSUMER = new VertexConsumer() { @Override public @NotNull VertexConsumer addVertex(float x, float y, float z) { return this; @@ -123,7 +123,7 @@ public static void render(PoseStack poseStack, float partialTick, Camera camera, } } - private static Entity resolveEntity(Entity entity) { + protected static Entity resolveEntity(Entity entity) { if (entity == null) return null; if (entity instanceof EnderDragonPart dragonPart) { return dragonPart.parentMob; @@ -139,7 +139,7 @@ private static Entity resolveEntity(Entity entity) { return entity; } - private static VertexConsumer createTintedConsumer(VertexConsumer delegate, MultiBufferSource provider, float r, float g, float b, float a) { + protected static VertexConsumer createTintedConsumer(VertexConsumer delegate, MultiBufferSource provider, float r, float g, float b, float a) { if (Services.PLATFORM.isModLoaded("entity_texture_features")) { return EtfCompat.createTintedConsumer(delegate, provider, r, g, b, a); } @@ -413,7 +413,7 @@ private static Set gatherTreeBlocks(Minecraft mc, BlockPos startPos) { return blocks; } - private static void renderBlockModelAsShell(Minecraft mc, BlockState state, BlockPos pos, PoseStack poseStack, VertexConsumer consumer, Set blocksToRender) { + protected static void renderBlockModelAsShell(Minecraft mc, BlockState state, BlockPos pos, PoseStack poseStack, VertexConsumer consumer, Set blocksToRender) { BakedModel model = mc.getBlockRenderer().getBlockModel(state); RandomSource random = RandomSource.create(); long seed = state.getSeed(pos); @@ -486,7 +486,7 @@ private static void renderEntityOverlay(PoseStack poseStack, float partialTick, poseStack.popPose(); } - private static class ScanBufferSourceWrapper implements MultiBufferSource { + protected static class ScanBufferSourceWrapper implements MultiBufferSource { private final MultiBufferSource.BufferSource delegate; private final float r, g, b, a; private final boolean isDepth; diff --git a/common/src/main/java/com/evandev/fieldguide/client/scan/manager/FieldGuideScanManager.java b/common/src/main/java/com/evandev/fieldguide/client/scan/manager/FieldGuideScanManager.java index 552b85c9..605528b7 100644 --- a/common/src/main/java/com/evandev/fieldguide/client/scan/manager/FieldGuideScanManager.java +++ b/common/src/main/java/com/evandev/fieldguide/client/scan/manager/FieldGuideScanManager.java @@ -6,6 +6,7 @@ import com.evandev.fieldguide.client.ClientFieldGuideManager; import com.evandev.fieldguide.client.FieldGuideClient; import com.evandev.fieldguide.client.progress.ProgressManager; +import com.evandev.fieldguide.item.ModItems; import com.evandev.fieldguide.compat.cobblemon.FieldGuideCobblemonCompat; import com.evandev.fieldguide.config.ClientConfig; import com.evandev.fieldguide.config.ServerConfig; @@ -68,8 +69,7 @@ public void onClientTick(Minecraft minecraft) { FieldGuideScanState state = FieldGuideScanState.getInstance(); - boolean hasSpyglass = isUsingSpyglass(minecraft.player); - boolean canScan = (hasSpyglass && ServerConfig.get().enableSpyglassScanning) || ServerConfig.get().enableNakedEyeScanning; + boolean canScan = getScanDistance(minecraft) > 0; boolean isScanningActive = canScan && !ServerConfig.get().disableScanning; if (FieldGuideClient.SCAN_KEY != null && !FieldGuideClient.SCAN_KEY.isUnbound()) { @@ -95,6 +95,53 @@ private boolean isUsingSpyglass(Player player) { return player.isScoping() || (player.isUsingItem() && player.getUseItem().is(ModTags.Items.SPYGLASSES)); } + private boolean holdsFieldGuide(Player player) { + if (ModItems.FIELD_GUIDE == null) return false; + return player.getMainHandItem().is(ModItems.FIELD_GUIDE.get()) || player.getOffhandItem().is(ModItems.FIELD_GUIDE.get()); + } + + private boolean holdsLens(Player player) { + return player.getMainHandItem().is(ModTags.Items.LENSES) || player.getOffhandItem().is(ModTags.Items.LENSES); + } + + private boolean isRightClickHeld() { + return Minecraft.getInstance().options.keyUse.isDown(); + } + + public double getScanDistance(Minecraft minecraft) { + Player player = minecraft.player; + if (player == null) return 0; + if (isUsingSpyglass(player) && ServerConfig.get().enableSpyglassScanning) { + return ServerConfig.get().spyglassScanDistance; + } + if (holdsLens(player) && isRightClickHeld()) { + return ServerConfig.get().lensScanDistance; + } + if (ServerConfig.get().enableFieldGuideScanning && holdsFieldGuide(player) && isRightClickHeld()) { + return ServerConfig.get().fieldGuideScanDistance; + } + if (ServerConfig.get().enableNakedEyeScanning) { + return ServerConfig.get().nakedEyeScanDistance; + } + return 0; + } + + public double getDiscoveryDistance(Minecraft minecraft) { + Player player = minecraft.player; + if (player == null) return 0; + double distance = 0; + if (isUsingSpyglass(player) && ServerConfig.get().enableSpyglassDiscovery) { + distance = Math.max(distance, ServerConfig.get().spyglassScanDistance); + } + if (holdsLens(player) && ServerConfig.get().enableLensDiscovery) { + distance = Math.max(distance, ServerConfig.get().lensDiscoveryDistance); + } + if (ServerConfig.get().enableNakedEyeDiscovery) { + distance = Math.max(distance, ServerConfig.get().nakedEyeDiscoveryDistance); + } + return distance; + } + public void handleTargetAcquisition(Minecraft minecraft, Object foundTarget, Object resolvedEntry, double hitDistSq, BlockHitResult blockHit) { FieldGuideScanState state = FieldGuideScanState.getInstance(); if (foundTarget == null) { @@ -110,9 +157,7 @@ public void handleTargetAcquisition(Minecraft minecraft, Object foundTarget, Obj return; } - double activeScanDist = (minecraft.player != null && isUsingSpyglass(minecraft.player) && ServerConfig.get().enableSpyglassScanning) - ? ServerConfig.get().spyglassScanDistance - : (ServerConfig.get().enableNakedEyeScanning ? ServerConfig.get().nakedEyeScanDistance : 0); + double activeScanDist = getScanDistance(minecraft); if (hitDistSq > (activeScanDist * activeScanDist)) { state.setOutOfRangeTarget(foundTarget); diff --git a/common/src/main/java/com/evandev/fieldguide/config/ClothConfigIntegration.java b/common/src/main/java/com/evandev/fieldguide/config/ClothConfigIntegration.java index bd1736c3..99f5926f 100644 --- a/common/src/main/java/com/evandev/fieldguide/config/ClothConfigIntegration.java +++ b/common/src/main/java/com/evandev/fieldguide/config/ClothConfigIntegration.java @@ -45,6 +45,12 @@ public static Screen createScreen(Screen parent) { .setSaveConsumer(newValue -> serverConfig.enableFieldGuideItem = newValue) .build()); + general.addEntry(entryBuilder.startBooleanToggle(Component.translatable("option.fieldguide.enable_lens_item"), serverConfig.enableLensItem) + .setDefaultValue(false) + .setTooltip(Component.translatable("option.fieldguide.enable_lens_item.tooltip")) + .setSaveConsumer(newValue -> serverConfig.enableLensItem = newValue) + .build()); + general.addEntry(entryBuilder.startBooleanToggle(Component.translatable("option.fieldguide.require_item_to_open"), serverConfig.requireItemToOpen) .setDefaultValue(false) .setTooltip(Component.translatable("option.fieldguide.require_item_to_open.tooltip")) @@ -115,6 +121,12 @@ public static Screen createScreen(Screen parent) { .setSaveConsumer(newValue -> serverConfig.spyglassScanDistance = newValue) .build()); + general.addEntry(entryBuilder.startBooleanToggle(Component.translatable("option.fieldguide.enable_spyglass_discovery"), serverConfig.enableSpyglassDiscovery) + .setDefaultValue(true) + .setTooltip(Component.translatable("option.fieldguide.enable_spyglass_discovery.tooltip")) + .setSaveConsumer(newValue -> serverConfig.enableSpyglassDiscovery = newValue) + .build()); + general.addEntry(entryBuilder.startDoubleField(Component.translatable("option.fieldguide.naked_eye_scan_distance"), serverConfig.nakedEyeScanDistance) .setDefaultValue(10.0D) .setMin(1.0D) @@ -123,6 +135,56 @@ public static Screen createScreen(Screen parent) { .setSaveConsumer(newValue -> serverConfig.nakedEyeScanDistance = newValue) .build()); + general.addEntry(entryBuilder.startBooleanToggle(Component.translatable("option.fieldguide.enable_naked_eye_discovery"), serverConfig.enableNakedEyeDiscovery) + .setDefaultValue(false) + .setTooltip(Component.translatable("option.fieldguide.enable_naked_eye_discovery.tooltip")) + .setSaveConsumer(newValue -> serverConfig.enableNakedEyeDiscovery = newValue) + .build()); + + general.addEntry(entryBuilder.startDoubleField(Component.translatable("option.fieldguide.naked_eye_discovery_distance"), serverConfig.nakedEyeDiscoveryDistance) + .setDefaultValue(8.0D) + .setMin(1.0D) + .setMax(256.0D) + .setTooltip(Component.translatable("option.fieldguide.naked_eye_discovery_distance.tooltip")) + .setSaveConsumer(newValue -> serverConfig.nakedEyeDiscoveryDistance = newValue) + .build()); + + general.addEntry(entryBuilder.startBooleanToggle(Component.translatable("option.fieldguide.enable_field_guide_scanning"), serverConfig.enableFieldGuideScanning) + .setDefaultValue(false) + .setTooltip(Component.translatable("option.fieldguide.enable_field_guide_scanning.tooltip")) + .setSaveConsumer(newValue -> serverConfig.enableFieldGuideScanning = newValue) + .build()); + + general.addEntry(entryBuilder.startDoubleField(Component.translatable("option.fieldguide.field_guide_scan_distance"), serverConfig.fieldGuideScanDistance) + .setDefaultValue(4.0D) + .setMin(1.0D) + .setMax(256.0D) + .setTooltip(Component.translatable("option.fieldguide.field_guide_scan_distance.tooltip")) + .setSaveConsumer(newValue -> serverConfig.fieldGuideScanDistance = newValue) + .build()); + + general.addEntry(entryBuilder.startDoubleField(Component.translatable("option.fieldguide.lens_scan_distance"), serverConfig.lensScanDistance) + .setDefaultValue(4.0D) + .setMin(1.0D) + .setMax(256.0D) + .setTooltip(Component.translatable("option.fieldguide.lens_scan_distance.tooltip")) + .setSaveConsumer(newValue -> serverConfig.lensScanDistance = newValue) + .build()); + + general.addEntry(entryBuilder.startBooleanToggle(Component.translatable("option.fieldguide.enable_lens_discovery"), serverConfig.enableLensDiscovery) + .setDefaultValue(true) + .setTooltip(Component.translatable("option.fieldguide.enable_lens_discovery.tooltip")) + .setSaveConsumer(newValue -> serverConfig.enableLensDiscovery = newValue) + .build()); + + general.addEntry(entryBuilder.startDoubleField(Component.translatable("option.fieldguide.lens_discovery_distance"), serverConfig.lensDiscoveryDistance) + .setDefaultValue(48.0D) + .setMin(1.0D) + .setMax(256.0D) + .setTooltip(Component.translatable("option.fieldguide.lens_discovery_distance.tooltip")) + .setSaveConsumer(newValue -> serverConfig.lensDiscoveryDistance = newValue) + .build()); + general.addEntry(entryBuilder.startBooleanToggle(Component.translatable("option.fieldguide.grant_xp_on_scan"), serverConfig.grantXpOnScan) .setDefaultValue(true) .setTooltip(Component.translatable("option.fieldguide.grant_xp_on_scan.tooltip")) diff --git a/common/src/main/java/com/evandev/fieldguide/config/ModConfig.java b/common/src/main/java/com/evandev/fieldguide/config/ModConfig.java index 94ff3232..bab7a234 100644 --- a/common/src/main/java/com/evandev/fieldguide/config/ModConfig.java +++ b/common/src/main/java/com/evandev/fieldguide/config/ModConfig.java @@ -71,12 +71,21 @@ private static void migrate() { if (json.has("keepSilhouetteWhenUnlocked")) server.keepSilhouetteWhenUnlocked = json.get("keepSilhouetteWhenUnlocked").getAsBoolean(); if (json.has("unlockAllVariants")) server.unlockAllVariants = json.get("unlockAllVariants").getAsBoolean(); if (json.has("enableFieldGuideItem")) server.enableFieldGuideItem = json.get("enableFieldGuideItem").getAsBoolean(); + if (json.has("enableLensItem")) server.enableLensItem = json.get("enableLensItem").getAsBoolean(); if (json.has("enableCopyingPages")) server.enableCopyingPages = json.get("enableCopyingPages").getAsBoolean(); if (json.has("hideTabsUntilUnlocked")) server.hideTabsUntilUnlocked = json.get("hideTabsUntilUnlocked").getAsBoolean(); if (json.has("enableSpyglassScanning")) server.enableSpyglassScanning = json.get("enableSpyglassScanning").getAsBoolean(); if (json.has("spyglassScanDistance")) server.spyglassScanDistance = json.get("spyglassScanDistance").getAsDouble(); + if (json.has("enableSpyglassDiscovery")) server.enableSpyglassDiscovery = json.get("enableSpyglassDiscovery").getAsBoolean(); if (json.has("enableNakedEyeScanning")) server.enableNakedEyeScanning = json.get("enableNakedEyeScanning").getAsBoolean(); if (json.has("nakedEyeScanDistance")) server.nakedEyeScanDistance = json.get("nakedEyeScanDistance").getAsDouble(); + if (json.has("enableNakedEyeDiscovery")) server.enableNakedEyeDiscovery = json.get("enableNakedEyeDiscovery").getAsBoolean(); + if (json.has("nakedEyeDiscoveryDistance")) server.nakedEyeDiscoveryDistance = json.get("nakedEyeDiscoveryDistance").getAsDouble(); + if (json.has("enableFieldGuideScanning")) server.enableFieldGuideScanning = json.get("enableFieldGuideScanning").getAsBoolean(); + if (json.has("fieldGuideScanDistance")) server.fieldGuideScanDistance = json.get("fieldGuideScanDistance").getAsDouble(); + if (json.has("lensScanDistance")) server.lensScanDistance = json.get("lensScanDistance").getAsDouble(); + if (json.has("enableLensDiscovery")) server.enableLensDiscovery = json.get("enableLensDiscovery").getAsBoolean(); + if (json.has("lensDiscoveryDistance")) server.lensDiscoveryDistance = json.get("lensDiscoveryDistance").getAsDouble(); if (json.has("showUndiscoveredNames")) server.showUndiscoveredNames = json.get("showUndiscoveredNames").getAsBoolean(); if (json.has("hideUndiscoveredFromSearch")) server.hideUndiscoveredFromSearch = json.get("hideUndiscoveredFromSearch").getAsBoolean(); if (json.has("scanSpeed")) server.scanSpeed = json.get("scanSpeed").getAsDouble(); diff --git a/common/src/main/java/com/evandev/fieldguide/config/ServerConfig.java b/common/src/main/java/com/evandev/fieldguide/config/ServerConfig.java index 0190fdb1..0ce1c7b8 100644 --- a/common/src/main/java/com/evandev/fieldguide/config/ServerConfig.java +++ b/common/src/main/java/com/evandev/fieldguide/config/ServerConfig.java @@ -30,6 +30,7 @@ public class ServerConfig { public boolean keepSilhouetteWhenUnlocked = false; public boolean unlockAllVariants = false; public boolean enableFieldGuideItem = false; + public boolean enableLensItem = false; public boolean requireItemToOpen = false; public boolean enableCopyingPages = true; @@ -37,10 +38,21 @@ public class ServerConfig { public boolean enableSpyglassScanning = true; public double spyglassScanDistance = 64.0D; + public boolean enableSpyglassDiscovery = true; public boolean enableNakedEyeScanning = false; public double nakedEyeScanDistance = 10.0D; + public boolean enableNakedEyeDiscovery = false; + public double nakedEyeDiscoveryDistance = 8.0D; + + public boolean enableFieldGuideScanning = false; + public double fieldGuideScanDistance = 4.0D; + + public double lensScanDistance = 6.0D; + public boolean enableLensDiscovery = true; + public double lensDiscoveryDistance = 24.0D; + public boolean showUndiscoveredNames = false; public boolean hideUndiscoveredFromSearch = false; diff --git a/common/src/main/java/com/evandev/fieldguide/item/ModItems.java b/common/src/main/java/com/evandev/fieldguide/item/ModItems.java index 3edf713e..13bb5b45 100644 --- a/common/src/main/java/com/evandev/fieldguide/item/ModItems.java +++ b/common/src/main/java/com/evandev/fieldguide/item/ModItems.java @@ -12,6 +12,7 @@ public class ModItems { public static Supplier FIELD_GUIDE; + public static Supplier LENS; public static Supplier PAGE; public static void init() { @@ -19,6 +20,10 @@ public static void init() { if (ServerConfig.get().enableFieldGuideItem) { Services.REGISTRY.registerToTab(ResourceKey.create(Registries.CREATIVE_MODE_TAB, ResourceLocation.withDefaultNamespace("tools_and_utilities")), FIELD_GUIDE); } + LENS = Services.REGISTRY.registerItem("lens", () -> new Item(new Item.Properties().stacksTo(1))); + if (ServerConfig.get().enableLensItem) { + Services.REGISTRY.registerToTab(ResourceKey.create(Registries.CREATIVE_MODE_TAB, ResourceLocation.withDefaultNamespace("tools_and_utilities")), LENS); + } PAGE = Services.REGISTRY.registerItem("page", () -> new PageItem(new Item.Properties().stacksTo(64))); Services.REGISTRY.registerToTab(ResourceKey.create(Registries.CREATIVE_MODE_TAB, ResourceLocation.withDefaultNamespace("tools_and_utilities")), PAGE); } diff --git a/common/src/main/java/com/evandev/fieldguide/mixin/client/LevelRendererMixin.java b/common/src/main/java/com/evandev/fieldguide/mixin/client/LevelRendererMixin.java index 955a62a3..78fbe0f2 100644 --- a/common/src/main/java/com/evandev/fieldguide/mixin/client/LevelRendererMixin.java +++ b/common/src/main/java/com/evandev/fieldguide/mixin/client/LevelRendererMixin.java @@ -1,5 +1,6 @@ package com.evandev.fieldguide.mixin.client; +import com.evandev.fieldguide.client.render.DiscoveryOverlayRenderer; import com.evandev.fieldguide.client.render.ScanOverlayRenderer; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Camera; @@ -8,6 +9,8 @@ import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -42,5 +45,17 @@ private void renderScanOverlays( camera, Minecraft.getInstance().renderBuffers().bufferSource() ); + + Frustum frustum = new Frustum(frustumMatrix, projectionMatrix); + Vec3 camPos = camera.getPosition(); + frustum.prepare(camPos.x, camPos.y, camPos.z); + + DiscoveryOverlayRenderer.render( + poseStack, + partialTick, + camera, + frustum, + Minecraft.getInstance().renderBuffers().bufferSource() + ); } } \ No newline at end of file diff --git a/common/src/main/resources/assets/fieldguide/lang/en_us.json b/common/src/main/resources/assets/fieldguide/lang/en_us.json index 7b163894..df3abb26 100644 --- a/common/src/main/resources/assets/fieldguide/lang/en_us.json +++ b/common/src/main/resources/assets/fieldguide/lang/en_us.json @@ -77,9 +77,33 @@ "option.fieldguide.enable_spyglass_scanning": "Enable Spyglass Scanning", "option.fieldguide.enable_spyglass_scanning.tooltip": "Allows players to scan entries from afar using a Spyglass.", + "option.fieldguide.enable_spyglass_discovery": "Enable Spyglass Discovery", + "option.fieldguide.enable_spyglass_discovery.tooltip": "Reveals undiscovered entries from afar using a Spyglass.", + + "option.fieldguide.enable_lens_discovery": "Enable Lens Discovery", + "option.fieldguide.enable_lens_discovery.tooltip": "Reveals nearby undiscovered entries while holding the Lens.", + "option.fieldguide.enable_naked_eye_scanning": "Enable Naked Eye Scanning", "option.fieldguide.enable_naked_eye_scanning.tooltip": "Allows players to scan entries just by looking at them without a Spyglass.", + "option.fieldguide.enable_naked_eye_discovery": "Enable Naked Eye Discovery", + "option.fieldguide.enable_naked_eye_discovery.tooltip": "Reveals nearby undiscovered entries.", + + "option.fieldguide.naked_eye_discovery_distance": "Naked Eye Discovery Distance", + "option.fieldguide.naked_eye_discovery_distance.tooltip": "The distance within which undiscovered entries are revealed.", + + "option.fieldguide.enable_field_guide_scanning": "Enable Field Guide Scanning", + "option.fieldguide.enable_field_guide_scanning.tooltip": "Allows players to scan entries by holding right-click while holding the Field Guide.", + + "option.fieldguide.field_guide_scan_distance": "Field Guide Scan Distance", + "option.fieldguide.field_guide_scan_distance.tooltip": "The maximum distance from which you can scan an entry while holding the Field Guide.", + + "option.fieldguide.lens_scan_distance": "Lens Scan Distance", + "option.fieldguide.lens_scan_distance.tooltip": "The maximum distance from which you can scan an entry while holding the Lens.", + + "option.fieldguide.lens_discovery_distance": "Lens Discovery Distance", + "option.fieldguide.lens_discovery_distance.tooltip": "The distance within which nearby undiscovered entries are revealed while holding the Lens.", + "option.fieldguide.grant_xp_on_scan": "Grant XP on Scan", "option.fieldguide.grant_xp_on_scan.tooltip": "If enabled, discovering a new entry via scanning will grant the player experience points.", @@ -197,6 +221,8 @@ "option.fieldguide.enable_field_guide_item": "Enable Field Guide Item (Requires Restart)", "option.fieldguide.enable_field_guide_item.tooltip": "If enabled, a craftable Field Guide item will be available in the game.", + "option.fieldguide.enable_lens_item": "Enable Lens Item (Requires Restart)", + "option.fieldguide.enable_lens_item.tooltip": "If enabled, a Lens item will be available in the game. Holding it reveals nearby undiscovered entries.", "option.fieldguide.enable_copying_pages": "Enable Copying Pages", "option.fieldguide.enable_copying_pages.tooltip": "If enabled, players can copy pages from their Field Guide to share with others.", @@ -226,6 +252,8 @@ "item.fieldguide.field_guide": "Field Guide", "item.fieldguide.field_guide.tooltip": "Right-click to open your Field Guide", + "item.fieldguide.lens": "Lens", + "item.fieldguide.lens.tooltip": "Hold to discover nearby Field Guide entries.", "item.fieldguide.page": "Field Guide Notes", "item.fieldguide.page.entry": "Entry: %s", diff --git a/common/src/main/resources/assets/fieldguide/models/item/lens.json b/common/src/main/resources/assets/fieldguide/models/item/lens.json new file mode 100644 index 00000000..3f026005 --- /dev/null +++ b/common/src/main/resources/assets/fieldguide/models/item/lens.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "fieldguide:item/lens" + } +} diff --git a/common/src/main/resources/assets/fieldguide/textures/item/lens.png b/common/src/main/resources/assets/fieldguide/textures/item/lens.png new file mode 100644 index 0000000000000000000000000000000000000000..8b20b3209d69799f70ddcf4a31658b6210f83858 GIT binary patch literal 491 zcmVyVT^ac<+XL+)L#-#5>x?SV6zjgZKM6Fr20L$!m-zU(t>-IQzPXA=t1sBw+F>Xa0PBr9t^Ia7 zk}nn4%q&Nz9*)4s0EFL10JtAwZaulf=I%EAp3A~wg~Rp%06~0)FBMKCa3XNTxOw`J zyc5OIsmHUmhrIs$5rBTrrM2J2mkR$^q6C~!qgm1bwuNrquIP=WIlXe-(zb=RE%a}p hX@B87Fty|V(oe6i+vb62!chPK002ovPDHLkV1kAd=jH$a literal 0 HcmV?d00001 diff --git a/common/src/main/resources/data/fieldguide/tags/item/lenses.json b/common/src/main/resources/data/fieldguide/tags/item/lenses.json new file mode 100644 index 00000000..edfdf554 --- /dev/null +++ b/common/src/main/resources/data/fieldguide/tags/item/lenses.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "fieldguide:lens" + ] +} From 51ecd0745a998011ad60a5d77ab9ade9534925f3 Mon Sep 17 00:00:00 2001 From: tazer Date: Sat, 6 Jun 2026 02:38:58 +0300 Subject: [PATCH 2/2] fix default config values --- .../com/evandev/fieldguide/config/ClothConfigIntegration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/com/evandev/fieldguide/config/ClothConfigIntegration.java b/common/src/main/java/com/evandev/fieldguide/config/ClothConfigIntegration.java index 99f5926f..28e0b6df 100644 --- a/common/src/main/java/com/evandev/fieldguide/config/ClothConfigIntegration.java +++ b/common/src/main/java/com/evandev/fieldguide/config/ClothConfigIntegration.java @@ -164,7 +164,7 @@ public static Screen createScreen(Screen parent) { .build()); general.addEntry(entryBuilder.startDoubleField(Component.translatable("option.fieldguide.lens_scan_distance"), serverConfig.lensScanDistance) - .setDefaultValue(4.0D) + .setDefaultValue(6.0D) .setMin(1.0D) .setMax(256.0D) .setTooltip(Component.translatable("option.fieldguide.lens_scan_distance.tooltip")) @@ -178,7 +178,7 @@ public static Screen createScreen(Screen parent) { .build()); general.addEntry(entryBuilder.startDoubleField(Component.translatable("option.fieldguide.lens_discovery_distance"), serverConfig.lensDiscoveryDistance) - .setDefaultValue(48.0D) + .setDefaultValue(24.0D) .setMin(1.0D) .setMax(256.0D) .setTooltip(Component.translatable("option.fieldguide.lens_discovery_distance.tooltip"))