diff --git a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt index 43a84aa74..5f6c60746 100644 --- a/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt +++ b/common/src/main/kotlin/com/lambda/graphics/renderer/esp/DirectionMask.kt @@ -19,8 +19,6 @@ package com.lambda.graphics.renderer.esp import com.lambda.util.world.FastVector import com.lambda.util.world.offset -import com.lambda.util.world.toBlockPos -import com.lambda.util.world.toFastVec import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction @@ -43,8 +41,15 @@ object DirectionMask { fun Int.exclude(direction: Direction) = exclude(direction.mask) fun Int.hasDirection(dir: Int) = (this and dir) != 0 - fun buildSideMesh(position: BlockPos, filter: (BlockPos) -> Boolean) = - buildSideMesh(position.toFastVec()) { filter(it.toBlockPos()) } + fun buildSideMesh(position: BlockPos, filter: (BlockPos) -> Boolean): Int { + var sides = ALL + + Direction.entries + .filter { filter(position.offset(it)) } + .forEach { sides = sides.exclude(it.mask) } + + return sides + } fun buildSideMesh(position: FastVector, filter: (FastVector) -> Boolean): Int { var sides = ALL diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt index 74d4930b7..09a849f2e 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/render/BlockESP.kt @@ -28,8 +28,7 @@ import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafe import com.lambda.util.extension.blockColor -import com.lambda.util.extension.blockFilledMesh -import com.lambda.util.extension.blockOutlineMesh +import com.lambda.util.extension.outlineShape import com.lambda.util.extension.getBlockState import com.lambda.util.world.fastVectorOf import com.lambda.util.world.toBlockPos @@ -91,12 +90,11 @@ object BlockESP : Module( pos: BlockPos, sides: Int, ) = runSafe { - val filledMesh = blockFilledMesh(state, pos) - val outlineMesh = blockOutlineMesh(state, pos) + val shape = outlineShape(state, pos) val blockColor = blockColor(state, pos) - if (drawFaces) buildFilledMesh(filledMesh, if (useBlockColor) blockColor else faceColor, sides) - if (drawOutlines) buildOutlineMesh(outlineMesh, if (useBlockColor) blockColor else outlineColor, sides, outlineMode) + if (drawFaces) buildFilledMesh(shape, if (useBlockColor) blockColor else faceColor, sides) + if (drawOutlines) buildOutlineMesh(shape, if (useBlockColor) blockColor else outlineColor, sides, outlineMode) } private fun rebuildMesh(from: Any, to: Any): Unit = esp.rebuild() diff --git a/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt new file mode 100644 index 000000000..5fac5a4de --- /dev/null +++ b/common/src/main/kotlin/com/lambda/module/modules/render/StorageESP.kt @@ -0,0 +1,196 @@ +/* + * Copyright 2025 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.module.modules.render + +import com.lambda.context.SafeContext +import com.lambda.event.events.RenderEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.graphics.renderer.esp.DirectionMask +import com.lambda.graphics.renderer.esp.DirectionMask.buildSideMesh +import com.lambda.graphics.renderer.esp.builders.buildFilled +import com.lambda.graphics.renderer.esp.builders.buildFilledMesh +import com.lambda.graphics.renderer.esp.builders.buildOutline +import com.lambda.graphics.renderer.esp.builders.buildOutlineMesh +import com.lambda.graphics.renderer.esp.impl.StaticESPRenderer +import com.lambda.module.Module +import com.lambda.module.tag.ModuleTag +import com.lambda.threading.runSafe +import com.lambda.util.extension.blockColor +import com.lambda.util.extension.outlineShape +import com.lambda.util.math.setAlpha +import com.lambda.util.world.blockEntitySearch +import com.lambda.util.world.entitySearch +import net.minecraft.block.entity.BarrelBlockEntity +import net.minecraft.block.entity.BlastFurnaceBlockEntity +import net.minecraft.block.entity.BlockEntity +import net.minecraft.block.entity.BrewingStandBlockEntity +import net.minecraft.block.entity.ChestBlockEntity +import net.minecraft.block.entity.DispenserBlockEntity +import net.minecraft.block.entity.EnderChestBlockEntity +import net.minecraft.block.entity.FurnaceBlockEntity +import net.minecraft.block.entity.HopperBlockEntity +import net.minecraft.block.entity.ShulkerBoxBlockEntity +import net.minecraft.block.entity.SmokerBlockEntity +import net.minecraft.entity.Entity +import net.minecraft.entity.decoration.ItemFrameEntity +import net.minecraft.entity.vehicle.AbstractMinecartEntity +import net.minecraft.entity.vehicle.MinecartEntity +import net.minecraft.util.math.BlockPos +import java.awt.Color + +object StorageESP : Module( + name = "StorageESP", + description = "Render storage blocks/entities", + defaultTags = setOf(ModuleTag.RENDER), +) { + private val page by setting("Page", Page.Render) + + /* General settings */ + private val distance by setting("Distance", 64.0, 10.0..256.0, 1.0, "Maximum distance for rendering") { page == Page.General } + + /* Render settings */ + private var drawFaces: Boolean by setting("Draw Faces", true, "Draw faces of blocks") { page == Page.Render }.apply { onValueSet { _, to -> if (!to) drawOutlines = true } } + private var drawOutlines: Boolean by setting("Draw Outlines", true, "Draw outlines of blocks") { page == Page.Render }.apply { onValueSet { _, to -> if (!to) drawFaces = true } } + private val outlineMode by setting("Outline Mode", DirectionMask.OutlineMode.AND, "Outline mode") { page == Page.Render } + private val mesh by setting("Mesh", true, "Connect similar adjacent blocks") { page == Page.Render } + + /* Color settings */ + private val useBlockColor by setting("Use Block Color", true, "Use the color of the block instead") { page == Page.Color } + private val alpha by setting("Alpha", 0.3, 0.1..1.0, 0.05) { page == Page.Color } + + // TODO: + // val blockColors by setting("Block Colors", mapOf()) { page == Page.Color && !useBlockColor } + // val renders by setting("Render Blocks", mapOf()) { page == Page.General } + // + // TODO: Create enum of MapColors + + // I used this to extract the colors as rgb format + //> function extract(color) { + // ... console.log((color >> 16) & 0xFF) + // ... console.log((color >> 8) & 0xFF) + // ... console.log(color & 0xFF) + // ... } + + private val barrelColor by setting("Barrel Color", Color(143, 119, 72)) { page == Page.Color && !useBlockColor } + private val blastFurnaceColor by setting("Blast Furnace Color", Color(153, 153, 153)) { page == Page.Color && !useBlockColor } + private val brewingStandColor by setting("Brewing Stand Color", Color(167, 167, 167)) + private val chestColor by setting("Chest Color", Color(216, 127, 51)) { page == Page.Color && !useBlockColor } + private val dispenserColor by setting("Dispenser Color", Color(153, 153, 153)) { page == Page.Color && !useBlockColor } + private val enderChestColor by setting("Ender Chest Color", Color(127, 63, 178)) { page == Page.Color && !useBlockColor } + private val furnaceColor by setting("Furnace Color", Color(153, 153, 153)) { page == Page.Color && !useBlockColor } + private val hopperColor by setting("Hopper Color", Color(76, 76, 76)) { page == Page.Color && !useBlockColor } + private val smokerColor by setting("Smoker Color", Color(112, 112, 112)) { page == Page.Color && !useBlockColor } + private val shulkerColor by setting("Shulker Color", Color(178, 76, 216)) { page == Page.Color && !useBlockColor } + private val itemFrameColor by setting("Item Frame Color", Color(216, 127, 51)) { page == Page.Color && !useBlockColor } + private val cartColor by setting("Minecart Color", Color(102, 127, 51)) { page == Page.Color && !useBlockColor } + + private val entities = setOf( + BarrelBlockEntity::class, + BlastFurnaceBlockEntity::class, + BrewingStandBlockEntity::class, + ChestBlockEntity::class, + DispenserBlockEntity::class, + EnderChestBlockEntity::class, + FurnaceBlockEntity::class, + HopperBlockEntity::class, + SmokerBlockEntity::class, + ShulkerBoxBlockEntity::class, + AbstractMinecartEntity::class, + ItemFrameEntity::class, + MinecartEntity::class, + ) + + init { + listen { event -> + blockEntitySearch(distance) + .filter { it::class in entities } + .forEach { event.renderer.build(it, it.pos, excludedSides(it)) } + + val mineCarts = entitySearch(distance) + val itemFrames = entitySearch(distance) + (mineCarts + itemFrames) + .forEach { event.renderer.build(it, DirectionMask.ALL) } + } + } + + private fun SafeContext.excludedSides(blockEntity: BlockEntity): Int { + val isFullCube = blockEntity.cachedState.isFullCube(world, blockEntity.pos) + return if (mesh && isFullCube) { + buildSideMesh(blockEntity.pos) { neighbor -> + val other = world.getBlockEntity(neighbor) ?: return@buildSideMesh false + val otherFullCube = other.cachedState.isFullCube(world, other.pos) + val sameType = blockEntity.cachedState.block == other.cachedState.block + val searchedFor = other::class in entities + + searchedFor && otherFullCube && sameType + } + } else DirectionMask.ALL + } + + private fun StaticESPRenderer.build( + block: BlockEntity, + pos: BlockPos, + sides: Int, + ) = runSafe { + val color = if (useBlockColor) { + blockColor(block.cachedState, pos) + } else getBlockEntityColor(block) ?: return@runSafe + val shape = outlineShape(block.cachedState, pos) + + if (drawFaces) buildFilledMesh(shape, color.setAlpha(alpha), sides) + if (drawOutlines) buildOutlineMesh(shape, color, sides, outlineMode) + } + + private fun StaticESPRenderer.build( + entity: Entity, + sides: Int, + ) = runSafe { + val color = getEntityColor(entity) ?: return@runSafe + + if (drawFaces) buildFilled(entity.boundingBox, color.setAlpha(alpha), sides) + if (drawOutlines) buildOutline(entity.boundingBox, color, sides, outlineMode) + } + + private fun getBlockEntityColor(block: BlockEntity?) = + when (block) { + is BarrelBlockEntity -> barrelColor + is BlastFurnaceBlockEntity -> blastFurnaceColor + is BrewingStandBlockEntity -> brewingStandColor + is ChestBlockEntity -> chestColor + is DispenserBlockEntity -> dispenserColor + is EnderChestBlockEntity -> enderChestColor + is FurnaceBlockEntity -> furnaceColor + is HopperBlockEntity -> hopperColor + is SmokerBlockEntity -> smokerColor + is ShulkerBoxBlockEntity -> shulkerColor + else -> null + } + + private fun getEntityColor(entity: Entity?) = + when (entity) { + is AbstractMinecartEntity -> cartColor + is ItemFrameEntity -> itemFrameColor + else -> null + } + + private enum class Page { + General, + Render, + Color + } +} diff --git a/common/src/main/kotlin/com/lambda/util/extension/World.kt b/common/src/main/kotlin/com/lambda/util/extension/World.kt index 623df5a66..de812386a 100644 --- a/common/src/main/kotlin/com/lambda/util/extension/World.kt +++ b/common/src/main/kotlin/com/lambda/util/extension/World.kt @@ -36,10 +36,10 @@ import net.minecraft.world.World import java.awt.Color import kotlin.experimental.and -fun SafeContext.blockFilledMesh(state: BlockState, pos: BlockPos) = +fun SafeContext.collisionShape(state: BlockState, pos: BlockPos) = state.getCollisionShape(world, pos).offset(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) -fun SafeContext.blockOutlineMesh(state: BlockState, pos: BlockPos) = +fun SafeContext.outlineShape(state: BlockState, pos: BlockPos) = state.getOutlineShape(world, pos).offset(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) fun SafeContext.blockColor(state: BlockState, pos: BlockPos) = @@ -68,6 +68,7 @@ fun World.getFluidState(x: Int, y: Int, z: Int): FluidState { } fun World.getBlockState(vec: FastVector): BlockState = getBlockState(vec.x, vec.y, vec.z) +fun World.getBlockEntity(vec: FastVector) = getBlockEntity(vec.toBlockPos()) fun World.getFluidState(vec: FastVector): FluidState = getFluidState(vec.x, vec.y, vec.z) private fun positionFromIndex(width: Int, length: Int, index: Int): FastVector { diff --git a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt index aeb148d44..b37145069 100644 --- a/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt +++ b/common/src/main/kotlin/com/lambda/util/world/WorldDsl.kt @@ -124,7 +124,7 @@ inline fun SafeContext.blockEntitySearch( range: Double = 64.0, pos: BlockPos = player.blockPos, noinline filter: (T) -> Boolean = { true }, -) = internalGetBlockEntities(pos.toFastVec(), range, predicate = filter) +) = internalGetBlockEntities(pos.toFastVec(), range, predicate = filter).toSet() @DslMarker annotation class EntityMarker @@ -201,7 +201,7 @@ inline fun SafeContext.fastEntitySearch( range: Double, pos: BlockPos = player.blockPos, noinline filter: (T) -> Boolean = { true }, -) = internalGetFastEntities(pos.toFastVec(), range, predicate = filter) +) = internalGetFastEntities(pos.toFastVec(), range, predicate = filter).toSet() @DslMarker annotation class FluidMarker