Compare commits
2 Commits
master
...
rewrite-pr
Author | SHA1 | Date | |
---|---|---|---|
2946914101 | |||
09ed358681 |
@ -1,3 +1,8 @@
|
||||
# SkyBlockTweaks
|
||||
|
||||
My QOL mod for Hypixel Skyblock
|
||||
My QOL mod for Hypixel Skyblock
|
||||
|
||||
## TODO (Asin these WILL be implemented):
|
||||
- Add route builder history GUI/Command or something
|
||||
- Make Route Builder export to Skytils/CrystalHollowWaypoints/Coleweight
|
||||
- Optimize Block Outline Rendering
|
@ -1,4 +1,5 @@
|
||||
import org.apache.commons.lang3.SystemUtils
|
||||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
idea
|
||||
@ -9,25 +10,24 @@ plugins {
|
||||
kotlin("jvm") version "2.0.0"
|
||||
}
|
||||
|
||||
//Constants:
|
||||
|
||||
val baseGroup: String by project
|
||||
val mcVersion: String by project
|
||||
val version: String by project
|
||||
val mixinGroup = "$baseGroup.mixin"
|
||||
val versionBase: String by project
|
||||
val buildNumber: String by project
|
||||
val modid: String by project
|
||||
val mixinGroup = "$baseGroup.mixin"
|
||||
|
||||
group = baseGroup
|
||||
version = "alpha_$versionBase-b$buildNumber"
|
||||
|
||||
// Toolchains:
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(8))
|
||||
}
|
||||
|
||||
// Minecraft configuration:
|
||||
loom {
|
||||
log4jConfigs.from(file("log4j2.xml"))
|
||||
launchConfigs {
|
||||
"client" {
|
||||
// If you don't want mixins, remove these lines
|
||||
property("mixin.debug", "true")
|
||||
arg("--tweakClass", "org.spongepowered.asm.launch.MixinTweaker")
|
||||
}
|
||||
@ -35,7 +35,6 @@ loom {
|
||||
runConfigs {
|
||||
"client" {
|
||||
if (SystemUtils.IS_OS_MAC_OSX) {
|
||||
// This argument causes a crash on macOS
|
||||
vmArgs.remove("-XstartOnFirstThread")
|
||||
}
|
||||
}
|
||||
@ -43,10 +42,8 @@ loom {
|
||||
}
|
||||
forge {
|
||||
pack200Provider.set(dev.architectury.pack200.java.Pack200Adapter())
|
||||
// If you don't want mixins, remove this lines
|
||||
mixinConfig("mixins.$modid.json")
|
||||
}
|
||||
// If you don't want mixins, remove these lines
|
||||
mixin {
|
||||
defaultRefmapName.set("mixins.$modid.refmap.json")
|
||||
}
|
||||
@ -62,12 +59,9 @@ sourceSets.main {
|
||||
kotlin.destinationDirectory.set(java.destinationDirectory)
|
||||
}
|
||||
|
||||
// Dependencies:
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://repo.spongepowered.org/maven/")
|
||||
// If you don't want to log in with your real minecraft account, remove this line
|
||||
maven("https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1")
|
||||
}
|
||||
|
||||
@ -82,30 +76,24 @@ dependencies {
|
||||
|
||||
shadowImpl(kotlin("stdlib-jdk8"))
|
||||
|
||||
// If you don't want mixins, remove these lines
|
||||
shadowImpl("org.spongepowered:mixin:0.7.11-SNAPSHOT") {
|
||||
isTransitive = false
|
||||
}
|
||||
annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT")
|
||||
|
||||
// If you don't want to log in with your real minecraft account, remove this line
|
||||
runtimeOnly("me.djtheredstoner:DevAuth-forge-legacy:1.2.1")
|
||||
|
||||
}
|
||||
|
||||
// Tasks:
|
||||
|
||||
tasks.withType(JavaCompile::class) {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
tasks.withType(org.gradle.jvm.tasks.Jar::class) {
|
||||
archiveBaseName.set(modid)
|
||||
archiveVersion.set("$version")
|
||||
manifest.attributes.run {
|
||||
this["FMLCorePluginContainsFMLMod"] = "true"
|
||||
this["ForceLoadAsMod"] = "true"
|
||||
|
||||
// If you don't want mixins, remove these lines
|
||||
this["TweakClass"] = "org.spongepowered.asm.launch.MixinTweaker"
|
||||
this["MixinConfigs"] = "mixins.$modid.json"
|
||||
}
|
||||
@ -124,7 +112,6 @@ tasks.processResources {
|
||||
rename("(.+_at.cfg)", "META-INF/$1")
|
||||
}
|
||||
|
||||
|
||||
val remapJar by tasks.named<net.fabricmc.loom.task.RemapJarTask>("remapJar") {
|
||||
archiveClassifier.set("")
|
||||
from(tasks.shadowJar)
|
||||
@ -145,10 +132,25 @@ tasks.shadowJar {
|
||||
println("Copying dependencies into mod: ${it.files}")
|
||||
}
|
||||
}
|
||||
|
||||
// If you want to include other dependencies and shadow them, you can relocate them in here
|
||||
fun relocate(name: String) = relocate(name, "$baseGroup.deps.$name")
|
||||
}
|
||||
|
||||
tasks.assemble.get().dependsOn(tasks.remapJar)
|
||||
tasks.assemble {
|
||||
dependsOn(tasks.remapJar)
|
||||
dependsOn("incrementBuildNumber")
|
||||
}
|
||||
|
||||
tasks.register("incrementBuildNumber") {
|
||||
doLast {
|
||||
val propsFile = file("gradle.properties")
|
||||
val props = Properties()
|
||||
props.load(propsFile.inputStream())
|
||||
|
||||
val current = props.getProperty("buildNumber")?.toIntOrNull() ?: 1
|
||||
val next = current + 1
|
||||
props.setProperty("buildNumber", next.toString())
|
||||
props.store(propsFile.outputStream(), null)
|
||||
|
||||
println("🔢 Build number incremented to $next")
|
||||
}
|
||||
}
|
||||
|
131
docs/CLIENT_COMMAND_BUILDER.md
Normal file
131
docs/CLIENT_COMMAND_BUILDER.md
Normal file
@ -0,0 +1,131 @@
|
||||
# Client Command Framework
|
||||
I'm using a lightweight Kotlin DSL, annotation-based command system for making client side commands.
|
||||
|
||||
## Defining Commands
|
||||
|
||||
All commands are defined as Kotlin functions annotated with `@SubCommand` in `ClientCommands.kt`.
|
||||
|
||||
### Example:
|
||||
|
||||
```kotlin
|
||||
@SubCommand(
|
||||
name = "test",
|
||||
description = "Runs a test command"
|
||||
)
|
||||
fun test(ctx: CommandContext) {
|
||||
ctx.send("Test command executed!")
|
||||
}
|
||||
```
|
||||
|
||||
### Subcommand Options:
|
||||
- `name`: command name (required)
|
||||
- `usage`: shows usage in help menu
|
||||
- `description`: description shown in help
|
||||
- `aliases`: optional aliases
|
||||
|
||||
### CommandContext helpers:
|
||||
- `ctx.arg(index: Int)`: get argument (nullable)
|
||||
- `ctx.require(index: Int, errorMsg: String)`: get required argument (throws error if missing)
|
||||
- `ctx.send(msg: String)`: send message to client player
|
||||
|
||||
## Help System
|
||||
- `/sbt` shows all available subcommands
|
||||
- `/sbt help <command>` shows usage & description for a subcommand
|
||||
|
||||
## Tab Completion
|
||||
- Tab completion works for first argument (subcommands)
|
||||
|
||||
|
||||
### Example:
|
||||
|
||||
```kotlin
|
||||
@SubCommand(
|
||||
name = "test",
|
||||
description = "Runs a test command"
|
||||
)
|
||||
fun test(ctx: CommandContext) {
|
||||
ctx.send("Test command executed!")
|
||||
}
|
||||
```
|
||||
|
||||
### Subcommand Options:
|
||||
- `name`: command name (required)
|
||||
- `usage`: shows usage in help menu
|
||||
- `description`: description shown in help
|
||||
- `aliases`: optional aliases
|
||||
|
||||
### CommandContext helpers:
|
||||
- `ctx.arg(index: Int)`: get argument (nullable)
|
||||
- `ctx.require(index: Int, errorMsg: String)`: get required argument (throws error if missing)
|
||||
- `ctx.send(msg: String)`: send message to client player
|
||||
|
||||
## Help System
|
||||
- `/sbt` shows all available subcommands
|
||||
- `/sbt help <command>` shows usage & description for a subcommand
|
||||
|
||||
## Tab Completion
|
||||
- Tab completion works for first argument (subcommands)
|
||||
|
||||
## Example Commands
|
||||
|
||||
```kotlin
|
||||
@SubCommand(
|
||||
name = "autogrotto",
|
||||
usage = "<status|enable|disable>",
|
||||
description = "Manages the AutoGrotto feature"
|
||||
)
|
||||
fun autogrotto(ctx: CommandContext) {
|
||||
when (ctx.require(0, "Provide status, enable, or disable")) {
|
||||
"status" -> ctx.send("Autogrotto is enabled")
|
||||
"enable" -> ctx.send("Autogrotto enabled")
|
||||
"disable" -> ctx.send("Autogrotto disabled")
|
||||
else -> ctx.send("Invalid usage. Try /sbt help autogrotto")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How to Add a New Command
|
||||
1. Go to `ClientCommands.kt`
|
||||
2. Define a function
|
||||
3. Annotate with `@SubCommand`
|
||||
4. Use `ctx` to get args and send messages
|
||||
|
||||
That's it!
|
||||
|
||||
## Using CommandContext Helpers
|
||||
|
||||
```kotlin
|
||||
val playerName = ctx.player?.name // Nullable: client-side player
|
||||
val firstArg = ctx.arg(0) // Safe nullable accessor
|
||||
val secondArgRequired = ctx.require(1, "Missing second argument") // Throws if missing
|
||||
ctx.send("You said: $firstArg and $secondArgRequired") // Send chat to player
|
||||
```
|
||||
|
||||
## Calling Functions From Another Class
|
||||
|
||||
Create a utility or manager class like:
|
||||
|
||||
```kotlin
|
||||
object MyFeatureManager {
|
||||
fun toggleFeature(enabled: Boolean) {
|
||||
// logic here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then call it from a command:
|
||||
|
||||
```kotlin
|
||||
@SubCommand(name = "feature", usage = "<on|off>", description = "Toggles a feature")
|
||||
fun feature(ctx: CommandContext) {
|
||||
val toggle = ctx.require(0, "Provide a toggle option")
|
||||
when (toggle.lowercase()) {
|
||||
"on", "enable" -> // same action
|
||||
"off", "disable" -> // another action
|
||||
else -> {
|
||||
ctx.send("Invalid argument '$toggle'. Usage:")
|
||||
ctx.send("/sbt autogrotto <enable|disable|status>")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@ -1,6 +1,8 @@
|
||||
#Tue Apr 22 09:20:13 MDT 2025
|
||||
versionBase=2.0.0
|
||||
loom.platform=forge
|
||||
baseGroup=com.github.itzilly.sbt
|
||||
mcVersion=1.8.9
|
||||
org.gradle.jvmargs=-Xmx2g
|
||||
baseGroup = com.github.itzilly.sbt
|
||||
mcVersion = 1.8.9
|
||||
modid = sbt
|
||||
version = 1.0.0
|
||||
buildNumber=214
|
||||
modid=sbt
|
||||
|
@ -7,7 +7,7 @@ pluginManagement {
|
||||
maven("https://maven.fabricmc.net")
|
||||
maven("https://maven.minecraftforge.net/")
|
||||
maven("https://repo.spongepowered.org/maven/")
|
||||
maven("https://repo.sk1er.club/repository/maven-releases/")
|
||||
maven("https://repo.essential.gg/repository/maven-releases/")
|
||||
}
|
||||
resolutionStrategy {
|
||||
eachPlugin {
|
||||
|
@ -1,24 +0,0 @@
|
||||
package com.github.itzilly.sbt
|
||||
|
||||
import net.minecraft.client.renderer.GlStateManager
|
||||
import java.util.*
|
||||
|
||||
class HudText(var textLines: Array<String>, var x: Int, var y: Int, var shadow: Boolean) : RenderHudAble {
|
||||
var scale = 1f
|
||||
val id = UUID.randomUUID()
|
||||
override fun render(partialTicks: Float) {
|
||||
GlStateManager.pushMatrix()
|
||||
GlStateManager.translate(x.toFloat(), y.toFloat(), 0f)
|
||||
GlStateManager.scale(scale, scale, scale)
|
||||
var yOff = 0
|
||||
for (line in textLines) {
|
||||
if (shadow) {
|
||||
SkyBlockTweaks.INSTANCE.fontRenderer.drawStringWithShadow(line, 0f, yOff.toFloat(), 0)
|
||||
} else {
|
||||
SkyBlockTweaks.INSTANCE.fontRenderer.drawString(line, 0, yOff, 0)
|
||||
}
|
||||
yOff += 10
|
||||
}
|
||||
GlStateManager.popMatrix()
|
||||
}
|
||||
}
|
26
src/main/kotlin/com/github/itzilly/sbt/Keybinds.kt
Normal file
26
src/main/kotlin/com/github/itzilly/sbt/Keybinds.kt
Normal file
@ -0,0 +1,26 @@
|
||||
package com.github.itzilly.sbt
|
||||
|
||||
import net.minecraft.client.settings.KeyBinding
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry
|
||||
import org.lwjgl.input.Keyboard
|
||||
|
||||
|
||||
object Keybinds {
|
||||
// Grotto Finder
|
||||
var searchGrotto: KeyBinding = KeyBinding("key.searchGrotto", Keyboard.KEY_Y, "key.categories.sbt")
|
||||
var clearGrotto: KeyBinding = KeyBinding("key.clearGrotto", Keyboard.KEY_X, "key.categories.sbt")
|
||||
|
||||
// Route Builder
|
||||
var addRouteNode: KeyBinding = KeyBinding("key.addRouteNode", Keyboard.KEY_ADD, "key.categories.sbt")
|
||||
var removeLastNode: KeyBinding = KeyBinding("key.removeLastNode", Keyboard.KEY_MINUS, "key.categories.sbt")
|
||||
var finishRoute: KeyBinding = KeyBinding("key.finishRoute", Keyboard.KEY_F4, "key.categories.sbt")
|
||||
|
||||
fun register() {
|
||||
ClientRegistry.registerKeyBinding(searchGrotto)
|
||||
ClientRegistry.registerKeyBinding(clearGrotto)
|
||||
|
||||
ClientRegistry.registerKeyBinding(addRouteNode)
|
||||
ClientRegistry.registerKeyBinding(removeLastNode)
|
||||
ClientRegistry.registerKeyBinding(finishRoute)
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
package com.github.itzilly.sbt
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator
|
||||
import net.minecraft.client.renderer.WorldRenderer
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
|
||||
import net.minecraft.util.AxisAlignedBB
|
||||
import java.awt.Color
|
||||
|
||||
|
||||
class RenderUtils {
|
||||
var TESSELLATOR: Tessellator = Tessellator.getInstance()
|
||||
var WORLD_RENDERER: WorldRenderer = Tessellator.getInstance().worldRenderer
|
||||
|
||||
fun drawBlock(box: AxisAlignedBB, outlineStartColor: Int, outlineEndColor: Int) {
|
||||
drawBlockTop(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
drawBlockBottom(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
drawBlockNorth(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
drawBlockEast(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
drawBlockSouth(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
drawBlockWest(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
}
|
||||
|
||||
private fun drawBlockTop(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
private fun drawBlockBottom(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
private fun drawBlockNorth(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
private fun drawBlockEast(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
private fun drawBlockSouth(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
private fun drawBlockWest(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
}
|
@ -1,37 +1,19 @@
|
||||
package com.github.itzilly.sbt
|
||||
|
||||
import com.google.gson.Gson
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.*
|
||||
import net.minecraft.client.renderer.GlStateManager
|
||||
import net.minecraft.client.renderer.Tessellator
|
||||
import net.minecraft.client.renderer.WorldRenderer
|
||||
import net.minecraft.client.renderer.entity.RenderManager
|
||||
import net.minecraft.client.settings.KeyBinding
|
||||
import net.minecraft.command.CommandException
|
||||
import net.minecraft.command.ICommandSender
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.util.AxisAlignedBB
|
||||
import net.minecraft.util.BlockPos
|
||||
import net.minecraft.util.ChatComponentText
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent
|
||||
|
||||
import com.github.itzilly.sbt.command.ClientCommands
|
||||
import com.github.itzilly.sbt.command.ModCommandRegistry
|
||||
import com.github.itzilly.sbt.command.registerBaseCommand
|
||||
import com.github.itzilly.sbt.features.GrottoFinder
|
||||
import com.github.itzilly.sbt.features.RouteBuilder
|
||||
import net.minecraftforge.common.MinecraftForge
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry
|
||||
import net.minecraftforge.common.config.Configuration
|
||||
import net.minecraftforge.fml.common.Mod
|
||||
import net.minecraftforge.fml.common.event.FMLInitializationEvent
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
||||
import net.minecraftforge.fml.common.gameevent.InputEvent
|
||||
import org.lwjgl.input.Keyboard
|
||||
import org.lwjgl.opengl.GL11
|
||||
import java.awt.Color
|
||||
import java.awt.Toolkit
|
||||
import java.awt.datatransfer.Clipboard
|
||||
import java.awt.datatransfer.StringSelection
|
||||
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent
|
||||
import java.io.File
|
||||
|
||||
|
||||
@Mod(modid = "sbt", useMetadata = true)
|
||||
@Mod(modid = "sbt", useMetadata = true, clientSideOnly = true)
|
||||
class SkyBlockTweaks {
|
||||
init {
|
||||
INSTANCE = this
|
||||
@ -39,276 +21,42 @@ class SkyBlockTweaks {
|
||||
|
||||
companion object {
|
||||
lateinit var INSTANCE: SkyBlockTweaks
|
||||
var routeList: ArrayList<RouteMarker> = ArrayList()
|
||||
lateinit var config: Configuration
|
||||
}
|
||||
|
||||
lateinit var tessellator: Tessellator
|
||||
lateinit var worldRenderer: WorldRenderer
|
||||
lateinit var renderManager: RenderManager
|
||||
lateinit var fontRenderer: FontRenderer
|
||||
|
||||
private var renderWorldList: ArrayList<RenderHudAble> = ArrayList()
|
||||
|
||||
@Mod.EventHandler
|
||||
fun preInit(event: FMLPreInitializationEvent) {
|
||||
val directory = event.modConfigurationDirectory
|
||||
config = Configuration(File(directory.path, "sbt.cfg"))
|
||||
loadConfig()
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
fun init(event: FMLInitializationEvent) {
|
||||
try {
|
||||
val resource: net.minecraft.client.resources.IResource = Minecraft.getMinecraft().resourceManager
|
||||
.getResource(net.minecraft.util.ResourceLocation("test:test.txt"))
|
||||
org.apache.commons.io.IOUtils.copy(resource.inputStream, java.lang.System.out)
|
||||
} catch (e: java.io.IOException) {
|
||||
throw java.lang.RuntimeException(e)
|
||||
}
|
||||
Keybinds.register()
|
||||
registerEventListeners()
|
||||
|
||||
STBKeyBinds.init()
|
||||
|
||||
tessellator = Tessellator.getInstance()
|
||||
worldRenderer = tessellator.worldRenderer
|
||||
renderManager = Minecraft.getMinecraft().renderManager
|
||||
fontRenderer = Minecraft.getMinecraft().fontRendererObj
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(this)
|
||||
MinecraftForge.EVENT_BUS.register(KeyInputHandler())
|
||||
MinecraftForge.EVENT_BUS.register(RouteMarkerRender())
|
||||
ModCommandRegistry.register(ClientCommands)
|
||||
registerBaseCommand()
|
||||
}
|
||||
|
||||
// @SubscribeEvent
|
||||
// fun worldLoadEvent(event: WorldEvent.Load) {
|
||||
// val msg = "Hello illy!"
|
||||
// renderWorldList.add(
|
||||
// HudText(
|
||||
// arrayOf(msg),
|
||||
// (Display.getWidth() / 2),
|
||||
// Display.getHeight() / 2 / 2,
|
||||
// false
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
private fun loadConfig() {
|
||||
config.load()
|
||||
|
||||
@SubscribeEvent
|
||||
fun renderOverlayEvent(event: RenderGameOverlayEvent.Text) {
|
||||
if (renderWorldList.size == 0) {
|
||||
return
|
||||
}
|
||||
for (render: RenderHudAble in renderWorldList) {
|
||||
render.render(event.partialTicks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface RenderHudAble {
|
||||
fun render(partialTicks: Float)
|
||||
}
|
||||
|
||||
|
||||
interface RenderWorldAble {
|
||||
fun render(partialTicks: Float)
|
||||
}
|
||||
|
||||
|
||||
object STBKeyBinds {
|
||||
lateinit var addWaypoint: KeyBinding
|
||||
lateinit var finishWaypoints: KeyBinding
|
||||
lateinit var clearWaypoints: KeyBinding
|
||||
lateinit var openRoutesGui: KeyBinding
|
||||
|
||||
fun init() {
|
||||
addWaypoint = KeyBinding("key.addWaypoint", Keyboard.KEY_ADD, "key.categories.sbt")
|
||||
ClientRegistry.registerKeyBinding(addWaypoint)
|
||||
|
||||
finishWaypoints = KeyBinding("key.finishWaypoints", Keyboard.KEY_P, "key.categories.sbt")
|
||||
ClientRegistry.registerKeyBinding(finishWaypoints)
|
||||
|
||||
clearWaypoints = KeyBinding("key.clearWaypoints", Keyboard.KEY_MINUS, "key.categories.sbt")
|
||||
ClientRegistry.registerKeyBinding(clearWaypoints)
|
||||
|
||||
openRoutesGui = KeyBinding("key.openRoutesGui", Keyboard.KEY_O, "key.categories.sbt")
|
||||
ClientRegistry.registerKeyBinding(openRoutesGui)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class KeyInputHandler {
|
||||
private val mc = Minecraft.getMinecraft()
|
||||
|
||||
@SubscribeEvent
|
||||
fun onKeyInput(event: InputEvent.KeyInputEvent?) {
|
||||
if (STBKeyBinds.addWaypoint.isPressed) {
|
||||
val x = mc.thePlayer.posX.toInt()
|
||||
val y = mc.thePlayer.posY.toInt()
|
||||
val z = mc.thePlayer.posZ.toInt() - 1 // Offset from player (no clue why I need this)
|
||||
|
||||
if (!isPointInRouteList(SkyBlockTweaks.routeList, x, y, z)) {
|
||||
val name = SkyBlockTweaks.routeList.size + 1
|
||||
val point = RouteMarker(x, y, z, 0u, 1u, 0u, RouteOptions(name.toString()))
|
||||
SkyBlockTweaks.routeList.add(point)
|
||||
mc.thePlayer.addChatMessage(ChatComponentText("Adding Point ($x, $y, $z) to route list"))
|
||||
} else {
|
||||
mc.thePlayer.addChatMessage(ChatComponentText("Point ($x, $y, $z) is already in the route list"))
|
||||
}
|
||||
|
||||
}
|
||||
else if (STBKeyBinds.finishWaypoints.isPressed) {
|
||||
mc.thePlayer.addChatMessage(ChatComponentText("Finished route! Copied route to clipboard"))
|
||||
val jsonString = Gson().toJson(SkyBlockTweaks.routeList)
|
||||
setClipboard(jsonString)
|
||||
SkyBlockTweaks.routeList.clear()
|
||||
}
|
||||
else if (STBKeyBinds.clearWaypoints.isPressed) {
|
||||
SkyBlockTweaks.routeList.clear()
|
||||
mc.thePlayer.addChatMessage(ChatComponentText("Reset current route"))
|
||||
}
|
||||
else if (STBKeyBinds.openRoutesGui.isPressed) {
|
||||
mc.displayGuiScreen(RouteGui())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class RouteMarker(
|
||||
val x: Int,
|
||||
val y: Int,
|
||||
val z: Int,
|
||||
|
||||
val r: UByte,
|
||||
val g: UByte,
|
||||
val b: UByte,
|
||||
|
||||
val options: RouteOptions
|
||||
)
|
||||
|
||||
fun isPointInRouteList(routeList: List<RouteMarker>, x: Int, y: Int, z: Int): Boolean {
|
||||
return routeList.any { it.x == x && it.y == y && it.z == z }
|
||||
}
|
||||
|
||||
data class RouteOptions(
|
||||
val name: String
|
||||
)
|
||||
|
||||
fun setClipboard(s: String) {
|
||||
val selection = StringSelection(s)
|
||||
val clipboard: Clipboard = Toolkit.getDefaultToolkit().systemClipboard
|
||||
clipboard.setContents(selection, selection)
|
||||
}
|
||||
|
||||
class RouteMarkerRender {
|
||||
@SubscribeEvent
|
||||
fun renderBlockOverlay(event: RenderWorldLastEvent) {
|
||||
val player = Minecraft.getMinecraft().thePlayer
|
||||
|
||||
for (marker: RouteMarker in SkyBlockTweaks.routeList) {
|
||||
val pos = BlockPos(marker.x, marker.y, marker.z)
|
||||
renderPoint(player, pos, event.partialTicks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun renderPoint(entity: Entity, blockPos: BlockPos, partialTicks: Float) {
|
||||
val padding: Double = 0.0020000000949949026
|
||||
|
||||
val entityX: Double = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * partialTicks
|
||||
val entityY: Double = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * partialTicks
|
||||
val entityZ: Double = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * partialTicks
|
||||
|
||||
val outlineStartColor = 0xFFFFFF
|
||||
val outlineEndColor = 0xFFFFFF
|
||||
|
||||
val world = Minecraft.getMinecraft().theWorld
|
||||
val blockState = world.getBlockState(blockPos)
|
||||
val block: Block = blockState.block
|
||||
|
||||
// Create a default bounding box for blocks that don't have one
|
||||
val boundingBox: AxisAlignedBB = block.getCollisionBoundingBox(world, blockPos, blockState)
|
||||
?: AxisAlignedBB(blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble(),
|
||||
blockPos.x + 1.0, blockPos.y + 1.0, blockPos.z + 1.0).expand(padding, padding, padding)
|
||||
|
||||
GL11.glPushMatrix()
|
||||
GlStateManager.disableTexture2D()
|
||||
GlStateManager.depthMask(false)
|
||||
GlStateManager.disableDepth()
|
||||
GL11.glEnable(GL11.GL_LINE_SMOOTH)
|
||||
GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST)
|
||||
GL11.glLineWidth(2.0f)
|
||||
GL11.glShadeModel(GL11.GL_SMOOTH)
|
||||
|
||||
RenderUtils().drawBlock(boundingBox.offset(-entityX, -entityY, -entityZ), outlineStartColor, outlineEndColor)
|
||||
|
||||
GL11.glLineWidth(2.0f)
|
||||
GL11.glDisable(GL11.GL_LINE_SMOOTH)
|
||||
GlStateManager.enableDepth()
|
||||
GlStateManager.depthMask(true)
|
||||
GlStateManager.enableAlpha()
|
||||
GlStateManager.enableTexture2D()
|
||||
GL11.glPopMatrix()
|
||||
}
|
||||
|
||||
|
||||
class RouteMarkerButton(buttonId: Int, x: Int, y: Int, widthIn: Int, heightIn: Int, buttonText: String?) :
|
||||
GuiButton(buttonId, x, y, widthIn, heightIn, buttonText) {
|
||||
constructor(buttonId: Int, x: Int, y: Int, buttonText: String?) : this(buttonId, x, y, 200, 20, buttonText)
|
||||
|
||||
override fun drawButton(mc: Minecraft, mouseX: Int, mouseY: Int) {
|
||||
if (visible) {
|
||||
val fontrenderer = mc.fontRendererObj
|
||||
//mc.getTextureManager().bindTexture(buttonTextures);
|
||||
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f)
|
||||
hovered =
|
||||
mouseX >= xPosition && mouseY >= yPosition && mouseX < xPosition + width && mouseY < yPosition + height
|
||||
//int i = this.getHoverState(this.hovered);
|
||||
drawRect(
|
||||
xPosition, yPosition, xPosition + width, yPosition + height,
|
||||
if (hovered) Color(255, 255, 255, 80).rgb else Color(0, 0, 0, 80).rgb
|
||||
)
|
||||
//this.drawTexturedModalRect(this.xPosition, this.yPosition, 0, 46 + i * 20, this.width / 2, this.height);
|
||||
//this.drawTexturedModalRect(this.xPosition + this.width / 2, this.yPosition, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height);
|
||||
mouseDragged(mc, mouseX, mouseY)
|
||||
var j = 14737632
|
||||
if (packedFGColour != 0) {
|
||||
j = packedFGColour
|
||||
} else if (!enabled) {
|
||||
j = 10526880
|
||||
} else if (hovered) {
|
||||
j = 16777120
|
||||
}
|
||||
drawCenteredString(
|
||||
fontrenderer,
|
||||
displayString, xPosition + width / 2, yPosition + (height - 8) / 2, j
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RouteGui : GuiScreen() {
|
||||
private val id: Int = 0
|
||||
|
||||
override fun initGui() {
|
||||
super.initGui()
|
||||
buttonList.clear()
|
||||
|
||||
populateRouteList()
|
||||
buttonList.add(RouteMarkerButton(9000, width/2 + 20, height - 40, "Exit"))
|
||||
}
|
||||
|
||||
override fun actionPerformed(button: GuiButton?) {
|
||||
if (button == null) { return }
|
||||
if (button.id < 1000) { buttonList.remove(button) }
|
||||
else if (button.id == 9000) { Minecraft.getMinecraft().thePlayer.closeScreen() }
|
||||
}
|
||||
|
||||
override fun drawScreen(mouseX: Int, mouseY: Int, partialTicks: Float) {
|
||||
this.drawDefaultBackground()
|
||||
super.drawScreen(mouseX, mouseY, partialTicks)
|
||||
}
|
||||
|
||||
private fun populateRouteList() {
|
||||
for ((buttonId, m: RouteMarker) in SkyBlockTweaks.routeList.withIndex()) {
|
||||
val markerField = GuiTextField(1000 + buttonId, fontRendererObj, width/2 - 200, 0, 100, 20)
|
||||
markerField.text = "${m.options.name} X:${m.x} Y:${m.y} Z:${m.z}"
|
||||
val removeButton = RouteMarkerButton(2000 + buttonId, width/2 + 175, 0, 50, 20, "Remove")
|
||||
buttonList.add(removeButton)
|
||||
if (config.hasChanged()) {
|
||||
config.save()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
fun syncConfig() {
|
||||
loadConfig()
|
||||
if (config.hasChanged()) {
|
||||
config.save()
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerEventListeners() {
|
||||
MinecraftForge.EVENT_BUS.register(RouteBuilder)
|
||||
MinecraftForge.EVENT_BUS.register(GrottoFinder)
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.github.itzilly.sbt.command
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class SubCommand(
|
||||
val name: String,
|
||||
val usage: String = "",
|
||||
val description: String = "",
|
||||
val aliases: Array<String> = []
|
||||
)
|
@ -0,0 +1,31 @@
|
||||
package com.github.itzilly.sbt.command
|
||||
|
||||
import net.minecraft.command.CommandBase
|
||||
import net.minecraft.command.ICommandSender
|
||||
import net.minecraft.util.BlockPos
|
||||
import net.minecraftforge.client.ClientCommandHandler
|
||||
|
||||
fun registerBaseCommand() {
|
||||
ClientCommandHandler.instance.registerCommand(object : CommandBase() {
|
||||
override fun getCommandName() = "sbt"
|
||||
override fun getCommandUsage(sender: ICommandSender) = "/sbt <subcommand>"
|
||||
|
||||
override fun processCommand(sender: ICommandSender, args: Array<out String>) {
|
||||
ModCommandRegistry.dispatch(CommandContext(sender, args))
|
||||
}
|
||||
|
||||
override fun addTabCompletionOptions(
|
||||
sender: ICommandSender,
|
||||
args: Array<out String>,
|
||||
pos: BlockPos?
|
||||
): List<String>? {
|
||||
return if (args.size == 1) {
|
||||
ModCommandRegistry.subcommands.keys.toList().distinct().filter { it.startsWith(args[0]) }
|
||||
} else null
|
||||
}
|
||||
|
||||
override fun canCommandSenderUseCommand(sender: ICommandSender): Boolean {
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.github.itzilly.sbt.command
|
||||
|
||||
import com.github.itzilly.sbt.features.GrottoFinder
|
||||
import com.github.itzilly.sbt.util.Chatterbox
|
||||
import com.github.itzilly.sbt.util.SimpleChatMsg
|
||||
|
||||
object ClientCommands {
|
||||
|
||||
@SubCommand(
|
||||
name = "autogrotto",
|
||||
usage = "<status|enable|disable>",
|
||||
description = "Manages the AutoGrotto feature"
|
||||
)
|
||||
fun autogrotto(ctx: CommandContext) {
|
||||
val action = ctx.arg(0)?.lowercase()
|
||||
|
||||
when (action) {
|
||||
"status" -> {
|
||||
val isEnabled = GrottoFinder.isAutoDetectEnabled()
|
||||
Chatterbox.say("Autogrotto is currently ${if (isEnabled) "enabled" else "disabled"}")
|
||||
}
|
||||
|
||||
"enable", "on" -> {
|
||||
GrottoFinder.enableAutoDetect()
|
||||
Chatterbox.say("Autogrotto has been enabled")
|
||||
}
|
||||
|
||||
"disable", "off" -> {
|
||||
GrottoFinder.disableAutoDetect()
|
||||
Chatterbox.say("Autogrotto has been disabled")
|
||||
}
|
||||
|
||||
else -> {
|
||||
Chatterbox.say(SimpleChatMsg("Invalid argument! Usage:").red())
|
||||
Chatterbox.say("/sbt autogrotto <status | enable/on | disable/off>")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubCommand(
|
||||
name = "reveal_grotto",
|
||||
usage = "",
|
||||
description = "Reveal Grotto"
|
||||
)
|
||||
fun reveal_grotto(ctx: CommandContext) {
|
||||
GrottoFinder.revealDetectedJasperBlocks()
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.github.itzilly.sbt.command
|
||||
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.command.ICommandSender
|
||||
import net.minecraft.util.ChatComponentText
|
||||
|
||||
/**
|
||||
* Wrapper for command execution context.
|
||||
*
|
||||
* Provides access to the command sender, arguments, and helper methods.
|
||||
*
|
||||
* @property sender The ICommandSender executing the command.
|
||||
* @property args The raw array of command arguments.
|
||||
*/
|
||||
class CommandContext(
|
||||
val sender: ICommandSender,
|
||||
var args: Array<out String>
|
||||
) {
|
||||
|
||||
/**
|
||||
* The client-side player instance.
|
||||
* Can be null in rare cases (e.g., not in-game).
|
||||
*/
|
||||
val player get() = Minecraft.getMinecraft().thePlayer
|
||||
|
||||
/**
|
||||
* Sends a chat message to the player.
|
||||
*
|
||||
* @param msg The message to send.
|
||||
*/
|
||||
fun send(msg: String) {
|
||||
player?.addChatMessage(ChatComponentText(msg))
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an argument at the given index, or null if not present.
|
||||
*
|
||||
* @param index The argument index.
|
||||
* @return The argument or null.
|
||||
*/
|
||||
fun arg(index: Int): String? = args.getOrNull(index)
|
||||
|
||||
/**
|
||||
* Retrieves an argument and throws if it's missing.
|
||||
*
|
||||
* @param index The argument index.
|
||||
* @param error Error message to show if argument is missing.
|
||||
* @return The argument string.
|
||||
* @throws IllegalArgumentException if missing.
|
||||
*/
|
||||
fun require(index: Int, error: String): String {
|
||||
return args.getOrNull(index) ?: throw IllegalArgumentException(error)
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package com.github.itzilly.sbt.command
|
||||
|
||||
object ModCommandRegistry {
|
||||
val subcommands = mutableMapOf<String, CommandEntry>()
|
||||
|
||||
fun register(target: Any) {
|
||||
for (method in target::class.java.declaredMethods) {
|
||||
val annotation = method.getAnnotation(SubCommand::class.java) ?: continue
|
||||
val entry = CommandEntry(annotation, method, target)
|
||||
subcommands[annotation.name.lowercase()] = entry
|
||||
annotation.aliases.forEach {
|
||||
subcommands[it.lowercase()] = entry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun dispatch(context: CommandContext) {
|
||||
val sub = context.arg(0)?.lowercase()
|
||||
val subArgs = context.args.drop(1).toTypedArray()
|
||||
|
||||
if (sub == null || sub == "help") {
|
||||
showHelp(context, subArgs.firstOrNull())
|
||||
return
|
||||
}
|
||||
|
||||
val cmd = subcommands[sub]
|
||||
if (cmd != null) {
|
||||
context.args = subArgs
|
||||
cmd.invoke(context)
|
||||
} else {
|
||||
context.send("Unknown subcommand '$sub'. Use /sbt help.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun showHelp(ctx: CommandContext, target: String?) {
|
||||
if (target != null) {
|
||||
val cmd = subcommands[target.lowercase()]
|
||||
if (cmd != null) {
|
||||
ctx.send("Usage: /sbt ${cmd.annotation.name} ${cmd.annotation.usage}")
|
||||
ctx.send(cmd.annotation.description)
|
||||
return
|
||||
}
|
||||
ctx.send("Unknown command '$target'")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.send("Available /sbt commands:")
|
||||
subcommands.values.toSet().forEach {
|
||||
ctx.send("- ${it.annotation.name} ${it.annotation.usage} : ${it.annotation.description}")
|
||||
}
|
||||
}
|
||||
|
||||
class CommandEntry(
|
||||
val annotation: SubCommand,
|
||||
val method: java.lang.reflect.Method,
|
||||
val target: Any
|
||||
) {
|
||||
fun invoke(ctx: CommandContext) {
|
||||
try {
|
||||
method.invoke(target, ctx)
|
||||
} catch (e: Exception) {
|
||||
ctx.send("§cError: ${e.cause?.message ?: e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
src/main/kotlin/com/github/itzilly/sbt/data/RouteNode.kt
Normal file
17
src/main/kotlin/com/github/itzilly/sbt/data/RouteNode.kt
Normal file
@ -0,0 +1,17 @@
|
||||
package com.github.itzilly.sbt.data
|
||||
|
||||
data class RouteNode(
|
||||
val x: Int,
|
||||
val y: Int,
|
||||
val z: Int,
|
||||
|
||||
val r: UByte,
|
||||
val g: UByte,
|
||||
val b: UByte,
|
||||
|
||||
val options: NodeOptions
|
||||
)
|
||||
|
||||
data class NodeOptions(
|
||||
val name: String
|
||||
)
|
4
src/main/kotlin/com/github/itzilly/sbt/data/Vec.kt
Normal file
4
src/main/kotlin/com/github/itzilly/sbt/data/Vec.kt
Normal file
@ -0,0 +1,4 @@
|
||||
package com.github.itzilly.sbt.data
|
||||
|
||||
data class Vecd3(val x:Double, val y:Double, val z:Double)
|
||||
data class Vecf3(val x:Float, val y:Float, val z:Float)
|
338
src/main/kotlin/com/github/itzilly/sbt/features/GrottoFinder.kt
Normal file
338
src/main/kotlin/com/github/itzilly/sbt/features/GrottoFinder.kt
Normal file
@ -0,0 +1,338 @@
|
||||
package com.github.itzilly.sbt.features
|
||||
|
||||
import com.github.itzilly.sbt.Keybinds
|
||||
import com.github.itzilly.sbt.render.BlockOutlineRenderer
|
||||
import com.github.itzilly.sbt.util.Chatterbox
|
||||
import com.github.itzilly.sbt.util.DelayedFunction
|
||||
import com.github.itzilly.sbt.util.SimpleChatMsg
|
||||
import com.github.itzilly.sbt.util.getMiniServerIdFromScoreboard
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.init.Blocks
|
||||
import net.minecraft.util.BlockPos
|
||||
import net.minecraft.util.ChatComponentText
|
||||
import net.minecraft.util.ChatStyle
|
||||
import net.minecraft.util.EnumChatFormatting
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.chunk.Chunk
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent
|
||||
import net.minecraftforge.event.world.ChunkEvent
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
||||
import net.minecraftforge.fml.common.gameevent.InputEvent
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent
|
||||
|
||||
object GrottoFinder {
|
||||
private var isRunningAutoDetect: Boolean = true
|
||||
private var autoDetectEnabled: Boolean = false
|
||||
private val scannedChunks = mutableMapOf<Pair<Int, Int>, Long>()
|
||||
private val pendingJasperBlocks = mutableListOf<BlockPos>()
|
||||
private var hasLocatedGrotto = false
|
||||
private const val SCAN_COOLDOWN_MS = 5 * 60 * 1100 // 5 minutes (+ a little to account for lag)
|
||||
|
||||
// World Bounds 824, 189, 824 201, 30, 201
|
||||
private val minBounds = BlockPos(201, 30, 201) // Max area of the Crystal Hollows
|
||||
private val maxBounds = BlockPos(824, 189, 824) // Max area of the Crystal Hollows
|
||||
private val targetBlock: Block = Blocks.stained_glass
|
||||
private val targetMeta: Int = 2 // Magenta stained glass
|
||||
|
||||
private var lastWorldLoadTime = 0L
|
||||
private var lastMiniServerId: String? = null
|
||||
|
||||
private var renderBlockList: ArrayList<BlockPos> = ArrayList()
|
||||
private var jasperCrystalsList: ArrayList<BlockPos> = ArrayList()
|
||||
|
||||
fun enableAutoDetect() {
|
||||
autoDetectEnabled = true
|
||||
}
|
||||
|
||||
fun disableAutoDetect() {
|
||||
autoDetectEnabled = false
|
||||
}
|
||||
|
||||
fun isAutoDetectEnabled(): Boolean {
|
||||
return autoDetectEnabled
|
||||
}
|
||||
|
||||
private fun clear() {
|
||||
renderBlockList.clear()
|
||||
}
|
||||
|
||||
private fun magentaNodeExists(x: Int, y: Int, z: Int): Boolean {
|
||||
return renderBlockList.any { it.x == x && it.y == y && it.z == z }
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun onKeyInput(event: InputEvent.KeyInputEvent) {
|
||||
if (Keybinds.searchGrotto.isPressed) {
|
||||
Chatterbox.say("Checking chunks for grotto...")
|
||||
checkWorldForMagentaStainedGlassAsync(Minecraft.getMinecraft().theWorld)
|
||||
renderBlockList.addAll(jasperCrystalsList)
|
||||
}
|
||||
|
||||
if (Keybinds.clearGrotto.isPressed) {
|
||||
renderBlockList.clear()
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Use async instead")
|
||||
private fun checkWorldForMagentaStainedGlassSync(world: World) {
|
||||
for (chunkX in minBounds.x / 16..maxBounds.x / 16) {
|
||||
for (chunkZ in minBounds.z / 16..maxBounds.z / 16) {
|
||||
val chunk = world.getChunkFromChunkCoords(chunkX, chunkZ)
|
||||
|
||||
// Iterate through the chunk's blocks within the world bounds
|
||||
for (x in 0..15) {
|
||||
for (y in minBounds.y..maxBounds.y) {
|
||||
for (z in 0..15) {
|
||||
val worldX = chunk.xPosition * 16 + x
|
||||
val worldZ = chunk.zPosition * 16 + z
|
||||
|
||||
// Ensure the block is within the specified bounds
|
||||
if (worldX in minBounds.x..maxBounds.x && worldZ in minBounds.z..maxBounds.z) {
|
||||
val blockPos = BlockPos(worldX, y, worldZ)
|
||||
val blockState = chunk.getBlockState(blockPos)
|
||||
val block = blockState.block
|
||||
val meta = block.getMetaFromState(blockState)
|
||||
|
||||
if (block === targetBlock && meta == targetMeta) {
|
||||
if (!magentaNodeExists(blockPos.x, blockPos.y, blockPos.z)) {
|
||||
renderBlockList.add(BlockPos(blockPos.x, blockPos.y, blockPos.z))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (renderBlockList.isNotEmpty()) {
|
||||
val pos = renderBlockList[0]
|
||||
val msg = SimpleChatMsg(
|
||||
"Found Jasper near (%d, %d, %d)".format(
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z
|
||||
)
|
||||
).light_purple()
|
||||
Chatterbox.say(msg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkWorldForMagentaStainedGlassAsync(world: World) {
|
||||
Thread {
|
||||
val foundBlocks = ArrayList<BlockPos>()
|
||||
for (chunkX in minBounds.x / 16..maxBounds.x / 16) {
|
||||
for (chunkZ in minBounds.z / 16..maxBounds.z / 16) {
|
||||
val chunk = world.getChunkFromChunkCoords(chunkX, chunkZ)
|
||||
|
||||
for (x in 0..15) {
|
||||
for (y in minBounds.y..maxBounds.y) {
|
||||
for (z in 0..15) {
|
||||
val worldX = chunk.xPosition * 16 + x
|
||||
val worldZ = chunk.zPosition * 16 + z
|
||||
|
||||
if (worldX in minBounds.x..maxBounds.x && worldZ in minBounds.z..maxBounds.z) {
|
||||
val blockPos = BlockPos(worldX, y, worldZ)
|
||||
val blockState = chunk.getBlockState(blockPos)
|
||||
val block = blockState.block
|
||||
val meta = block.getMetaFromState(blockState)
|
||||
|
||||
if (block === targetBlock && meta == targetMeta) {
|
||||
foundBlocks.add(blockPos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Minecraft.getMinecraft().addScheduledTask {
|
||||
clear()
|
||||
renderBlockList.addAll(foundBlocks)
|
||||
|
||||
if (renderBlockList.isNotEmpty()) {
|
||||
val pos = renderBlockList[0]
|
||||
val msg = SimpleChatMsg(
|
||||
"Found Jasper near (%d, %d, %d)".format(
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z
|
||||
)
|
||||
).light_purple()
|
||||
Chatterbox.say(msg)
|
||||
} else {
|
||||
Chatterbox.say("No Jasper found.")
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun renderBlockOverlay(event: RenderWorldLastEvent) {
|
||||
val player = Minecraft.getMinecraft().thePlayer
|
||||
|
||||
for (glass: BlockPos in renderBlockList) {
|
||||
val pos = BlockPos(glass.x, glass.y, glass.z)
|
||||
BlockOutlineRenderer.drawBlockOutline(player, pos, event.partialTicks)
|
||||
BlockOutlineRenderer.drawBlockLine(pos, event.partialTicks)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInCrystalHollowsFromTab(): Boolean {
|
||||
val infoList = Minecraft.getMinecraft().thePlayer?.sendQueue?.playerInfoMap ?: return false
|
||||
|
||||
val lines = infoList
|
||||
.mapNotNull { it.displayName?.unformattedText }
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotBlank() }
|
||||
|
||||
for (i in 0 until lines.size - 1) {
|
||||
if (lines[i].equals("Info", ignoreCase = true) &&
|
||||
lines[i + 1].contains("Area: Crystal Hollows", ignoreCase = true)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// @SubscribeEvent
|
||||
// fun onEntityJoinWorldEvent(event: EntityJoinWorldEvent) {
|
||||
// val mc = Minecraft.getMinecraft()
|
||||
// val player = mc.thePlayer
|
||||
// val TWO_SECONDS = 2000
|
||||
//
|
||||
// if (event.entity === player && event.world.isRemote) {
|
||||
// val now = System.currentTimeMillis()
|
||||
// if (now - lastWorldLoadTime < TWO_SECONDS) return
|
||||
// lastWorldLoadTime = now
|
||||
//
|
||||
// DelayedFunction({
|
||||
// isRunningAutoDetect = isInCrystalHollowsFromTab()
|
||||
// if (isRunningAutoDetect) {
|
||||
// if (pendingJasperBlocks.isNotEmpty()) {
|
||||
// jasperCrystalsList.addAll(pendingJasperBlocks)
|
||||
// pendingJasperBlocks.clear()
|
||||
// showRevealMessage()
|
||||
// }
|
||||
// }
|
||||
// }, delayTicks = 100)
|
||||
// }
|
||||
// }
|
||||
|
||||
// @SubscribeEvent
|
||||
// fun onClientTick(event: TickEvent.ClientTickEvent) {
|
||||
// if (!autoDetectEnabled || event.phase != TickEvent.Phase.END) return
|
||||
//
|
||||
// val currentMiniId = getMiniServerIdFromScoreboard() ?: return
|
||||
// if (currentMiniId != lastMiniServerId) {
|
||||
// if (lastMiniServerId != null) {
|
||||
// // TODO: fix
|
||||
//// isRunningAutoDetect = false
|
||||
// renderBlockList.clear()
|
||||
// jasperCrystalsList.clear()
|
||||
// pendingJasperBlocks.clear()
|
||||
// hasLocatedGrotto = false
|
||||
// }
|
||||
// lastMiniServerId = currentMiniId
|
||||
// }
|
||||
// }
|
||||
|
||||
// @SubscribeEvent
|
||||
// fun onChunkLoad(event: ChunkEvent.Load) {
|
||||
// if (!autoDetectEnabled) return
|
||||
//
|
||||
// val chunk = event.chunk
|
||||
// val key = Pair(chunk.xPosition, chunk.zPosition)
|
||||
// val now = System.currentTimeMillis()
|
||||
//
|
||||
// if (now - (scannedChunks[key] ?: 0) < SCAN_COOLDOWN_MS) return
|
||||
// scannedChunks[key] = now
|
||||
//
|
||||
// scanChunkForJasper(chunk)
|
||||
// }
|
||||
|
||||
private fun scanChunkForJasper(chunk: Chunk) {
|
||||
Thread {
|
||||
val found = mutableListOf<BlockPos>()
|
||||
|
||||
val baseX = chunk.xPosition * 16
|
||||
val baseZ = chunk.zPosition * 16
|
||||
|
||||
for (x in 0..15) {
|
||||
for (y in minBounds.y..maxBounds.y) {
|
||||
for (z in 0..15) {
|
||||
val pos = BlockPos(baseX + x, y, baseZ + z)
|
||||
val blockState = chunk.getBlockState(pos)
|
||||
val block = blockState.block
|
||||
val meta = block.getMetaFromState(blockState)
|
||||
|
||||
if (block == targetBlock && meta == targetMeta) {
|
||||
found.add(pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found.count() != 1) {
|
||||
if (isRunningAutoDetect) {
|
||||
jasperCrystalsList.addAll(found)
|
||||
if (!hasLocatedGrotto) {
|
||||
showRevealMessage()
|
||||
hasLocatedGrotto = true
|
||||
}
|
||||
} else {
|
||||
pendingJasperBlocks.addAll(found)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Chatterbox.say("Scanned Chunk, found: ${found.count()}")
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun showRevealMessage() {
|
||||
Chatterbox.say(SimpleChatMsg("- - - - - - - - - - - - - - - - - - -").light_purple())
|
||||
val clickMsg = ChatComponentText("§dDetected Fairy Grotto! §7[Click to reveal]").apply {
|
||||
chatStyle = ChatStyle().apply {
|
||||
color = EnumChatFormatting.LIGHT_PURPLE
|
||||
underlined = true
|
||||
chatClickEvent = net.minecraft.event.ClickEvent(
|
||||
net.minecraft.event.ClickEvent.Action.RUN_COMMAND,
|
||||
"/sbt reveal_grotto"
|
||||
)
|
||||
chatHoverEvent = net.minecraft.event.HoverEvent(
|
||||
net.minecraft.event.HoverEvent.Action.SHOW_TEXT,
|
||||
ChatComponentText("§7Click to show block outlines.")
|
||||
)
|
||||
}
|
||||
}
|
||||
Chatterbox.say(clickMsg)
|
||||
Chatterbox.say(SimpleChatMsg("- - - - - - - - - - - - - - - - - - -").light_purple())
|
||||
}
|
||||
|
||||
fun revealDetectedJasperBlocks() {
|
||||
jasperCrystalsList.addAll(pendingJasperBlocks)
|
||||
if (jasperCrystalsList.isEmpty()) {
|
||||
Chatterbox.say(SimpleChatMsg("[Grotto] No Jasper found!").light_purple())
|
||||
return
|
||||
}
|
||||
|
||||
renderBlockList.clear()
|
||||
renderBlockList.addAll(jasperCrystalsList)
|
||||
Chatterbox.say(SimpleChatMsg("[Grotto] Revealed detected Fairy Grotto blocks").light_purple())
|
||||
|
||||
val pos = renderBlockList[0]
|
||||
val msg = SimpleChatMsg(
|
||||
"Found Jasper near (%d, %d, %d)".format(
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z
|
||||
)
|
||||
).light_purple()
|
||||
Chatterbox.say(msg)
|
||||
}
|
||||
}
|
121
src/main/kotlin/com/github/itzilly/sbt/features/RouteBuilder.kt
Normal file
121
src/main/kotlin/com/github/itzilly/sbt/features/RouteBuilder.kt
Normal file
@ -0,0 +1,121 @@
|
||||
package com.github.itzilly.sbt.features
|
||||
|
||||
import com.github.itzilly.sbt.Keybinds
|
||||
import com.github.itzilly.sbt.data.NodeOptions
|
||||
import com.github.itzilly.sbt.data.RouteNode
|
||||
import com.github.itzilly.sbt.render.BlockOutlineRenderer
|
||||
import com.github.itzilly.sbt.util.Chatterbox
|
||||
import com.github.itzilly.sbt.util.SimpleChatMsg
|
||||
import com.google.gson.Gson
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.util.BlockPos
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
||||
import net.minecraftforge.fml.common.gameevent.InputEvent
|
||||
import java.awt.Toolkit
|
||||
import java.awt.datatransfer.Clipboard
|
||||
import java.awt.datatransfer.StringSelection
|
||||
import kotlin.math.round
|
||||
|
||||
object RouteBuilder {
|
||||
private const val OFFSET: Float = 0.5f;
|
||||
private const val MAX_HISTORY = 5
|
||||
|
||||
private var nodes: ArrayList<RouteNode> = ArrayList()
|
||||
private val history = ArrayDeque<List<RouteNode>>()
|
||||
|
||||
private fun nodeExists(x: Int, y: Int, z: Int): Boolean {
|
||||
return nodes.any { it.x == x && it.y == y && it.z == z }
|
||||
}
|
||||
|
||||
private fun addNode(x: Int, y: Int, z: Int) {
|
||||
nodes.add(RouteNode(x, y, z, 0u, 1u, 0u, NodeOptions("${nodes.size + 1}")))
|
||||
}
|
||||
|
||||
private fun addNode(x: Int, y: Int, z: Int, r: UByte, g: UByte, b: UByte) {
|
||||
nodes.add(RouteNode(x, y, z, r, g, b, NodeOptions("${nodes.size + 1}")))
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun onKeyInput(event: InputEvent.KeyInputEvent) {
|
||||
if (Keybinds.addRouteNode.isPressed) {
|
||||
addRouteNode()
|
||||
}
|
||||
|
||||
if (Keybinds.removeLastNode.isPressed) {
|
||||
removeLastNode()
|
||||
}
|
||||
|
||||
if (Keybinds.finishRoute.isPressed) {
|
||||
finishRoute()
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun renderBlockOverlay(event: RenderWorldLastEvent) {
|
||||
val player = Minecraft.getMinecraft().thePlayer
|
||||
|
||||
for (marker: RouteNode in nodes) {
|
||||
val pos = BlockPos(marker.x, marker.y, marker.z)
|
||||
BlockOutlineRenderer.drawBlockOutline(player, pos, event.partialTicks)
|
||||
BlockOutlineRenderer.drawBlockLine(pos, event.partialTicks)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addRouteNode() {
|
||||
val mc = Minecraft.getMinecraft()
|
||||
val x = round(mc.thePlayer.posX - OFFSET).toInt()
|
||||
val y = round(mc.thePlayer.posY).toInt() - 1 // for the block below the player
|
||||
val z = round(mc.thePlayer.posZ - OFFSET).toInt()
|
||||
|
||||
if (nodeExists(x, y, z)) {
|
||||
Chatterbox.say(SimpleChatMsg("Node already exists!").red())
|
||||
} else {
|
||||
addNode(x, y, z)
|
||||
Chatterbox.say("This is a string")
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeLastNode() {
|
||||
val wasRemoved = nodes.removeLastOrNull()
|
||||
if (wasRemoved == null) {
|
||||
val msg = SimpleChatMsg("There isn't a node to delete!")
|
||||
Chatterbox.say(msg.red())
|
||||
} else {
|
||||
val msg = SimpleChatMsg("Removed previous node")
|
||||
Chatterbox.say(msg.green())
|
||||
}
|
||||
}
|
||||
|
||||
private fun finishRoute() {
|
||||
if (nodes.size == 0) {
|
||||
val msg = SimpleChatMsg("There aren't any nodes to copy!")
|
||||
Chatterbox.say(msg.red())
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Make a config option to export to coleweight or the other mod (different format)
|
||||
// Maybe skytils too??
|
||||
saveRouteToHistory()
|
||||
// TODO: Make a way to view the history in case you goof up
|
||||
|
||||
val jsonString = Gson().toJson(nodes)
|
||||
val msg = SimpleChatMsg("String copied to clipboard!")
|
||||
setClipboard(jsonString)
|
||||
Chatterbox.say(msg.green())
|
||||
nodes.clear()
|
||||
}
|
||||
|
||||
private fun setClipboard(s: String) {
|
||||
val selection = StringSelection(s)
|
||||
val clipboard: Clipboard = Toolkit.getDefaultToolkit().systemClipboard
|
||||
clipboard.setContents(selection, selection)
|
||||
}
|
||||
|
||||
private fun saveRouteToHistory() {
|
||||
history.addLast(nodes.map { it.copy() })
|
||||
if (history.size > MAX_HISTORY) {
|
||||
history.removeFirst()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package com.github.itzilly.sbt.mixin;
|
||||
|
||||
import net.minecraft.client.gui.GuiMainMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(GuiMainMenu.class)
|
||||
public class MixinGuiMainMenu {
|
||||
|
||||
@Inject(method = "initGui", at = @At("HEAD"))
|
||||
public void onInitGui(CallbackInfo ci) {
|
||||
// System.out.println("Hello from Main Menu!");
|
||||
}
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
package com.github.itzilly.sbt.render
|
||||
|
||||
import com.github.itzilly.sbt.data.Vecd3
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.GlStateManager
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.util.AxisAlignedBB
|
||||
import net.minecraft.util.BlockPos
|
||||
import net.minecraft.util.EnumFacing
|
||||
import org.lwjgl.opengl.GL11
|
||||
import java.awt.Color
|
||||
|
||||
object BlockOutlineRenderer {
|
||||
|
||||
fun drawBlockOutline(entity: Entity, blockPos: BlockPos, partialTicks: Float, outlineColor: Int) {
|
||||
val padding = 0.0020000000949949026 // Magic number DO NOT TOUCH
|
||||
|
||||
val entityX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * partialTicks
|
||||
val entityY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * partialTicks
|
||||
val entityZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * partialTicks
|
||||
|
||||
val world = Minecraft.getMinecraft().theWorld
|
||||
val blockState = world.getBlockState(blockPos)
|
||||
val block = blockState.block
|
||||
|
||||
val boundingBox = block.getCollisionBoundingBox(world, blockPos, blockState)
|
||||
?: AxisAlignedBB(
|
||||
blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble(),
|
||||
blockPos.x + 1.0, blockPos.y + 1.0, blockPos.z + 1.0
|
||||
).expand(padding, padding, padding)
|
||||
|
||||
val r = (outlineColor shr 16 and 0xFF) / 255.0f
|
||||
val g = (outlineColor shr 8 and 0xFF) / 255.0f
|
||||
val b = (outlineColor and 0xFF) / 255.0f
|
||||
val a = (outlineColor shr 24 and 0xFF) / 255.0f
|
||||
|
||||
GL11.glPushMatrix()
|
||||
GlStateManager.disableTexture2D()
|
||||
GlStateManager.depthMask(false)
|
||||
GlStateManager.disableDepth()
|
||||
GL11.glEnable(GL11.GL_BLEND)
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA)
|
||||
GL11.glEnable(GL11.GL_LINE_SMOOTH)
|
||||
GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST)
|
||||
GL11.glLineWidth(2.0f)
|
||||
GL11.glColor4f(r, g, b, a)
|
||||
|
||||
RenderPrimitives.drawBlock(boundingBox.offset(-entityX, -entityY, -entityZ), outlineColor, outlineColor)
|
||||
|
||||
GL11.glDisable(GL11.GL_LINE_SMOOTH)
|
||||
GL11.glDisable(GL11.GL_BLEND)
|
||||
GlStateManager.enableDepth()
|
||||
GlStateManager.depthMask(true)
|
||||
GlStateManager.enableAlpha()
|
||||
GlStateManager.enableTexture2D()
|
||||
GL11.glPopMatrix()
|
||||
}
|
||||
|
||||
fun drawBlockOutline(entity: Entity, blockPos: BlockPos, partialTicks: Float) {
|
||||
val padding: Double = 0.0020000000949949026
|
||||
|
||||
val entityX: Double = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * partialTicks
|
||||
val entityY: Double = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * partialTicks
|
||||
val entityZ: Double = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * partialTicks
|
||||
|
||||
val outlineStartColor = 0xFFFFFF
|
||||
val outlineEndColor = 0xFFFFFF
|
||||
|
||||
val world = Minecraft.getMinecraft().theWorld
|
||||
val blockState = world.getBlockState(blockPos)
|
||||
val block: Block = blockState.block
|
||||
|
||||
val boundingBox: AxisAlignedBB = block.getCollisionBoundingBox(world, blockPos, blockState)
|
||||
?: AxisAlignedBB(
|
||||
blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble(),
|
||||
blockPos.x + 1.0, blockPos.y + 1.0, blockPos.z + 1.0
|
||||
).expand(padding, padding, padding)
|
||||
|
||||
GL11.glPushMatrix()
|
||||
GlStateManager.disableTexture2D()
|
||||
GlStateManager.depthMask(false)
|
||||
GlStateManager.disableDepth()
|
||||
GL11.glEnable(GL11.GL_LINE_SMOOTH)
|
||||
GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST)
|
||||
GL11.glLineWidth(2.0f)
|
||||
GL11.glShadeModel(GL11.GL_SMOOTH)
|
||||
|
||||
RenderPrimitives.drawBlock(boundingBox.offset(-entityX, -entityY, -entityZ), outlineStartColor, outlineEndColor)
|
||||
|
||||
GL11.glLineWidth(2.0f)
|
||||
GL11.glDisable(GL11.GL_LINE_SMOOTH)
|
||||
GlStateManager.enableDepth()
|
||||
GlStateManager.depthMask(true)
|
||||
GlStateManager.enableAlpha()
|
||||
GlStateManager.enableTexture2D()
|
||||
GL11.glPopMatrix()
|
||||
}
|
||||
|
||||
fun drawBlockLine(pos: BlockPos, partialTicks: Float) {
|
||||
val render = Minecraft.getMinecraft().renderViewEntity
|
||||
val rm = Minecraft.getMinecraft().renderManager
|
||||
// TODO: Fix this not working with view bobbing enabled
|
||||
val pos1 = Vecd3(rm.viewerPosX, rm.viewerPosY + render.eyeHeight, rm.viewerPosZ)
|
||||
|
||||
val pos2 = Vecd3(
|
||||
pos.x.toDouble() + 0.5, // Center of the block
|
||||
pos.y.toDouble() + 0.5, // Center of the block
|
||||
pos.z.toDouble() + 0.5 // Center of the block
|
||||
)
|
||||
|
||||
val lineColor = Color(0xe310d5)
|
||||
RenderPrimitives.drawLine(pos1, pos2, lineColor, 2, false, partialTicks)
|
||||
}
|
||||
|
||||
fun drawClusterOutline(entity: Entity, positions: List<BlockPos>, partialTicks: Float, outlineColor: Int) {
|
||||
val padding = 0.0020000000949949026 // Magic number DO NOT TOUCH
|
||||
|
||||
val entityX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * partialTicks
|
||||
val entityY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * partialTicks
|
||||
val entityZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * partialTicks
|
||||
|
||||
val world = Minecraft.getMinecraft().theWorld
|
||||
|
||||
GL11.glPushMatrix()
|
||||
GlStateManager.disableTexture2D()
|
||||
GlStateManager.depthMask(false)
|
||||
GlStateManager.disableDepth()
|
||||
GL11.glEnable(GL11.GL_BLEND)
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA)
|
||||
GL11.glEnable(GL11.GL_LINE_SMOOTH)
|
||||
GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST)
|
||||
GL11.glLineWidth(2.0f)
|
||||
|
||||
val r = (outlineColor shr 16 and 0xFF) / 255.0f
|
||||
val g = (outlineColor shr 8 and 0xFF) / 255.0f
|
||||
val b = (outlineColor and 0xFF) / 255.0f
|
||||
val a = (outlineColor shr 24 and 0xFF) / 255.0f
|
||||
|
||||
GL11.glColor4f(r, g, b, a)
|
||||
|
||||
for (pos in positions) {
|
||||
val blockState = world.getBlockState(pos)
|
||||
val boundingBox = blockState.block.getCollisionBoundingBox(world, pos, blockState)
|
||||
?: AxisAlignedBB(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), pos.x + 1.0, pos.y + 1.0, pos.z + 1.0).expand(padding, padding, padding)
|
||||
|
||||
val offsetBoundingBox = boundingBox.offset(-entityX, -entityY, -entityZ)
|
||||
|
||||
// Only draw exposed faces
|
||||
if (!positions.contains(pos.add(1, 0, 0))) {
|
||||
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.EAST, outlineColor)
|
||||
}
|
||||
if (!positions.contains(pos.add(-1, 0, 0))) {
|
||||
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.WEST, outlineColor)
|
||||
}
|
||||
if (!positions.contains(pos.add(0, 1, 0))) {
|
||||
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.UP, outlineColor)
|
||||
}
|
||||
if (!positions.contains(pos.add(0, -1, 0))) {
|
||||
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.DOWN, outlineColor)
|
||||
}
|
||||
if (!positions.contains(pos.add(0, 0, 1))) {
|
||||
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.SOUTH, outlineColor)
|
||||
}
|
||||
if (!positions.contains(pos.add(0, 0, -1))) {
|
||||
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.NORTH, outlineColor)
|
||||
}
|
||||
}
|
||||
|
||||
GL11.glDisable(GL11.GL_LINE_SMOOTH)
|
||||
GL11.glDisable(GL11.GL_BLEND)
|
||||
GlStateManager.enableDepth()
|
||||
GlStateManager.depthMask(true)
|
||||
GlStateManager.enableAlpha()
|
||||
GlStateManager.enableTexture2D()
|
||||
GL11.glPopMatrix()
|
||||
}
|
||||
}
|
@ -0,0 +1,318 @@
|
||||
package com.github.itzilly.sbt.render
|
||||
|
||||
import com.github.itzilly.sbt.data.Vecd3
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.renderer.GlStateManager
|
||||
import net.minecraft.client.renderer.Tessellator
|
||||
import net.minecraft.client.renderer.WorldRenderer
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats
|
||||
import net.minecraft.util.AxisAlignedBB
|
||||
import net.minecraft.util.EnumFacing
|
||||
import org.lwjgl.opengl.GL11
|
||||
import java.awt.Color
|
||||
|
||||
object RenderPrimitives {
|
||||
var TESSELLATOR: Tessellator = Tessellator.getInstance()
|
||||
var WORLD_RENDERER: WorldRenderer = Tessellator.getInstance().worldRenderer
|
||||
|
||||
fun drawBlock(box: AxisAlignedBB, outlineStartColor: Int, outlineEndColor: Int) {
|
||||
drawBlockTop(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
drawBlockBottom(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
drawBlockNorth(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
drawBlockEast(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
drawBlockSouth(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
drawBlockWest(box, Color(outlineStartColor), Color(outlineEndColor));
|
||||
}
|
||||
|
||||
private fun drawBlockTop(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
private fun drawBlockBottom(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
private fun drawBlockNorth(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
private fun drawBlockEast(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
private fun drawBlockSouth(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
private fun drawBlockWest(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color) {
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
fun drawLine(p1: Vecd3, p2: Vecd3, color: Color, lineWidth: Int, depth: Boolean, partialTicks: Float) {
|
||||
GlStateManager.disableCull()
|
||||
|
||||
val render = Minecraft.getMinecraft().renderViewEntity
|
||||
val worldRenderer = TESSELLATOR.worldRenderer
|
||||
|
||||
val realX = render.lastTickPosX + (render.posX - render.lastTickPosX) * partialTicks
|
||||
val realY = render.lastTickPosY + (render.posY - render.lastTickPosY) * partialTicks
|
||||
val realZ = render.lastTickPosZ + (render.posZ - render.lastTickPosZ) * partialTicks
|
||||
|
||||
|
||||
GlStateManager.pushMatrix()
|
||||
GlStateManager.translate(-realX, -realY, -realZ)
|
||||
GlStateManager.disableTexture2D()
|
||||
GlStateManager.enableBlend()
|
||||
GlStateManager.disableAlpha()
|
||||
GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0)
|
||||
GL11.glLineWidth(lineWidth.toFloat())
|
||||
if (!depth) {
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST)
|
||||
GlStateManager.depthMask(false)
|
||||
}
|
||||
GlStateManager.color(color.red / 255f, color.green / 255f, color.blue / 255f, color.alpha / 255f)
|
||||
worldRenderer.begin(GL11.GL_LINE_STRIP, DefaultVertexFormats.POSITION)
|
||||
worldRenderer.pos(p1.x, p1.y, p1.z).endVertex()
|
||||
worldRenderer.pos(p2.x, p2.y, p2.z).endVertex()
|
||||
Tessellator.getInstance().draw()
|
||||
GlStateManager.translate(realX, realY, realZ)
|
||||
if (!depth) {
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST)
|
||||
GlStateManager.depthMask(true)
|
||||
}
|
||||
GlStateManager.disableBlend()
|
||||
GlStateManager.enableAlpha()
|
||||
GlStateManager.enableTexture2D()
|
||||
GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f)
|
||||
GlStateManager.popMatrix()
|
||||
GlStateManager.disableLighting()
|
||||
GlStateManager.enableCull()
|
||||
}
|
||||
|
||||
fun drawAnimatedBlockTop(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color, time: Float) {
|
||||
val offsetY = Math.sin(time.toDouble()).toFloat() * (box.maxY - box.minY) / 2.0f
|
||||
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY + offsetY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY + offsetY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY + offsetY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY + offsetY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
fun drawAnimatedBlockBottom(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color, time: Float) {
|
||||
val offsetY = Math.sin(time.toDouble()).toFloat() * (box.maxY - box.minY) / 2.0f
|
||||
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY + offsetY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY + offsetY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY + offsetY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY + offsetY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
fun drawAnimatedBlockNorth(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color, time: Float) {
|
||||
val offsetY = Math.sin(time.toDouble()).toFloat() * (box.maxY - box.minY) / 2.0f
|
||||
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY + offsetY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY + offsetY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY + offsetY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY + offsetY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
fun drawAnimatedBlockEast(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color, time: Float) {
|
||||
val offsetY = Math.sin(time.toDouble()).toFloat() * (box.maxY - box.minY) / 2.0f
|
||||
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY + offsetY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY + offsetY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY + offsetY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY + offsetY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
fun drawAnimatedBlockSouth(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color, time: Float) {
|
||||
val offsetY = Math.sin(time.toDouble()).toFloat() * (box.maxY - box.minY) / 2.0f
|
||||
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY + offsetY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY + offsetY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY + offsetY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY + offsetY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
fun drawAnimatedBlockWest(box: AxisAlignedBB, outlineStartColor: Color, outlineEndColor: Color, time: Float) {
|
||||
val offsetY = Math.sin(time.toDouble()).toFloat() * (box.maxY - box.minY) / 2.0f
|
||||
|
||||
WORLD_RENDERER.begin(2, DefaultVertexFormats.POSITION_COLOR)
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY + offsetY, box.minZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY + offsetY, box.maxZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY + offsetY, box.maxZ)
|
||||
.color(outlineStartColor.red, outlineStartColor.green, outlineStartColor.blue, outlineStartColor.alpha)
|
||||
.endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY + offsetY, box.minZ)
|
||||
.color(outlineEndColor.red, outlineEndColor.green, outlineEndColor.blue, outlineEndColor.alpha).endVertex()
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
|
||||
fun drawBlockFace(box: AxisAlignedBB, face: EnumFacing, color: Int) {
|
||||
WORLD_RENDERER.begin(GL11.GL_LINE_STRIP, DefaultVertexFormats.POSITION_COLOR)
|
||||
|
||||
val r = (color shr 16 and 0xFF) / 255.0f
|
||||
val g = (color shr 8 and 0xFF) / 255.0f
|
||||
val b = (color and 0xFF) / 255.0f
|
||||
val a = (color shr 24 and 0xFF) / 255.0f
|
||||
|
||||
GL11.glColor4f(r, g, b, a)
|
||||
|
||||
when (face) {
|
||||
EnumFacing.UP -> {
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.minZ).endVertex()
|
||||
}
|
||||
EnumFacing.DOWN -> {
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ).endVertex()
|
||||
}
|
||||
EnumFacing.NORTH -> {
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ).endVertex()
|
||||
}
|
||||
EnumFacing.SOUTH -> {
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.maxZ).endVertex()
|
||||
}
|
||||
EnumFacing.WEST -> {
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.maxY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.minX, box.minY, box.minZ).endVertex()
|
||||
}
|
||||
EnumFacing.EAST -> {
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.maxZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.maxY, box.minZ).endVertex()
|
||||
WORLD_RENDERER.pos(box.maxX, box.minY, box.minZ).endVertex()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
TESSELLATOR.draw()
|
||||
}
|
||||
}
|
41
src/main/kotlin/com/github/itzilly/sbt/util/Chatterbox.kt
Normal file
41
src/main/kotlin/com/github/itzilly/sbt/util/Chatterbox.kt
Normal file
@ -0,0 +1,41 @@
|
||||
package com.github.itzilly.sbt.util
|
||||
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.entity.player.EntityPlayer
|
||||
import net.minecraft.util.ChatComponentText
|
||||
import net.minecraft.util.ChatStyle
|
||||
import net.minecraft.util.EnumChatFormatting
|
||||
import net.minecraft.util.IChatComponent
|
||||
|
||||
object Chatterbox {
|
||||
private const val prefixString = "[SBT] "
|
||||
private val prefixColorStyle = ChatStyle().setColor(EnumChatFormatting.AQUA)
|
||||
private val prefixComponent = ChatComponentText(prefixString).setChatStyle(prefixColorStyle)
|
||||
private val player = Minecraft.getMinecraft().thePlayer
|
||||
|
||||
private fun copy(component: IChatComponent): IChatComponent {
|
||||
val newComponent = ChatComponentText(component.unformattedTextForChat)
|
||||
newComponent.chatStyle = component.chatStyle.createShallowCopy()
|
||||
component.siblings.forEach { sibling ->
|
||||
newComponent.appendSibling(copy(sibling))
|
||||
}
|
||||
return newComponent
|
||||
}
|
||||
|
||||
fun say(msg: String) {
|
||||
val messageComponent = ChatComponentText(msg).setChatStyle(ChatStyle().setColor(EnumChatFormatting.RESET))
|
||||
player.addChatMessage(prefixComponent.createCopy().appendSibling(messageComponent))
|
||||
}
|
||||
|
||||
fun say(component: ChatComponentText) {
|
||||
player.addChatMessage(copy(prefixComponent).appendSibling(copy(component)))
|
||||
}
|
||||
|
||||
fun say(component: IChatComponent) {
|
||||
player.addChatMessage(copy(prefixComponent).appendSibling(copy(component)))
|
||||
}
|
||||
|
||||
fun say(player: EntityPlayer, component: IChatComponent) {
|
||||
player.addChatMessage(copy(prefixComponent).appendSibling(copy(component)))
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.github.itzilly.sbt.util
|
||||
|
||||
import net.minecraftforge.common.MinecraftForge
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent
|
||||
|
||||
class DelayedFunction(private val function: () -> Unit, private val delayTicks: Int) {
|
||||
private var ticksLeft: Int = delayTicks
|
||||
|
||||
init {
|
||||
MinecraftForge.EVENT_BUS.register(this)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
fun onClientTick(event: TickEvent.ClientTickEvent) {
|
||||
if (ticksLeft > 0) {
|
||||
ticksLeft--
|
||||
if (ticksLeft == 0) {
|
||||
function()
|
||||
MinecraftForge.EVENT_BUS.unregister(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
146
src/main/kotlin/com/github/itzilly/sbt/util/SimpleChatMsg.kt
Normal file
146
src/main/kotlin/com/github/itzilly/sbt/util/SimpleChatMsg.kt
Normal file
@ -0,0 +1,146 @@
|
||||
package com.github.itzilly.sbt.util
|
||||
|
||||
import net.minecraft.util.ChatComponentText
|
||||
import net.minecraft.util.ChatStyle
|
||||
import net.minecraft.util.EnumChatFormatting.BLACK
|
||||
import net.minecraft.util.EnumChatFormatting.DARK_BLUE
|
||||
import net.minecraft.util.EnumChatFormatting.DARK_GREEN
|
||||
import net.minecraft.util.EnumChatFormatting.DARK_AQUA
|
||||
import net.minecraft.util.EnumChatFormatting.DARK_RED
|
||||
import net.minecraft.util.EnumChatFormatting.DARK_PURPLE
|
||||
import net.minecraft.util.EnumChatFormatting.GOLD
|
||||
import net.minecraft.util.EnumChatFormatting.GRAY
|
||||
import net.minecraft.util.EnumChatFormatting.DARK_GRAY
|
||||
import net.minecraft.util.EnumChatFormatting.BLUE
|
||||
import net.minecraft.util.EnumChatFormatting.GREEN
|
||||
import net.minecraft.util.EnumChatFormatting.AQUA
|
||||
import net.minecraft.util.EnumChatFormatting.RED
|
||||
import net.minecraft.util.EnumChatFormatting.LIGHT_PURPLE
|
||||
import net.minecraft.util.EnumChatFormatting.YELLOW
|
||||
import net.minecraft.util.EnumChatFormatting.WHITE
|
||||
import net.minecraft.util.EnumChatFormatting.OBFUSCATED
|
||||
import net.minecraft.util.EnumChatFormatting.BOLD
|
||||
import net.minecraft.util.EnumChatFormatting.STRIKETHROUGH
|
||||
import net.minecraft.util.EnumChatFormatting.UNDERLINE
|
||||
import net.minecraft.util.EnumChatFormatting.ITALIC
|
||||
import net.minecraft.util.EnumChatFormatting.RESET
|
||||
import net.minecraft.util.IChatComponent
|
||||
|
||||
class SimpleChatMsg(msg: String) {
|
||||
private var component: IChatComponent
|
||||
|
||||
init {
|
||||
component = ChatComponentText(msg)
|
||||
}
|
||||
|
||||
|
||||
fun default(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(RESET)
|
||||
return component
|
||||
}
|
||||
|
||||
fun black(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(BLACK)
|
||||
return component
|
||||
}
|
||||
|
||||
fun dark_blue(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(DARK_BLUE)
|
||||
return component
|
||||
}
|
||||
|
||||
fun dark_green(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(DARK_GREEN)
|
||||
return component
|
||||
}
|
||||
|
||||
fun dark_aqua(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(DARK_AQUA)
|
||||
return component
|
||||
}
|
||||
|
||||
fun dark_red(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(DARK_RED)
|
||||
return component
|
||||
}
|
||||
|
||||
fun dark_purple(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(DARK_PURPLE)
|
||||
return component
|
||||
}
|
||||
|
||||
fun gold(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(GOLD)
|
||||
return component
|
||||
}
|
||||
|
||||
fun gray(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(GRAY)
|
||||
return component
|
||||
}
|
||||
|
||||
fun dark_gray(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(DARK_GRAY)
|
||||
return component
|
||||
}
|
||||
|
||||
fun blue(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(BLUE)
|
||||
return component
|
||||
}
|
||||
|
||||
fun green(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(GREEN)
|
||||
return component
|
||||
}
|
||||
|
||||
fun aqua(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(AQUA)
|
||||
return component
|
||||
}
|
||||
|
||||
fun red(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(RED)
|
||||
return component
|
||||
}
|
||||
|
||||
fun light_purple(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(LIGHT_PURPLE)
|
||||
return component
|
||||
}
|
||||
|
||||
fun yellow(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(YELLOW)
|
||||
return component
|
||||
}
|
||||
|
||||
fun white(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(WHITE)
|
||||
return component
|
||||
}
|
||||
|
||||
fun obfuscated(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(OBFUSCATED)
|
||||
return component
|
||||
}
|
||||
|
||||
fun bold(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(BOLD)
|
||||
return component
|
||||
}
|
||||
|
||||
fun strikethrough(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(STRIKETHROUGH)
|
||||
return component
|
||||
}
|
||||
|
||||
fun underline(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(UNDERLINE)
|
||||
return component
|
||||
}
|
||||
|
||||
fun italic(): IChatComponent {
|
||||
component.chatStyle = ChatStyle().setColor(ITALIC)
|
||||
return component
|
||||
}
|
||||
}
|
19
src/main/kotlin/com/github/itzilly/sbt/util/WorldUtils.kt
Normal file
19
src/main/kotlin/com/github/itzilly/sbt/util/WorldUtils.kt
Normal file
@ -0,0 +1,19 @@
|
||||
package com.github.itzilly.sbt.util
|
||||
|
||||
import net.minecraft.client.Minecraft
|
||||
|
||||
fun getMiniServerIdFromScoreboard(): String? {
|
||||
val scoreboard = Minecraft.getMinecraft().theWorld?.scoreboard ?: return null
|
||||
val objective = scoreboard.getObjectiveInDisplaySlot(1) ?: return null
|
||||
|
||||
return scoreboard.getSortedScores(objective)
|
||||
.mapNotNull { score ->
|
||||
try {
|
||||
score.playerName
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
.map { it.replace(Regex("§."), "").trim() }
|
||||
.firstOrNull { it.matches(Regex("^\\[mini\\w+]$")) }
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
key.categories.sbt=SkyBlock Tweaks
|
||||
key.addWaypoint=Add Route Point
|
||||
key.finishWaypoints=Finish Route
|
||||
key.clearWaypoints=Clear Route Points
|
||||
key.searchGrotto=Search Grotto
|
||||
key.clearGrotto=Clear Grotto
|
||||
key.addRouteNode=Add Route Node
|
||||
key.removeLastNode=Remove Last Node
|
||||
key.finishRoute=Finish Route
|
||||
|
||||
key.categories.sbt=SkyBlock Tweaks
|
@ -1,14 +1,14 @@
|
||||
[
|
||||
{
|
||||
"modid": "${modid}",
|
||||
"name": "Xample Mod",
|
||||
"description": "A mod that is used as an example.",
|
||||
"name": "SkyBlock Tweaks",
|
||||
"description": "Simple tweaks for Hypixel Skyblock",
|
||||
"version": "${version}",
|
||||
"mcversion": "${mcversion}",
|
||||
"url": "https://github.com/romangraef/Forge1.8.9Template/",
|
||||
"url": "http://itzilly.com/illyum/SkyBlockTweaks",
|
||||
"updateUrl": "",
|
||||
"authorList": [
|
||||
"You"
|
||||
"itzilly"
|
||||
],
|
||||
"credits": "",
|
||||
"logoFile": "",
|
||||
|
Loading…
x
Reference in New Issue
Block a user