feat: block zapper overlay (#208)
Co-authored-by: Linnea Gräf <nea@nea.moe>
This commit is contained in:
146
src/main/kotlin/features/items/BlockZapperOverlay.kt
Normal file
146
src/main/kotlin/features/items/BlockZapperOverlay.kt
Normal file
@@ -0,0 +1,146 @@
|
||||
package moe.nea.firmament.features.items
|
||||
|
||||
import io.github.notenoughupdates.moulconfig.ChromaColour
|
||||
import java.util.LinkedList
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.hit.HitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import moe.nea.firmament.annotations.Subscribe
|
||||
import moe.nea.firmament.events.ClientStartedEvent
|
||||
import moe.nea.firmament.events.WorldKeyboardEvent
|
||||
import moe.nea.firmament.events.WorldRenderLastEvent
|
||||
import moe.nea.firmament.features.FirmamentFeature
|
||||
import moe.nea.firmament.gui.config.ManagedConfig
|
||||
import moe.nea.firmament.util.MC
|
||||
import moe.nea.firmament.util.render.RenderInWorldContext
|
||||
import moe.nea.firmament.util.skyBlockId
|
||||
import moe.nea.firmament.util.skyblock.SkyBlockItems
|
||||
|
||||
object BlockZapperOverlay : FirmamentFeature {
|
||||
override val identifier: String
|
||||
get() = "block-zapper-overlay"
|
||||
|
||||
object TConfig : ManagedConfig(identifier, Category.ITEMS) {
|
||||
var blockZapperOverlay by toggle("block-zapper-overlay") { false }
|
||||
val color by colour("color") { ChromaColour.fromStaticRGB(160, 0, 0, 60) }
|
||||
var undoKey by keyBindingWithDefaultUnbound("undo-key")
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onInit(event: ClientStartedEvent) {
|
||||
}
|
||||
|
||||
override val config: ManagedConfig
|
||||
get() = TConfig
|
||||
|
||||
val bannedZapper: List<Block> = listOf<Block>(
|
||||
Blocks.WHEAT,
|
||||
Blocks.CARROTS,
|
||||
Blocks.POTATOES,
|
||||
Blocks.PUMPKIN,
|
||||
Blocks.PUMPKIN_STEM,
|
||||
Blocks.MELON,
|
||||
Blocks.MELON_STEM,
|
||||
Blocks.CACTUS,
|
||||
Blocks.SUGAR_CANE,
|
||||
Blocks.NETHER_WART,
|
||||
Blocks.TALL_GRASS,
|
||||
Blocks.SUNFLOWER,
|
||||
Blocks.FARMLAND,
|
||||
Blocks.BREWING_STAND,
|
||||
Blocks.SNOW,
|
||||
Blocks.RED_MUSHROOM,
|
||||
Blocks.BROWN_MUSHROOM,
|
||||
)
|
||||
|
||||
private val zapperOffsets: List<BlockPos> = listOf(
|
||||
BlockPos(0, 0, -1),
|
||||
BlockPos(0, 0, 1),
|
||||
BlockPos(-1, 0, 0),
|
||||
BlockPos(1, 0, 0),
|
||||
BlockPos(0, 1, 0),
|
||||
BlockPos(0, -1, 0)
|
||||
)
|
||||
|
||||
// Skidded from NEU
|
||||
// Credit: https://github.com/NotEnoughUpdates/NotEnoughUpdates/blob/9b1fcfebc646e9fb69f99006327faa3e734e5f51/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CustomItemEffects.java#L1281-L1355 (Modified)
|
||||
@Subscribe
|
||||
fun renderBlockZapperOverlay(event: WorldRenderLastEvent) {
|
||||
if (!TConfig.blockZapperOverlay) return
|
||||
val player = MC.player ?: return
|
||||
val world = player.world ?: return
|
||||
val heldItem = MC.stackInHand
|
||||
if (heldItem.skyBlockId != SkyBlockItems.BLOCK_ZAPPER) return
|
||||
val hitResult = MC.instance.crosshairTarget ?: return
|
||||
|
||||
val zapperBlocks: HashSet<BlockPos> = HashSet()
|
||||
val returnablePositions = LinkedList<BlockPos>()
|
||||
|
||||
if (hitResult is BlockHitResult && hitResult.type == HitResult.Type.BLOCK) {
|
||||
var pos: BlockPos = hitResult.blockPos
|
||||
val firstBlockState: BlockState = world.getBlockState(pos)
|
||||
val block = firstBlockState.block
|
||||
|
||||
val initialAboveBlock = world.getBlockState(pos.up()).block
|
||||
if (!bannedZapper.contains(initialAboveBlock) && !bannedZapper.contains(block)) {
|
||||
var i = 0
|
||||
while (i < 164) {
|
||||
zapperBlocks.add(pos)
|
||||
returnablePositions.remove(pos)
|
||||
|
||||
val availableNeighbors: MutableList<BlockPos> = ArrayList()
|
||||
|
||||
for (offset in zapperOffsets) {
|
||||
val newPos = pos.add(offset)
|
||||
|
||||
if (zapperBlocks.contains(newPos)) continue
|
||||
|
||||
val state: BlockState? = world.getBlockState(newPos)
|
||||
if (state != null && state.block === block) {
|
||||
val above = newPos.up()
|
||||
val aboveBlock = world.getBlockState(above).block
|
||||
if (!bannedZapper.contains(aboveBlock)) {
|
||||
availableNeighbors.add(newPos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (availableNeighbors.size >= 2) {
|
||||
returnablePositions.add(pos)
|
||||
pos = availableNeighbors[0]
|
||||
} else if (availableNeighbors.size == 1) {
|
||||
pos = availableNeighbors[0]
|
||||
} else if (returnablePositions.isEmpty()) {
|
||||
break
|
||||
} else {
|
||||
i--
|
||||
pos = returnablePositions.last()
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
RenderInWorldContext.renderInWorld(event) {
|
||||
if (MC.player?.isSneaking ?: false) {
|
||||
zapperBlocks.forEach {
|
||||
block(it, TConfig.color.getEffectiveColourRGB())
|
||||
}
|
||||
} else {
|
||||
sharedVoxelSurface(zapperBlocks, TConfig.color.getEffectiveColourRGB())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onWorldKeyboard(it: WorldKeyboardEvent) {
|
||||
if (!TConfig.undoKey.isBound) return
|
||||
if (!it.matches(TConfig.undoKey)) return
|
||||
if (MC.stackInHand.skyBlockId != SkyBlockItems.BLOCK_ZAPPER) return
|
||||
MC.sendCommand("undozap")
|
||||
}
|
||||
}
|
||||
@@ -88,6 +88,7 @@ object CustomRenderLayers {
|
||||
val COLORED_QUADS = RenderLayer.of(
|
||||
"firmament_quads",
|
||||
RenderLayer.DEFAULT_BUFFER_SIZE,
|
||||
false, true,
|
||||
CustomRenderPipelines.COLORED_OMNIPRESENT_QUADS,
|
||||
RenderLayer.MultiPhaseParameters.builder()
|
||||
.lightmap(RenderPhase.DISABLE_LIGHTMAP)
|
||||
|
||||
@@ -11,7 +11,6 @@ import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.RenderTickCounter
|
||||
import net.minecraft.client.render.VertexConsumer
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.VertexFormats
|
||||
import net.minecraft.client.texture.Sprite
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.text.Text
|
||||
@@ -20,7 +19,6 @@ import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import moe.nea.firmament.events.WorldRenderLastEvent
|
||||
import moe.nea.firmament.util.FirmFormatters
|
||||
import moe.nea.firmament.util.IntUtil.toRGBA
|
||||
import moe.nea.firmament.util.MC
|
||||
|
||||
@RenderContextDSL
|
||||
@@ -49,6 +47,38 @@ class RenderInWorldContext private constructor(
|
||||
matrixStack.pop()
|
||||
}
|
||||
|
||||
fun sharedVoxelSurface(blocks: Set<BlockPos>, color: Int) {
|
||||
val m = BlockPos.Mutable()
|
||||
val l = vertexConsumers.getBuffer(CustomRenderLayers.COLORED_QUADS)
|
||||
blocks.forEach {
|
||||
matrixStack.push()
|
||||
matrixStack.translate(it.x.toFloat(), it.y.toFloat(), it.z.toFloat())
|
||||
val p = matrixStack.peek().positionMatrix
|
||||
m.set(it)
|
||||
if (m.setX(it.x + 1) !in blocks) {
|
||||
buildFaceXP(p, l, color)
|
||||
}
|
||||
if (m.setX(it.x - 1) !in blocks) {
|
||||
buildFaceXN(p, l, color)
|
||||
}
|
||||
m.set(it)
|
||||
if (m.setY(it.y + 1) !in blocks) {
|
||||
buildFaceYP(p, l, color)
|
||||
}
|
||||
if (m.setY(it.y - 1) !in blocks) {
|
||||
buildFaceYN(p, l, color)
|
||||
}
|
||||
m.set(it)
|
||||
if (m.setZ(it.z + 1) !in blocks) {
|
||||
buildFaceZP(p, l, color)
|
||||
}
|
||||
if (m.setZ(it.z - 1) !in blocks) {
|
||||
buildFaceZN(p, l, color)
|
||||
}
|
||||
matrixStack.pop()
|
||||
}
|
||||
}
|
||||
|
||||
enum class VerticalAlign {
|
||||
TOP, BOTTOM, CENTER;
|
||||
|
||||
@@ -205,41 +235,56 @@ class RenderInWorldContext private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildCube(matrix: Matrix4f, buf: VertexConsumer, colorInt: Int) {
|
||||
val (r, g, b, a) = colorInt.toRGBA()
|
||||
|
||||
// Y-
|
||||
buf.vertex(matrix, 0F, 0F, 0F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 0F, 0F, 1F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 1F, 0F, 1F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 1F, 0F, 0F).color(r, g, b, a)
|
||||
// Y+
|
||||
buf.vertex(matrix, 0F, 1F, 0F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 1F, 1F, 0F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 1F, 1F, 1F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 0F, 1F, 1F).color(r, g, b, a)
|
||||
// X-
|
||||
buf.vertex(matrix, 0F, 0F, 0F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 0F, 0F, 1F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 0F, 1F, 1F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 0F, 1F, 0F).color(r, g, b, a)
|
||||
// X+
|
||||
buf.vertex(matrix, 1F, 0F, 0F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 1F, 1F, 0F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 1F, 1F, 1F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 1F, 0F, 1F).color(r, g, b, a)
|
||||
// Z-
|
||||
buf.vertex(matrix, 0F, 0F, 0F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 1F, 0F, 0F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 1F, 1F, 0F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 0F, 1F, 0F).color(r, g, b, a)
|
||||
// Z+
|
||||
buf.vertex(matrix, 0F, 0F, 1F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 0F, 1F, 1F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 1F, 1F, 1F).color(r, g, b, a)
|
||||
buf.vertex(matrix, 1F, 0F, 1F).color(r, g, b, a)
|
||||
private fun buildFaceZP(matrix: Matrix4f, buf: VertexConsumer, rgba: Int) {
|
||||
buf.vertex(matrix, 0F, 0F, 1F).color(rgba)
|
||||
buf.vertex(matrix, 0F, 1F, 1F).color(rgba)
|
||||
buf.vertex(matrix, 1F, 1F, 1F).color(rgba)
|
||||
buf.vertex(matrix, 1F, 0F, 1F).color(rgba)
|
||||
}
|
||||
|
||||
private fun buildFaceZN(matrix: Matrix4f, buf: VertexConsumer, rgba: Int) {
|
||||
buf.vertex(matrix, 0F, 0F, 0F).color(rgba)
|
||||
buf.vertex(matrix, 1F, 0F, 0F).color(rgba)
|
||||
buf.vertex(matrix, 1F, 1F, 0F).color(rgba)
|
||||
buf.vertex(matrix, 0F, 1F, 0F).color(rgba)
|
||||
}
|
||||
|
||||
private fun buildFaceXP(matrix: Matrix4f, buf: VertexConsumer, rgba: Int) {
|
||||
buf.vertex(matrix, 1F, 0F, 0F).color(rgba)
|
||||
buf.vertex(matrix, 1F, 1F, 0F).color(rgba)
|
||||
buf.vertex(matrix, 1F, 1F, 1F).color(rgba)
|
||||
buf.vertex(matrix, 1F, 0F, 1F).color(rgba)
|
||||
}
|
||||
|
||||
private fun buildFaceXN(matrix: Matrix4f, buf: VertexConsumer, rgba: Int) {
|
||||
buf.vertex(matrix, 0F, 0F, 0F).color(rgba)
|
||||
buf.vertex(matrix, 0F, 0F, 1F).color(rgba)
|
||||
buf.vertex(matrix, 0F, 1F, 1F).color(rgba)
|
||||
buf.vertex(matrix, 0F, 1F, 0F).color(rgba)
|
||||
}
|
||||
|
||||
private fun buildFaceYN(matrix: Matrix4f, buf: VertexConsumer, rgba: Int) {
|
||||
buf.vertex(matrix, 0F, 0F, 0F).color(rgba)
|
||||
buf.vertex(matrix, 0F, 0F, 1F).color(rgba)
|
||||
buf.vertex(matrix, 1F, 0F, 1F).color(rgba)
|
||||
buf.vertex(matrix, 1F, 0F, 0F).color(rgba)
|
||||
}
|
||||
|
||||
private fun buildFaceYP(matrix: Matrix4f, buf: VertexConsumer, rgba: Int) {
|
||||
buf.vertex(matrix, 0F, 1F, 0F).color(rgba)
|
||||
buf.vertex(matrix, 1F, 1F, 0F).color(rgba)
|
||||
buf.vertex(matrix, 1F, 1F, 1F).color(rgba)
|
||||
buf.vertex(matrix, 0F, 1F, 1F).color(rgba)
|
||||
}
|
||||
|
||||
private fun buildCube(matrix4f: Matrix4f, buf: VertexConsumer, rgba: Int) {
|
||||
buildFaceXP(matrix4f, buf, rgba)
|
||||
buildFaceXN(matrix4f, buf, rgba)
|
||||
buildFaceYP(matrix4f, buf, rgba)
|
||||
buildFaceYN(matrix4f, buf, rgba)
|
||||
buildFaceZP(matrix4f, buf, rgba)
|
||||
buildFaceZN(matrix4f, buf, rgba)
|
||||
}
|
||||
|
||||
fun renderInWorld(event: WorldRenderLastEvent, block: RenderInWorldContext. () -> Unit) {
|
||||
// TODO: there should be *no more global state*. the only thing we should be doing is render layers. that includes settings like culling, blending, shader color, and depth testing
|
||||
|
||||
@@ -19,5 +19,6 @@ object SkyBlockItems {
|
||||
val BONE_BOOMERANG = SkyblockId("BONE_BOOMERANG")
|
||||
val STARRED_BONE_BOOMERANG = SkyblockId("STARRED_BONE_BOOMERANG")
|
||||
val TRIBAL_SPEAR = SkyblockId("TRIBAL_SPEAR")
|
||||
val BLOCK_ZAPPER = SkyblockId("BLOCK_ZAPPER")
|
||||
val HUNTING_TOOLKIT = SkyblockId("HUNTING_TOOLKIT")
|
||||
}
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
"firmament.config.auto-completions.warp-complete.description": "Auto complete warp destinations in chat. This may include warps you have not yet unlocked.",
|
||||
"firmament.config.auto-completions.warp-is": "Redirect /warp is to /warp island",
|
||||
"firmament.config.auto-completions.warp-is.description": "Redirects /warp is to /warp island, since hypixel does not recognize /warp is as a warp destination.",
|
||||
"firmament.config.block-zapper-overlay": "Block Zapper Overlay",
|
||||
"firmament.config.block-zapper-overlay.block-zapper-overlay": "Block Zapper Overlay",
|
||||
"firmament.config.block-zapper-overlay.block-zapper-overlay.description": "Shows what blocks will be zapped",
|
||||
"firmament.config.block-zapper-overlay.color": "Colour",
|
||||
"firmament.config.block-zapper-overlay.color.description": "The color that the blocks will be highlighted in",
|
||||
"firmament.config.block-zapper-overlay.undo-key": "Undo Keybind",
|
||||
"firmament.config.block-zapper-overlay.undo-key.description": "Keybind to undo your zap",
|
||||
"firmament.config.bonemerang-overlay": "Bonemerang Overlay",
|
||||
"firmament.config.bonemerang-overlay.bonemerang-overlay": "Bonemerang Overlay",
|
||||
"firmament.config.bonemerang-overlay.bonemerang-overlay-hud": "Bonemerang Overlay Hud",
|
||||
|
||||
Reference in New Issue
Block a user