Compare commits

..

2 Commits

Author SHA1 Message Date
2946914101 feat: add rewrite 2025-04-22 09:22:46 -06:00
09ed358681 Clean Project 2025-04-22 00:22:06 -06:00
41 changed files with 852 additions and 1417 deletions

View File

@ -1,10 +0,0 @@
## [1.4.0] - 2024-07-23
### Chore
- Remove comments
### Feat
- *(chestesp)* Add initial implementation, halfway done

View File

@ -2,15 +2,7 @@
My QOL mod for Hypixel Skyblock
## Includes:
- Grotto Finder
- Gemstone Finder
- Powder ChestESP
- Route Builder
## Config
You can configure the gemstone search radius, and also toggle the powder chest esp
## Commands:
- /sbt gemstonesearchradius <number>
- /sbt powderchest [disable/enable]
## 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

View File

@ -18,7 +18,7 @@ val modid: String by project
val mixinGroup = "$baseGroup.mixin"
group = baseGroup
version = "$versionBase-b$buildNumber"
version = "alpha_$versionBase-b$buildNumber"
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(8))
@ -140,31 +140,6 @@ tasks.assemble {
dependsOn("incrementBuildNumber")
}
tasks.register<Copy>("illyExtractMappings") {
from("O:/.gradle")
into("${layout.buildDirectory}/mcp_mappings")
}
tasks.register("illyApplyMappings") {
dependsOn("illyExtractMappings")
doLast {
val mappingsDir = file("O:/Kotlin Programming/sbt/build/mcp_mappings")
if (mappingsDir.exists()) {
project.ext.set("mappingsPath", mappingsDir.absolutePath)
} else {
throw GradleException("Mappings directory not found!")
}
}
}
tasks.withType<JavaCompile> {
dependsOn("illyApplyMappings")
doLast {
options.compilerArgs.add("-Amap=O:/Kotlin Programming/sbt/build/mcp_mappings")
}
}
// 🔁 Auto-increment build number each assemble
tasks.register("incrementBuildNumber") {
doLast {
val propsFile = file("gradle.properties")

View File

@ -1,36 +0,0 @@
# cliff.toml
[changelog]
title = "Changelog"
description = "All notable changes to this project will be documented in this file."
repository_url = "http://localhost:3001/illyum/sbt-stash"
template = """
# {{version}} - {{date}}
{% for group, commits in commits | group_by(attribute="group") %}
### {{group | upper_first}}
{% for commit in commits %}
- {{ commit.message | upper_first }} ({% if commit.breaking %}! {% endif %}{{ commit.hash }})
{% endfor %}
{% endfor %}
"""
[git]
commit_parsers = [
{ message = "^\\(Chore\\)", group = "Chore" },
{ message = "^\\(BugFix\\)", group = "Bug Fixes" },
{ message = "^\\(Feat\\)", group = "Features" },
{ message = "^\\(Docs\\)", group = "Documentation" },
{ message = "^\\(Refactor\\)", group = "Refactor" },
{ message = "^\\(Test\\)", group = "Tests" },
{ message = "^\\(Style\\)", group = "Styling" },
{ message = "^\\(Perf\\)", group = "Performance" },
]
[git.tag]
pattern = "^v[0-9]+"
[git.commit]
conventional = false
[git.filters]
exclude_merge_commits = true

View 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>")
}
}
}
```

View File

@ -1,33 +0,0 @@
# Contributing to This Project
Thank you for your interest in contributing to this project! Please read the following guidelines to understand how you can contribute.
## Private Repository
This is a **private repository**. While you're welcome to file issues and suggest ideas, please note that this project is primarily a personal endeavor. Contributions from the community are appreciated, but they might not always be incorporated.
## Filing Issues
If you encounter any issues or have suggestions for improvements, feel free to [open an issue](https://github.com/your-repo/issues) (or email me). Please be as detailed as possible when describing the problem or idea. I appreciate your input!
## Contributing Fixes
If you have a fix that you'd like to contribute:
- Please do **not** submit a pull request directly.
- Instead, email the developer at [developer@itzilly.com](mailto:developer@itzilly.com) with details about your fix.
- If the fix aligns with the project's goals and standards, you'll receive guidance on how to proceed.
## Suggesting Ideas
I welcome ideas and suggestions! However, please keep in mind:
- This project is more of a personal project, so while all ideas are welcome, not every suggestion will be implemented.
- Feel free to suggest ideas by [opening an issue](https://github.com/404) and detailing your thoughts.
## Code of Conduct
Please remember to be respectful and constructive when engaging with this project. At the end of the day, this is just my project.
Thank you for your understanding and for contributing to this project!
# Note for persons with a Gitea account
If you have access to an account with elevated permissions on this repo, you're welcome to commit code/make pull requests.

View File

@ -1,55 +0,0 @@
# Developer Notice
Welcome to my project! This is primarily a personal project, and while I'm happy to have others take an interest, it's important to follow the guidelines below to ensure consistency and maintainability. (This is mostly for my reference)
## Conventional Commits
I use [Conventional Commits](https://www.conventionalcommits.org/) for all commit messages. This means that every commit should follow this format:
```
<type>(<scope>): <description>
```
- **Type:** The type of change you're committing. Common types include `feat` (new feature), `fix` (bug fix), `docs` (documentation only changes), `style` (code style, formatting, etc.), `refactor` (refactoring code), `test` (adding or updating tests), and `chore` (other changes that don't modify src or test files).
- **Scope:** An optional field that describes the part of the codebase the change affects (e.g., `api`, `config`, `ui`).
- **Description:** A brief description of the change.
Example commit message:
```
feat(api): add new endpoint for user data
```
```
chore(release): bump version to 1.4.3
```
```
style(ui): clean up redundant CSS classes in header component
```
```
fix(auth): resolve login redirect loop issue
```
```
merge(pr-45): integrate feature-x from pull request #45
```
## Branching Strategy
Branches are primarily for adding new features. However, while the project is in pre-release, it's okay to work on minor features directly on the `main` branch. Just make sure you:
- Commit all changes for the feature at once.
- Avoid having partial changes spread across multiple commits. Each commit should represent a complete and functional piece of work.
## Versioning
The versioning scheme I use is simple:
- **Patch version:** Bumped when small, incremental changes or bug fixes are made. For example, `x.x.y` to `x.x.y + 1`.
- **Minor version:** Typically, if a feature is added or updated, the version may stay the same until the project exits pre-release. Version `2.x.x` will be used if the project ever gets out of pre-release.
- **Major version:** Reserved for significant changes, like command updates, configuration overhauls, or substantial new features.
Remember, this is my project, so while I'm open to contributions and suggestions, I maintain the final say on what gets merged or included. Thanks for your understanding and contributions!

View File

@ -1,8 +1,8 @@
#Sat Apr 19 14:54:37 MDT 2025
versionBase=1.5.0
#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
buildNumber=107
buildNumber=214
modid=sbt

Binary file not shown.

View File

@ -1,48 +0,0 @@
package com.github.itzilly.sbt
import com.github.itzilly.sbt.features.ChestEsp
import com.github.itzilly.sbt.features.GemstoneFinder
import com.github.itzilly.sbt.features.GrottoFinder
import com.github.itzilly.sbt.util.Chatterbox
import com.github.itzilly.sbt.util.SimpleChatMsg
import net.minecraft.client.Minecraft
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.InputEvent
class InputHandler {
@SubscribeEvent
fun onKeyInput(event: InputEvent.KeyInputEvent?) {
if (STBKeyBinds.searchForChests.isPressed) {
ChestEsp.highlightChests()
}
if (STBKeyBinds.clearChests.isPressed) {
ChestEsp.chestList.clear()
Chatterbox.say(SimpleChatMsg("Chests cleared").green())
}
if (STBKeyBinds.toggleGrottoFinder.isPressed) {
Chatterbox.say("Running check...")
GrottoFinder.checkWorldForMagentaStainedGlass(Minecraft.getMinecraft().thePlayer.worldObj)
Chatterbox.say("Check done")
// if (GrottoFinder.isEnabled) {
// GrottoFinder.isEnabled = false
// Chatterbox.say(SimpleChatMsg("Grotto finder disabled!").red())
// MinecraftForge.EVENT_BUS.register(GrottoFinder)
// } else {
// GrottoFinder.isEnabled = true
// Chatterbox.say(SimpleChatMsg("Grotto finder enabled!").green())
// // MinecraftForge.EVENT_BUS.unregister(GrottoFinder)
// }
}
if (STBKeyBinds.clearGrotto.isPressed) {
GrottoFinder.clear()
Chatterbox.say("Cleared grotto finder")
}
if (STBKeyBinds.findGemstones.isPressed) {
GemstoneFinder.find()
}
if (STBKeyBinds.clearGemstones.isPressed) {
GemstoneFinder.clear()
}
}
}

View 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)
}
}

View File

@ -1,86 +0,0 @@
package com.github.itzilly.sbt
import com.github.itzilly.sbt.features.ChestEsp
import com.github.itzilly.sbt.features.GemstoneFinder
import net.minecraft.command.CommandBase
import net.minecraft.command.CommandException
import net.minecraft.command.ICommandSender
import net.minecraft.util.ChatComponentText
import net.minecraftforge.common.config.Configuration.*
class SBTCommand : CommandBase() {
override fun getCommandName(): String {
return "sbt"
}
override fun getCommandUsage(sender: ICommandSender): String {
return "/sbt <powderchests/gemstonesearchradius> [enable/disable/radius]"
}
override fun getRequiredPermissionLevel(): Int {
return 0
}
@Throws(CommandException::class)
override fun processCommand(sender: ICommandSender, args: Array<String>) {
if (args.isEmpty()) {
sender.addChatMessage(ChatComponentText("Invalid command. Usage: /sbt <powderchests/gemstonesearchradius> [enable/disable/radius]"))
return
}
when (args[0].lowercase()) {
"powderchests" -> {
if (args.size < 2) {
sender.addChatMessage(ChatComponentText("Usage: /sbt powderchests <enable/disable>"))
return
}
when (args[1].lowercase()) {
"enable" -> {
ChestEsp.enabled = true
SkyBlockTweaks.config.getCategory(CATEGORY_GENERAL)?.get("powderchestenable")?.set(true)
SkyBlockTweaks.config.save()
sender.addChatMessage(ChatComponentText("Powder ChestESP enabled."))
}
"disable" -> {
ChestEsp.enabled = false
SkyBlockTweaks.config.getCategory(CATEGORY_GENERAL)?.get("powderchestenable")?.set(false)
SkyBlockTweaks.config.save()
sender.addChatMessage(ChatComponentText("Powder ChestESP disabled."))
}
else -> {
sender.addChatMessage(ChatComponentText("Invalid option. Usage: /sbt powderchests <enable/disable>"))
}
}
}
"gemstonesearchradius" -> {
if (args.size < 2) {
sender.addChatMessage(ChatComponentText("Usage: /sbt gemstonesearchradius <radius>"))
return
}
try {
val radius = args[1].toInt()
if (radius < 0) {
sender.addChatMessage(ChatComponentText("Radius must be a positive integer."))
return
}
SkyBlockTweaks.config.getCategory(CATEGORY_GENERAL)?.get("gemstonesearchradius")?.set(radius)
SkyBlockTweaks.config.save()
GemstoneFinder.radius = radius
sender.addChatMessage(ChatComponentText("Gemstone search radius set to $radius."))
} catch (e: NumberFormatException) {
sender.addChatMessage(ChatComponentText("Invalid radius. Please provide a positive integer."))
}
}
else -> {
sender.addChatMessage(ChatComponentText("Invalid command. Usage: /sbt <powderchests/gemstonesearchradius> [enable/disable/radius]"))
}
}
}
}

View File

@ -1,59 +0,0 @@
package com.github.itzilly.sbt
import net.minecraft.client.settings.KeyBinding
import net.minecraftforge.fml.client.registry.ClientRegistry
import org.lwjgl.input.Keyboard
object STBKeyBinds {
lateinit var searchForChests: KeyBinding
lateinit var clearChests: KeyBinding
lateinit var toggleGrottoFinder: KeyBinding
lateinit var clearGrotto: KeyBinding
lateinit var findGemstones: KeyBinding
lateinit var clearGemstones: KeyBinding
lateinit var toggleLeftWalk: KeyBinding
lateinit var toggleRightWalk: KeyBinding
lateinit var toggleForwardWalk: KeyBinding
lateinit var toggleBackwardWalk: KeyBinding
lateinit var toggleLeftMouse: KeyBinding
fun init() {
searchForChests = KeyBinding("key.xraySearch", Keyboard.KEY_F10, "key.categories.sbt")
ClientRegistry.registerKeyBinding(searchForChests)
clearChests = KeyBinding("key.xrayClear", Keyboard.KEY_MINUS, "key.categories.sbt")
ClientRegistry.registerKeyBinding(clearChests)
toggleGrottoFinder = KeyBinding("key.toggleGrottoFinder", Keyboard.KEY_N, "key.categories.sbt")
ClientRegistry.registerKeyBinding(toggleGrottoFinder)
clearGrotto = KeyBinding("key.clearGrotto", Keyboard.KEY_F1, "key.categories.sbt")
ClientRegistry.registerKeyBinding(clearGrotto)
findGemstones = KeyBinding("key.findGemstones", Keyboard.KEY_J, "key.categories.sbt")
ClientRegistry.registerKeyBinding(findGemstones)
clearGemstones = KeyBinding("key.clearGemstones", Keyboard.KEY_K, "key.categories.sbt")
ClientRegistry.registerKeyBinding(clearGemstones)
// -----------------------
// Macro Toggle
// -----------------------
toggleLeftWalk = KeyBinding("key.toggleLeftWalk", Keyboard.KEY_LEFT, "key.categories.sbt")
ClientRegistry.registerKeyBinding(toggleLeftWalk)
toggleRightWalk = KeyBinding("key.toggleRightWalk", Keyboard.KEY_RIGHT, "key.categories.sbt")
ClientRegistry.registerKeyBinding(toggleRightWalk)
toggleForwardWalk = KeyBinding("key.toggleForwardWalk", Keyboard.KEY_UP, "key.categories.sbt")
ClientRegistry.registerKeyBinding(toggleForwardWalk)
toggleBackwardWalk = KeyBinding("key.toggleBackwardWalk", Keyboard.KEY_DOWN, "key.categories.sbt")
ClientRegistry.registerKeyBinding(toggleBackwardWalk)
toggleLeftMouse = KeyBinding("key.toggleLeftMouse", Keyboard.KEY_PRIOR, "key.categories.sbt")
ClientRegistry.registerKeyBinding(toggleLeftMouse)
}
}

View File

@ -1,12 +1,11 @@
package com.github.itzilly.sbt
import com.github.itzilly.sbt.features.ChestEsp
import com.github.itzilly.sbt.features.FarmHelper
import com.github.itzilly.sbt.features.GemstoneFinder
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.router.RouteBuilder
import com.github.itzilly.sbt.util.MessageScheduler
import net.minecraftforge.client.ClientCommandHandler
import com.github.itzilly.sbt.features.RouteBuilder
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.common.config.Configuration
import net.minecraftforge.fml.common.Mod
@ -34,41 +33,16 @@ class SkyBlockTweaks {
@Mod.EventHandler
fun init(event: FMLInitializationEvent) {
STBKeyBinds.init()
Keybinds.register()
registerEventListeners()
MinecraftForge.EVENT_BUS.register(RouteBuilder)
MinecraftForge.EVENT_BUS.register(InputHandler())
MinecraftForge.EVENT_BUS.register(MessageScheduler)
MinecraftForge.EVENT_BUS.register(GrottoFinder)
MinecraftForge.EVENT_BUS.register(ChestEsp)
MinecraftForge.EVENT_BUS.register(GemstoneFinder)
MinecraftForge.EVENT_BUS.register(FarmHelper)
ClientCommandHandler.instance.registerCommand(SBTCommand())
ModCommandRegistry.register(ClientCommands)
registerBaseCommand()
}
private fun loadConfig() {
config.load()
val powderChestEnable = config.getBoolean(
"powderchestenable",
Configuration.CATEGORY_GENERAL,
true,
"Enables/Disables Powder ChestESP"
)
ChestEsp.enabled = powderChestEnable
val gemstoneRadius = config.getInt(
"gemstonesearchradius",
Configuration.CATEGORY_GENERAL,
16,
1,
256,
"Enables/Disables Powder ChestESP"
)
ChestEsp.enabled = powderChestEnable
GemstoneFinder.radius = gemstoneRadius
if (config.hasChanged()) {
config.save()
}
@ -80,4 +54,9 @@ class SkyBlockTweaks {
config.save()
}
}
private fun registerEventListeners() {
MinecraftForge.EVENT_BUS.register(RouteBuilder)
MinecraftForge.EVENT_BUS.register(GrottoFinder)
}
}

View File

@ -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> = []
)

View File

@ -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
}
})
}

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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}")
}
}
}
}

View 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
)

View 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)

View File

@ -1,175 +0,0 @@
package com.github.itzilly.sbt.features
import com.github.itzilly.sbt.SkyBlockTweaks
import com.github.itzilly.sbt.renderer.RenderUtils
import java.awt.Color
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.Vecd3
import net.minecraft.block.Block
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.GlStateManager
import net.minecraft.init.Blocks
import net.minecraft.util.BlockPos
import net.minecraft.entity.Entity
import net.minecraft.tileentity.TileEntityChest
import net.minecraft.util.AxisAlignedBB
import net.minecraft.util.MovingObjectPosition
import net.minecraftforge.client.event.ClientChatReceivedEvent
import net.minecraftforge.client.event.RenderWorldLastEvent
import net.minecraftforge.common.config.Configuration
import net.minecraftforge.event.entity.player.PlayerInteractEvent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import org.lwjgl.opengl.GL11
data class ChestNode(
val x: Int,
val y: Int,
val z: Int,
)
fun chestNodeExists(x: Int, y: Int, z: Int): Boolean {
return ChestEsp.chestList.any { it.x == x && it.y == y && it.z == z }
}
object ChestEsp {
var chestList: ArrayList<ChestNode> = ArrayList()
var enabled: Boolean = SkyBlockTweaks.config.getBoolean("powderchestenable", Configuration.CATEGORY_GENERAL, true, "Enables/Disables Powder ChestESP")
@SubscribeEvent
fun chatMessageEvent(event: ClientChatReceivedEvent) {
if (!enabled) return
if (event.message.unformattedText == "You uncovered a treasure chest!") {
DelayedFunction({ highlightChests() }, 5)
}
}
@SubscribeEvent
fun renderBlockOverlay(event: RenderWorldLastEvent) {
if (!enabled) return
val player = Minecraft.getMinecraft().thePlayer
for (chest: ChestNode in chestList) {
val pos = BlockPos(chest.x, chest.y, chest.z)
drawChestOutline(player, pos, event.partialTicks)
drawChestLine(pos, event.partialTicks)
}
}
@SubscribeEvent
fun rightClickEvent(event: PlayerInteractEvent) {
if (!enabled) return
if (event.action == PlayerInteractEvent.Action.RIGHT_CLICK_BLOCK) {
val pos = event.pos
if (chestNodeExists(pos.x, pos.y, pos.z)) {
chestList.removeIf { it.x == pos.x && it.y == pos.y && it.z == pos.z }
checkChestLocations()
}
}
}
fun checkChestLocations() {
for (chest in chestList) {
if (chestNodeExists(chest.x, chest.y, chest.z)) {
chestList.removeIf { it.x == chest.x && it.y == chest.y && it.z == chest.z }
}
}
}
fun highlightChests() {
if (!enabled) return
val searchRadius = 8
val world = Minecraft.getMinecraft().thePlayer.worldObj
val posX = Minecraft.getMinecraft().thePlayer.posX
val posY = Minecraft.getMinecraft().thePlayer.posY
val posZ = Minecraft.getMinecraft().thePlayer.posZ
val boundXMax = posX + searchRadius
val boundXMin = posX - searchRadius
val boundYMax = posY + searchRadius
val boundYMin = posY - searchRadius
val boundZMax = posZ + searchRadius
val boundZMin = posZ - searchRadius
for (x in boundXMin.toInt()..boundXMax.toInt()) {
for (y in boundYMin.toInt()..boundYMax.toInt()) {
for (z in boundZMin.toInt()..boundZMax.toInt()) {
val block = world.getBlockState(BlockPos(x, y, z)).block
if (block == Blocks.chest || block == Blocks.trapped_chest || block == Blocks.ender_chest) {
val tileEntity = world.getTileEntity(BlockPos(x, y, z))
if (tileEntity is TileEntityChest) {
if (tileEntity.numPlayersUsing != 0) {
val point = ChestNode(x, y, z)
if (!chestNodeExists(x, y, z)) {
chestList.add(point)
}
}
}
}
}
}
}
checkChestLocations()
}
private fun drawChestOutline(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()
}
private fun drawChestLine(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)
RenderUtils.drawLine(pos1, pos2, lineColor, 2, false, partialTicks)
}
}

View File

@ -1,105 +0,0 @@
package com.github.itzilly.sbt.features
import com.github.itzilly.sbt.STBKeyBinds.toggleLeftWalk;
import com.github.itzilly.sbt.features.gardener.CaneMacroer
import com.github.itzilly.sbt.util.Chatterbox
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Gui
import net.minecraft.client.gui.GuiChat
import net.minecraftforge.client.event.RenderGameOverlayEvent
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.InputEvent
import net.minecraftforge.fml.common.gameevent.TickEvent
import org.lwjgl.input.Keyboard
object FarmHelper {
private val mc: Minecraft = Minecraft.getMinecraft()
private var isToggledLeft = false
private var isToggledRight = false
private var isToggledForward = false
private var isToggledBack = false
private var isToggledAttack = false
private var ignoreNextKeyUp = false
private var lastToggleKeyCode = -1
@SubscribeEvent
fun onTick(event: TickEvent.ClientTickEvent) {
if (mc.gameSettings.keyBindForward.isKeyDown) isToggledForward = false
if (mc.gameSettings.keyBindLeft.isKeyDown) isToggledLeft = false
if (mc.gameSettings.keyBindRight.isKeyDown) isToggledRight = false
if (mc.gameSettings.keyBindBack.isKeyDown) isToggledBack = false
if (mc.gameSettings.keyBindAttack.isKeyDown) isToggledAttack = false
}
@SubscribeEvent
fun onKeyInput(event: InputEvent.KeyInputEvent) {
val key = Keyboard.getEventKey()
val isPressed = Keyboard.getEventKeyState()
if (toggleLeftWalk.isPressed) {
Chatterbox.say("Enabled macro!")
CaneMacroer.isMacroToggled = true
MinecraftForge.EVENT_BUS.register(CaneMacroer)
CaneMacroer.mc.displayGuiScreen(GuiChat(""))
lastToggleKeyCode = key
ignoreNextKeyUp = true
return
}
if (!isPressed && ignoreNextKeyUp && key == lastToggleKeyCode) {
ignoreNextKeyUp = false
return
}
if (CaneMacroer.isMacroToggled) {
Chatterbox.say("Macro disabled by input!")
CaneMacroer.isMacroToggled = false
CaneMacroer.stop()
MinecraftForge.EVENT_BUS.unregister(CaneMacroer)
}
// if (toggleLeftWalk.isPressed) isToggledLeft = !isToggledLeft
// if (toggleRightWalk.isPressed) isToggledRight = !isToggledRight
// if (toggleForwardWalk.isPressed) isToggledForward = !isToggledForward
// if (toggleBackwardWalk.isPressed) isToggledBack = !isToggledBack
// if (toggleLeftMouse.isPressed) isToggledAttack = !isToggledAttack
//
// if (isToggledLeft) (mc.gameSettings.keyBindLeft as KeyBindingAccessor).setPressed(true)
// if (isToggledRight) (mc.gameSettings.keyBindRight as KeyBindingAccessor).setPressed(true)
// if (isToggledForward) (mc.gameSettings.keyBindForward as KeyBindingAccessor).setPressed(true)
// if (isToggledBack) (mc.gameSettings.keyBindBack as KeyBindingAccessor).setPressed(true)
// if (isToggledAttack) (mc.gameSettings.keyBindAttack as KeyBindingAccessor).setPressed(true)
}
@SubscribeEvent
fun onRenderOverlay(event: RenderGameOverlayEvent.Text) {
val x = 10f
var y = 10f
val toggledKeys = mutableListOf<String>()
if (isToggledForward) toggledKeys.add("W")
if (isToggledLeft) toggledKeys.add("A")
if (isToggledBack) toggledKeys.add("S")
if (isToggledRight) toggledKeys.add("D")
if (isToggledAttack) toggledKeys.add("Attack")
if (toggledKeys.isEmpty()) return
val boxWidth = 60
val boxHeight = 10 + toggledKeys.size * 10
Gui.drawRect((x - 2).toInt(), (y - 2).toInt(), (x + boxWidth).toInt(), (y + boxHeight).toInt(), 0x90000000.toInt())
for (label in toggledKeys) {
mc.fontRendererObj.drawStringWithShadow("[$label]", x, y, 0xFFFFFF)
y += 10f
}
}
}

View File

@ -1,158 +0,0 @@
package com.github.itzilly.sbt.features
import com.github.itzilly.sbt.SkyBlockTweaks
import com.github.itzilly.sbt.renderer.RenderFuncs
import net.minecraft.client.Minecraft
import net.minecraft.init.Blocks
import net.minecraft.util.BlockPos
import net.minecraftforge.client.event.RenderWorldLastEvent
import net.minecraftforge.common.config.Configuration
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
enum class GemstoneColors(val color: Int) {
RUBY(0xFF5555), // Red
JADE(0x00AA00), // Lime
AMBER(0xFFAA00), // Orange
SAPPHIRE(0x55FFFF), // Light Blue
AMETHYST(0xAA00AA), // Purple
TOPAZ(0xFFFF55), // Yellow
JASPER(0xFF55FF) // Magenta
}
object GemstoneFinder {
private var gemstoneClusters: ArrayList<GemstoneCluster> = ArrayList()
private var gemstoneList: ArrayList<GemstoneBlock> = ArrayList()
var radius = SkyBlockTweaks.config.getCategory(Configuration.CATEGORY_GENERAL)?.get("gemstonesearchradius")?.int?: 16
fun clear() {
gemstoneClusters.clear()
}
fun find() {
val playerPos = Minecraft.getMinecraft().thePlayer.position
val minBounds = BlockPos(
playerPos.x - radius,
maxOf(playerPos.y - radius, 31),
playerPos.z - radius
)
val maxBounds = BlockPos(
playerPos.x + radius,
minOf(playerPos.y + radius, 188),
playerPos.z + radius
)
val world = Minecraft.getMinecraft().thePlayer.worldObj
val gemstonesMap = mutableMapOf<GemstoneColors, MutableList<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
if (block == Blocks.air) continue
val meta = block.getMetaFromState(blockState)
val gemstoneColor = when {
block === Blocks.stained_glass || block === Blocks.stained_glass_pane -> {
when (meta) {
1 -> GemstoneColors.AMBER // Orange
2 -> GemstoneColors.JASPER // Magenta
3 -> GemstoneColors.SAPPHIRE // Light Blue
4 -> GemstoneColors.TOPAZ // Yellow
5 -> GemstoneColors.JADE // Lime
14 -> GemstoneColors.RUBY // Red
10 -> GemstoneColors.AMETHYST // Purple
else -> null
}
}
else -> null
}
if (gemstoneColor != null) {
gemstonesMap.getOrPut(gemstoneColor) { mutableListOf() }.add(blockPos)
}
}
}
}
}
}
}
for ((color, positions) in gemstonesMap) {
val clusters = clusterGemstones(positions)
for (cluster in clusters) {
gemstoneClusters.add(GemstoneCluster(cluster, color))
}
}
}
private fun clusterGemstones(positions: List<BlockPos>): List<List<BlockPos>> {
val clusters = mutableListOf<List<BlockPos>>()
val visited = mutableSetOf<BlockPos>()
fun dfs(start: BlockPos, cluster: MutableList<BlockPos>) {
val stack = ArrayDeque<BlockPos>()
stack.add(start)
while (stack.isNotEmpty()) {
val pos = stack.removeLast()
if (pos in visited) continue
visited.add(pos)
cluster.add(pos)
// Check adjacent blocks in all six directions
for (offset in listOf(BlockPos(1, 0, 0), BlockPos(-1, 0, 0), BlockPos(0, 1, 0), BlockPos(0, -1, 0), BlockPos(0, 0, 1), BlockPos(0, 0, -1))) {
val neighbor = pos.add(offset)
if (neighbor in positions && neighbor !in visited) {
stack.add(neighbor)
}
}
}
}
for (pos in positions) {
if (pos !in visited) {
val cluster = mutableListOf<BlockPos>()
dfs(pos, cluster)
clusters.add(cluster)
}
}
return clusters
}
@SubscribeEvent
fun renderBlockOverlay(event: RenderWorldLastEvent) {
val player = Minecraft.getMinecraft().thePlayer
for (cluster in gemstoneClusters) {
RenderFuncs.drawClusterOutline(player, cluster.positions, event.partialTicks, cluster.color.color)
}
}
data class GemstoneCluster(
val positions: List<BlockPos>,
val color: GemstoneColors
)
data class GemstoneBlock(
val x: Int,
val y: Int,
val z: Int,
val color: GemstoneColors,
val maxX: Int = x,
val maxY: Int = y,
val maxZ: Int = z
)
}

View File

@ -1,38 +1,82 @@
package com.github.itzilly.sbt.features
import com.github.itzilly.sbt.renderer.RenderFuncs
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.MessageScheduler
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.block.state.IBlockState
import net.minecraft.client.Minecraft
import net.minecraft.client.network.NetHandlerPlayClient
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 magentaGlassList: ArrayList<ChestNode> = ArrayList()
private var lastWorldLoadTime = 0L
private var lastMiniServerId: String? = null
private val minBounds = BlockPos(201, 30, 201)
private val maxBounds = BlockPos(824, 189, 824)
private var renderBlockList: ArrayList<BlockPos> = ArrayList()
private var jasperCrystalsList: ArrayList<BlockPos> = ArrayList()
var isEnabled: Boolean = true
fun clear() {
magentaGlassList.clear()
fun enableAutoDetect() {
autoDetectEnabled = true
}
fun checkWorldForMagentaStainedGlass(world: World) {
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)
@ -51,38 +95,244 @@ object GrottoFinder {
val block = blockState.block
val meta = block.getMetaFromState(blockState)
if (block === Blocks.stained_glass && meta == 2) {
if (block === targetBlock && meta == targetMeta) {
if (!magentaNodeExists(blockPos.x, blockPos.y, blockPos.z)) {
magentaGlassList.add(ChestNode(blockPos.x, blockPos.y, blockPos.z))
renderBlockList.add(BlockPos(blockPos.x, blockPos.y, blockPos.z))
}
println("Found magenta stained glass at $blockPos")
val msg = SimpleChatMsg("Found magenta stained glass at $blockPos").light_purple()
}
}
}
}
}
}
}
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: ChestNode in magentaGlassList) {
for (glass: BlockPos in renderBlockList) {
val pos = BlockPos(glass.x, glass.y, glass.z)
RenderFuncs.drawBlockOutline(player, pos, event.partialTicks)
RenderFuncs.drawBlockLine(pos, event.partialTicks)
BlockOutlineRenderer.drawBlockOutline(player, pos, event.partialTicks)
BlockOutlineRenderer.drawBlockLine(pos, event.partialTicks)
}
}
private fun magentaNodeExists(x: Int, y: Int, z: Int): Boolean {
return magentaGlassList.any { it.x == x && it.y == y && it.z == z }
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)
}
}
// World Bounds 824, 189, 824 201, 30, 201

View File

@ -1,20 +0,0 @@
package com.github.itzilly.sbt.features
import net.minecraft.client.Minecraft
import net.minecraft.client.settings.GameSettings
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.TickEvent
object MacroInterface {
private val gs: GameSettings = Minecraft.getMinecraft().gameSettings
private var running: Boolean = false
@SubscribeEvent
private fun onClientTick(event: TickEvent.PlayerTickEvent) {
}
fun stop() {
running = false
}
}

View 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()
}
}
}

View File

@ -1,83 +0,0 @@
package com.github.itzilly.sbt.features.gardener
import com.github.itzilly.sbt.mixin.KeyBindingAccessor
import com.github.itzilly.sbt.util.Chatterbox
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiChat
import net.minecraft.client.settings.KeyBinding
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.TickEvent
import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent
object CaneMacroer {
var isMacroToggled: Boolean = false
val mc = Minecraft.getMinecraft()
val allowedScreens = listOf(GuiChat::class.java)
@SubscribeEvent
fun onTick(event: ClientTickEvent) {
if (event.phase != TickEvent.Phase.END) return
if (!isMacroToggled) return
val currentScreen = mc.currentScreen
if (currentScreen != null && allowedScreens.none { it.isInstance(currentScreen) }) {
Chatterbox.say("Macro stopped: Invalid screen")
stop()
return
}
val mop = mc.objectMouseOver
if (mop == null || mop.typeOfHit?.name != "BLOCK") return
val blockPos = mop.blockPos
val facing = mop.sideHit
val player = mc.thePlayer
val blockCenter = blockPos.add(0.5, 0.5, 0.5)
val distanceSq = player.getDistanceSq(blockCenter.x.toDouble(), blockCenter.y.toDouble(), blockCenter.z.toDouble())
if (distanceSq > 25) {
Chatterbox.say("Macro stopped: Too far from block")
stop()
return
}
if (isEntityBlockingView()) {
Chatterbox.say("Macro stopped: Entity in the way")
stop()
return
}
pressKey(mc.gameSettings.keyBindForward.keyCode)
mc.playerController.onPlayerDamageBlock(blockPos, facing)
player.swingItem()
}
fun stop() {
KeyBinding.setKeyBindState(mc.gameSettings.keyBindForward.keyCode, false)
KeyBinding.setKeyBindState(mc.gameSettings.keyBindAttack.keyCode, false)
isMacroToggled = false
}
private fun pressKey(keyCode: Int) {
KeyBinding.setKeyBindState(keyCode, true)
}
private fun isEntityBlockingView(): Boolean {
val lookVec = mc.thePlayer.lookVec
val eyePos = mc.thePlayer.getPositionEyes(1f)
val entities = mc.theWorld.getEntitiesWithinAABBExcludingEntity(
mc.thePlayer,
mc.thePlayer.entityBoundingBox.expand(lookVec.xCoord * 5, lookVec.yCoord * 5, lookVec.zCoord * 5)
)
return entities.any {
it.canBeCollidedWith() &&
it.entityBoundingBox.isVecInside(eyePos)
}
}
}

View File

@ -1,199 +0,0 @@
package com.github.itzilly.sbt.features.gardener
import com.github.itzilly.sbt.util.Chatterbox
import net.minecraft.client.gui.*
class MacroMakerGui : GuiScreen() {
private var selectedTaskIndex: Int = -1
private var draggedTaskIndex: Int = -1
private var dragOffsetY: Int = 0
private val macro = Macro("Example Macro")
enum class ButtonId(val id: Int) {
SAVE_MACRO(0),
CANCEL(1)
}
override fun initGui() {
this.buttonList.clear()
buttonList.add(GuiButton(ButtonId.SAVE_MACRO.id, width / 2 - 100, height - 40, 90, 20, "Save Macro"))
buttonList.add(GuiButton(ButtonId.CANCEL.id, width / 2 + 10, height - 40, 90, 20, "Cancel"))
macro.tasks.add(MoveTask("W", true))
macro.tasks.add(MouseTask("Left Click"))
macro.tasks.add(ChatCommandTask("/spawn"))
}
override fun actionPerformed(button: GuiButton) {
when (button.id) {
ButtonId.SAVE_MACRO.id -> {
Chatterbox.say("Saved macro!")
Close()
}
ButtonId.CANCEL.id -> Close()
}
}
override fun drawScreen(mouseX: Int, mouseY: Int, partialTicks: Float) {
drawDefaultBackground()
val thirdWidth = width / 3
drawRect(0, 0, thirdWidth, height, 0xFF202020.toInt())
drawRect(thirdWidth, 0, thirdWidth * 2, height, 0xFF303030.toInt())
drawRect(thirdWidth * 2, 0, width, height, 0xFF202020.toInt())
drawCenteredString(fontRendererObj, "Macro Maker", width / 2, 10, 0xFFFFFF)
drawLeftPanel(mouseX, mouseY)
drawMiddlePanel(mouseX, mouseY)
drawRightPanel()
super.drawScreen(mouseX, mouseY, partialTicks)
}
private fun drawLeftPanel(mouseX: Int, mouseY: Int) {
drawString(fontRendererObj, "Editing: ${macro.name}", 10, 20, 0xFFFFFF)
drawString(fontRendererObj, "Add Task:", 10, 40, 0xAAAAAA)
val taskButtons = listOf(
Triple("+ Move", 60, { macro.tasks.add(MoveTask("W", false)) }),
Triple("+ Mouse", 80, { macro.tasks.add(MouseTask("Right Click")) }),
Triple("+ Command", 100, { macro.tasks.add(ChatCommandTask("/say Hello")) })
)
for ((label, y, _) in taskButtons) {
drawString(fontRendererObj, label, 10, y, 0x00FF00)
}
if (mouseClickedX in 10..100 && mouseClickedY != -1) {
for ((_, y, action) in taskButtons) {
if (mouseClickedY in y..(y + 10)) {
action.invoke()
break
}
}
mouseClickedX = -1
mouseClickedY = -1
}
}
private fun drawMiddlePanel(mouseX: Int, mouseY: Int) {
val thirdWidth = width / 3
val taskStartY = 40
val taskHeight = 20
for ((index, task) in macro.tasks.withIndex()) {
val y = taskStartY + index * (taskHeight + 5)
if (draggedTaskIndex == index) continue // skip rendering the dragged one
val color = if (index == selectedTaskIndex) 0xFF4444 else 0xFFFFFF
drawRect(thirdWidth + 10, y, thirdWidth * 2 - 10, y + taskHeight, 0xFF404040.toInt())
drawString(fontRendererObj, "${task.name} - ${task.getPreview()}", thirdWidth + 15, y + 6, color)
}
// Draw the dragged task following the mouse
if (draggedTaskIndex != -1) {
val task = macro.tasks[draggedTaskIndex]
val y = mouseY - dragOffsetY
drawRect(thirdWidth + 10, y, thirdWidth * 2 - 10, y + taskHeight, 0xFF606060.toInt())
drawString(fontRendererObj, "${task.name} - ${task.getPreview()}", thirdWidth + 15, y + 6, 0xFFFF00)
}
}
private fun drawRightPanel() {
val selected = macro.tasks.getOrNull(selectedTaskIndex) ?: return
val startX = width - 180
val startY = 20
val boxWidth = 160
val boxHeight = 80
// Box with outline
drawRect(startX - 2, startY - 2, startX + boxWidth + 2, startY + boxHeight + 2, 0xFFFFFFFF.toInt())
drawRect(startX, startY, startX + boxWidth, startY + boxHeight, 0xFF101010.toInt())
drawString(fontRendererObj, "Task Properties", startX + 5, startY + 5, 0xFFFFFF)
when (selected) {
is MoveTask -> {
drawString(fontRendererObj, "Direction: ${selected.direction}", startX + 5, startY + 25, 0xCCCCCC)
drawString(fontRendererObj, "Hold: ${selected.hold}", startX + 5, startY + 40, 0xCCCCCC)
}
is MouseTask -> {
drawString(fontRendererObj, "Action: ${selected.action}", startX + 5, startY + 25, 0xCCCCCC)
}
is ChatCommandTask -> {
drawString(fontRendererObj, "Command:", startX + 5, startY + 25, 0xCCCCCC)
drawString(fontRendererObj, selected.command, startX + 5, startY + 40, 0x00FFAA)
}
}
}
private var mouseClickedX = -1
private var mouseClickedY = -1
override fun mouseClicked(mouseX: Int, mouseY: Int, mouseButton: Int) {
mouseClickedX = mouseX
mouseClickedY = mouseY
val thirdWidth = width / 3
val taskStartY = 40
val taskHeight = 25
for ((index, _) in macro.tasks.withIndex()) {
val y = taskStartY + index * taskHeight
if (mouseX in (thirdWidth + 10)..(thirdWidth * 2 - 10) && mouseY in y..(y + 20)) {
selectedTaskIndex = index
draggedTaskIndex = index
dragOffsetY = mouseY - y
}
}
super.mouseClicked(mouseX, mouseY, mouseButton)
}
override fun mouseClickMove(mouseX: Int, mouseY: Int, clickedMouseButton: Int, timeSinceLastClick: Long) {
super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick)
}
override fun mouseReleased(mouseX: Int, mouseY: Int, state: Int) {
if (draggedTaskIndex != -1) {
val thirdWidth = width / 3
val taskStartY = 40
val taskHeight = 25
val newIndex = ((mouseY - taskStartY) / taskHeight).coerceIn(0, macro.tasks.size - 1)
if (newIndex != draggedTaskIndex) {
val task = macro.tasks.removeAt(draggedTaskIndex)
macro.tasks.add(newIndex, task)
selectedTaskIndex = newIndex
}
draggedTaskIndex = -1
}
super.mouseReleased(mouseX, mouseY, state)
}
fun Close() {
mc.displayGuiScreen(null)
}
}
sealed class MacroTask(val name: String) {
abstract fun getPreview(): String
}
class MoveTask(var direction: String, var hold: Boolean) : MacroTask("Move") {
override fun getPreview(): String = if (hold) "Hold $direction" else "Tap $direction"
}
class MouseTask(val action: String) : MacroTask("Mouse") {
override fun getPreview(): String = action
}
class ChatCommandTask(var command: String) : MacroTask("Command") {
override fun getPreview(): String = command
}
data class Macro(
val name: String,
val tasks: MutableList<MacroTask> = mutableListOf()
)

View File

@ -1,127 +0,0 @@
package com.github.itzilly.sbt.features.router
import com.github.itzilly.sbt.renderer.RenderFuncs
import java.awt.datatransfer.Clipboard
import java.awt.datatransfer.StringSelection
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.client.settings.KeyBinding
import net.minecraft.util.BlockPos
import net.minecraftforge.client.event.RenderWorldLastEvent
import net.minecraftforge.fml.client.registry.ClientRegistry
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.InputEvent
import org.lwjgl.input.Keyboard
import java.awt.Toolkit
import kotlin.math.round
object RouteBuilder {
var addNodeKeybind: KeyBinding
var deleteLastNodeKeybind: KeyBinding
var finishRouteKeybind: KeyBinding
var nodes: ArrayList<RouteNode> = ArrayList()
init {
addNodeKeybind = KeyBinding("key.addNode", Keyboard.KEY_ADD, "key.categories.sbt")
ClientRegistry.registerKeyBinding(addNodeKeybind)
deleteLastNodeKeybind = KeyBinding("key.removeLastNode", Keyboard.KEY_MINUS, "key.categories.sbt")
ClientRegistry.registerKeyBinding(deleteLastNodeKeybind)
finishRouteKeybind = KeyBinding("key.finishRoute", Keyboard.KEY_F4, "key.categories.sbt")
ClientRegistry.registerKeyBinding(finishRouteKeybind)
}
@SubscribeEvent
fun onKeyInput(event: InputEvent.KeyInputEvent) {
if (addNodeKeybind.isPressed) {
val mc = Minecraft.getMinecraft()
val x = round(mc.thePlayer.posX - 0.5).toInt()
val y = round(mc.thePlayer.posY).toInt() - 1
val z = round(mc.thePlayer.posZ - 0.5).toInt()
if (nodeExists(x, y, z)) {
Chatterbox.say(SimpleChatMsg("Node already exists!").red())
} else {
addNode(x, y, z)
Chatterbox.say("This is a string")
}
}
if (deleteLastNodeKeybind.isPressed) {
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())
}
}
if (finishRouteKeybind.isPressed) {
if (nodes.size == 0) {
val msg = SimpleChatMsg("There aren't any nodes to copy!")
Chatterbox.say(msg.red())
} else {
val jsonString = Gson().toJson(nodes)
val msg = SimpleChatMsg("String copied to clipboard!")
setClipboard(jsonString)
Chatterbox.say(msg.green())
}
}
}
@SubscribeEvent
fun renderBlockOverlay(event: RenderWorldLastEvent) {
val player = Minecraft.getMinecraft().thePlayer
for (marker: RouteNode in nodes) {
val pos = BlockPos(marker.x, marker.y, marker.z)
RenderFuncs.drawBlockOutline(player, pos, event.partialTicks)
RenderFuncs.drawBlockLine(pos, event.partialTicks)
}
}
fun addNode(x: Int, y: Int, z: Int) {
nodes.add(RouteNode(x, y, z, 0u, 1u, 0u, NodeOptions("${nodes.size + 1}")))
}
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}")))
}
fun setClipboard(s: String) {
val selection = StringSelection(s)
val clipboard: Clipboard = Toolkit.getDefaultToolkit().systemClipboard
clipboard.setContents(selection, selection)
}
}
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
)
fun nodeExists(x: Int, y: Int, z: Int): Boolean {
return RouteBuilder.nodes.any { it.x == x && it.y == y && it.z == z }
}

View File

@ -1,28 +0,0 @@
package com.github.itzilly.sbt.features.router
object RouteMaker {
var markerList: ArrayList<RouteMarker> = ArrayList()
}
data class RouteMarker(
val x: Int,
val y: Int,
val z: Int,
val r: UByte,
val g: UByte,
val b: UByte,
val options: RouteOptions
)
data class RouteOptions(
val name: String
)
fun isPointInRouteList(routeList: ArrayList<RouteMarker>, x: Int, y: Int, z: Int): Boolean {
return routeList.any { it.x == x && it.y == y && it.z == z }
}

View File

@ -1,11 +0,0 @@
package com.github.itzilly.sbt.mixin;
import net.minecraft.client.settings.KeyBinding;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(KeyBinding.class)
public interface KeyBindingAccessor {
@Accessor("pressed")
void setPressed(boolean pressed);
}

View File

@ -1,6 +1,6 @@
package com.github.itzilly.sbt.renderer
package com.github.itzilly.sbt.render
import com.github.itzilly.sbt.util.Vecd3
import com.github.itzilly.sbt.data.Vecd3
import net.minecraft.block.Block
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.GlStateManager
@ -11,10 +11,10 @@ import net.minecraft.util.EnumFacing
import org.lwjgl.opengl.GL11
import java.awt.Color
object RenderFuncs {
object BlockOutlineRenderer {
fun drawBlockOutline(entity: Entity, blockPos: BlockPos, partialTicks: Float, outlineColor: Int) {
val padding = 0.0020000000949949026
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
@ -30,7 +30,6 @@ object RenderFuncs {
blockPos.x + 1.0, blockPos.y + 1.0, blockPos.z + 1.0
).expand(padding, padding, padding)
// Convert integer color to r, g, b, a values
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
@ -45,12 +44,9 @@ object RenderFuncs {
GL11.glEnable(GL11.GL_LINE_SMOOTH)
GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST)
GL11.glLineWidth(2.0f)
// Set the color with alpha
GL11.glColor4f(r, g, b, a)
// Draw the outline using the bounding box
RenderUtils.drawBlock(boundingBox.offset(-entityX, -entityY, -entityZ), outlineColor, outlineColor)
RenderPrimitives.drawBlock(boundingBox.offset(-entityX, -entityY, -entityZ), outlineColor, outlineColor)
GL11.glDisable(GL11.GL_LINE_SMOOTH)
GL11.glDisable(GL11.GL_BLEND)
@ -75,7 +71,6 @@ object RenderFuncs {
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(),
@ -91,7 +86,7 @@ object RenderFuncs {
GL11.glLineWidth(2.0f)
GL11.glShadeModel(GL11.GL_SMOOTH)
RenderUtils.drawBlock(boundingBox.offset(-entityX, -entityY, -entityZ), outlineStartColor, outlineEndColor)
RenderPrimitives.drawBlock(boundingBox.offset(-entityX, -entityY, -entityZ), outlineStartColor, outlineEndColor)
GL11.glLineWidth(2.0f)
GL11.glDisable(GL11.GL_LINE_SMOOTH)
@ -115,13 +110,12 @@ object RenderFuncs {
)
val lineColor = Color(0xe310d5)
RenderUtils.drawLine(pos1, pos2, lineColor, 2, false, partialTicks)
RenderPrimitives.drawLine(pos1, pos2, lineColor, 2, false, partialTicks)
}
fun drawClusterOutline(entity: Entity, positions: List<BlockPos>, partialTicks: Float, outlineColor: Int) {
val padding = 0.0020000000949949026
val padding = 0.0020000000949949026 // Magic number DO NOT TOUCH
// Correctly calculate the player's interpolated position
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
@ -138,42 +132,38 @@ object RenderFuncs {
GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST)
GL11.glLineWidth(2.0f)
// Convert integer color to r, g, b, a values
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
// Set the color with alpha
GL11.glColor4f(r, g, b, a)
// Iterate over the positions and draw the outline around each block
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)
// Offset the bounding box by the interpolated player position
val offsetBoundingBox = boundingBox.offset(-entityX, -entityY, -entityZ)
// Check adjacent blocks and only draw faces that are exposed (not adjacent to another gemstone block)
// Only draw exposed faces
if (!positions.contains(pos.add(1, 0, 0))) {
RenderUtils.drawBlockFace(offsetBoundingBox, EnumFacing.EAST, outlineColor)
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.EAST, outlineColor)
}
if (!positions.contains(pos.add(-1, 0, 0))) {
RenderUtils.drawBlockFace(offsetBoundingBox, EnumFacing.WEST, outlineColor)
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.WEST, outlineColor)
}
if (!positions.contains(pos.add(0, 1, 0))) {
RenderUtils.drawBlockFace(offsetBoundingBox, EnumFacing.UP, outlineColor)
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.UP, outlineColor)
}
if (!positions.contains(pos.add(0, -1, 0))) {
RenderUtils.drawBlockFace(offsetBoundingBox, EnumFacing.DOWN, outlineColor)
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.DOWN, outlineColor)
}
if (!positions.contains(pos.add(0, 0, 1))) {
RenderUtils.drawBlockFace(offsetBoundingBox, EnumFacing.SOUTH, outlineColor)
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.SOUTH, outlineColor)
}
if (!positions.contains(pos.add(0, 0, -1))) {
RenderUtils.drawBlockFace(offsetBoundingBox, EnumFacing.NORTH, outlineColor)
RenderPrimitives.drawBlockFace(offsetBoundingBox, EnumFacing.NORTH, outlineColor)
}
}

View File

@ -1,7 +1,6 @@
package com.github.itzilly.sbt.renderer
package com.github.itzilly.sbt.render
import com.github.itzilly.sbt.util.Vecd3
import com.github.itzilly.sbt.data.Vecd3
import net.minecraft.client.Minecraft
import net.minecraft.client.renderer.GlStateManager
import net.minecraft.client.renderer.Tessellator
@ -12,7 +11,7 @@ import net.minecraft.util.EnumFacing
import org.lwjgl.opengl.GL11
import java.awt.Color
object RenderUtils {
object RenderPrimitives {
var TESSELLATOR: Tessellator = Tessellator.getInstance()
var WORLD_RENDERER: WorldRenderer = Tessellator.getInstance().worldRenderer

View File

@ -1,18 +0,0 @@
package com.github.itzilly.sbt.renderer
import net.minecraft.client.Minecraft
import net.minecraft.util.BlockPos
import net.minecraftforge.client.event.RenderWorldLastEvent
class RenderableBlockOutline(val x: Int, val y: Int, val z: Int, val outlineColor: Int) {
fun renderOutline(event: RenderWorldLastEvent) {
val player = Minecraft.getMinecraft().thePlayer
val pos = BlockPos(x, y, z)
RenderFuncs.drawBlockOutline(player, pos, event.partialTicks, outlineColor)
}
fun renderLine(event: RenderWorldLastEvent) {
val pos = BlockPos(x, y, z)
RenderFuncs.drawBlockLine(pos, event.partialTicks)
}
}

View File

@ -4,7 +4,6 @@ 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

View File

@ -1,22 +0,0 @@
package com.github.itzilly.sbt.util
import net.minecraft.util.IChatComponent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent
object MessageScheduler {
private val components: MutableList<IChatComponent> = mutableListOf()
@SubscribeEvent
fun onPlayerLoggedIn(event: PlayerLoggedInEvent) {
val player = event.player
for (component in components) {
Chatterbox.say(player, component)
}
components.clear() // Clear the list after sending the messages
}
fun sayWhenReady(component: IChatComponent) {
components.add(component)
}
}

View 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+]$")) }
}

View File

@ -1,4 +0,0 @@
package com.github.itzilly.sbt.util
data class Vecd3(val x:Double, val y:Double, val z:Double)
data class Vecf3(val x:Float, val y:Float, val z:Float)

View File

@ -1,6 +1,7 @@
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
key.addWaypoint=Add Route Point
key.finishWaypoints=Finish Route
key.clearWaypoints=Clear Route Points
key.openRoutesGui=Open Route GUI
key.xrayToggle=Toggle ChestESP

View File

@ -2,10 +2,10 @@
{
"modid": "${modid}",
"name": "SkyBlock Tweaks",
"description": "Utilities for making Hypixel Skyblock more bearable",
"description": "Simple tweaks for Hypixel Skyblock",
"version": "${version}",
"mcversion": "${mcversion}",
"url": "http://itzilly.com/illyum/illyum/sbt-stash/",
"url": "http://itzilly.com/illyum/SkyBlockTweaks",
"updateUrl": "",
"authorList": [
"itzilly"