Compare commits

..

26 Commits

Author SHA1 Message Date
1eb2e5ee6c No idea what this is 2025-04-22 00:15:58 -06:00
b6c4223da5 Update build 2025-04-22 00:15:04 -06:00
e10f66a0c0 Try to get things working 2025-04-22 00:12:59 -06:00
c3f5be60ed chore: add changelog gen settings 2025-04-09 13:25:14 -06:00
3373405b9c feat: add base for farmer feature 2025-04-09 13:24:43 -06:00
13e816401b chore: cleanup old settings from template 2025-04-09 13:24:11 -06:00
82c40c3047 build: auto increment jar build names 2025-04-09 13:23:41 -06:00
91d881392e Delete .kotlin/errors/errors-1721778737330.log 2024-08-23 07:34:52 -06:00
illyum
89dcc87487 docs: add CONTRIBUTING.md and developer notice 2024-08-23 07:34:18 -06:00
illyum
3ed286c0f9 (Chore): Version Bump to 1.4.4 2024-08-23 06:47:30 -06:00
illyum
ebfbd6fc74 (BugFix): Fix powder chest being registered while in opening animation 2024-08-23 06:47:05 -06:00
illyum
9749a3199a (BugFix): Recheck chest locations to remove ghost chest highlights 2024-08-22 13:13:08 -06:00
illyum
fab0790e58 (Chore): Bump version to 1.4.3 2024-08-22 12:54:24 -06:00
illyum
98eb315142 (BugFix): Fixes powder chest click not being detected correctly 2024-08-22 12:53:56 -06:00
illyum
ed04c31578 (Chore): Bump version 2024-08-19 19:06:19 -06:00
illyum
7aff986e3c (Chore): Update readme to explain config and commands 2024-08-19 19:05:49 -06:00
illyum
043aab18dd Merge #1 2024-08-19 19:03:32 -06:00
illyum
1db0e3b7a4 (Chore): Fix conflict? 2024-08-19 19:02:12 -06:00
illyum
5970d2d427 (FEAT): Add configuration and commands to gemstone finder and powder chestESP 2024-08-19 18:59:52 -06:00
illyum
3b8d715efb so try to group hitboxes but I'm doing something wront. this doesn't work 2024-08-12 16:36:50 -06:00
illyum
d7a5a2913e Make the gem finder mesh all hitboxes into 1 big hitbox 2024-08-12 16:24:47 -06:00
illyum
fbaab2536f ChestESP, Route Builder, Chatterbox, Gemstone Finder, Grotto Finder 2024-08-12 16:21:07 -06:00
illyum
6d9b943e2c Stashing Commit 2024-08-09 03:02:15 -06:00
illyum
01041fb645 chore: remove comments 2024-07-23 17:09:56 -06:00
illyum
3f5e7acc0f feat(chestesp): add initial implementation, halfway done
Adds ChestESP features, only toggles on. There is no off toggle, and
once a block is highlighted it will remain highlighted
2024-07-23 00:14:42 -06:00
illyum
f79942969a stash 2 2024-07-20 00:10:29 -06:00
41 changed files with 1417 additions and 852 deletions

10
CHANGELOG.md Normal file
View File

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

View File

@ -2,7 +2,15 @@
My QOL mod for Hypixel Skyblock My QOL mod for Hypixel Skyblock
## TODO (Asin these WILL be implemented): ## Includes:
- Add route builder history GUI/Command or something - Grotto Finder
- Make Route Builder export to Skytils/CrystalHollowWaypoints/Coleweight - Gemstone Finder
- Optimize Block Outline Rendering - 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]

View File

@ -18,7 +18,7 @@ val modid: String by project
val mixinGroup = "$baseGroup.mixin" val mixinGroup = "$baseGroup.mixin"
group = baseGroup group = baseGroup
version = "alpha_$versionBase-b$buildNumber" version = "$versionBase-b$buildNumber"
java { java {
toolchain.languageVersion.set(JavaLanguageVersion.of(8)) toolchain.languageVersion.set(JavaLanguageVersion.of(8))
@ -140,6 +140,31 @@ tasks.assemble {
dependsOn("incrementBuildNumber") 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") { tasks.register("incrementBuildNumber") {
doLast { doLast {
val propsFile = file("gradle.properties") val propsFile = file("gradle.properties")

36
cliff.toml Normal file
View File

@ -0,0 +1,36 @@
# 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

@ -1,131 +0,0 @@
# 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>")
}
}
}
```

33
docs/CONTRIBUTING.md Normal file
View File

@ -0,0 +1,33 @@
# 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.

55
docs/DEVELOPER_NOTICE.md Normal file
View File

@ -0,0 +1,55 @@
# 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 @@
#Tue Apr 22 09:20:13 MDT 2025 #Sat Apr 19 14:54:37 MDT 2025
versionBase=2.0.0 versionBase=1.5.0
loom.platform=forge loom.platform=forge
baseGroup=com.github.itzilly.sbt baseGroup=com.github.itzilly.sbt
mcVersion=1.8.9 mcVersion=1.8.9
org.gradle.jvmargs=-Xmx2g org.gradle.jvmargs=-Xmx2g
buildNumber=214 buildNumber=107
modid=sbt modid=sbt

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,48 @@
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

@ -1,26 +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 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

@ -0,0 +1,86 @@
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

@ -0,0 +1,59 @@
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,11 +1,12 @@
package com.github.itzilly.sbt package com.github.itzilly.sbt
import com.github.itzilly.sbt.features.ChestEsp
import com.github.itzilly.sbt.command.ClientCommands import com.github.itzilly.sbt.features.FarmHelper
import com.github.itzilly.sbt.command.ModCommandRegistry import com.github.itzilly.sbt.features.GemstoneFinder
import com.github.itzilly.sbt.command.registerBaseCommand
import com.github.itzilly.sbt.features.GrottoFinder import com.github.itzilly.sbt.features.GrottoFinder
import com.github.itzilly.sbt.features.RouteBuilder import com.github.itzilly.sbt.features.router.RouteBuilder
import com.github.itzilly.sbt.util.MessageScheduler
import net.minecraftforge.client.ClientCommandHandler
import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.common.config.Configuration import net.minecraftforge.common.config.Configuration
import net.minecraftforge.fml.common.Mod import net.minecraftforge.fml.common.Mod
@ -33,16 +34,41 @@ class SkyBlockTweaks {
@Mod.EventHandler @Mod.EventHandler
fun init(event: FMLInitializationEvent) { fun init(event: FMLInitializationEvent) {
Keybinds.register() STBKeyBinds.init()
registerEventListeners()
ModCommandRegistry.register(ClientCommands) MinecraftForge.EVENT_BUS.register(RouteBuilder)
registerBaseCommand() 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())
} }
private fun loadConfig() { private fun loadConfig() {
config.load() 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()) { if (config.hasChanged()) {
config.save() config.save()
} }
@ -54,9 +80,4 @@ class SkyBlockTweaks {
config.save() config.save()
} }
} }
private fun registerEventListeners() {
MinecraftForge.EVENT_BUS.register(RouteBuilder)
MinecraftForge.EVENT_BUS.register(GrottoFinder)
}
} }

View File

@ -1,10 +0,0 @@
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

@ -1,31 +0,0 @@
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

@ -1,48 +0,0 @@
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

@ -1,54 +0,0 @@
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

@ -1,66 +0,0 @@
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

@ -1,17 +0,0 @@
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

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

@ -0,0 +1,175 @@
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

@ -0,0 +1,105 @@
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

@ -0,0 +1,158 @@
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,82 +1,38 @@
package com.github.itzilly.sbt.features package com.github.itzilly.sbt.features
import com.github.itzilly.sbt.Keybinds import com.github.itzilly.sbt.renderer.RenderFuncs
import com.github.itzilly.sbt.render.BlockOutlineRenderer
import com.github.itzilly.sbt.util.Chatterbox import com.github.itzilly.sbt.util.Chatterbox
import com.github.itzilly.sbt.util.DelayedFunction import com.github.itzilly.sbt.util.MessageScheduler
import com.github.itzilly.sbt.util.SimpleChatMsg import com.github.itzilly.sbt.util.SimpleChatMsg
import com.github.itzilly.sbt.util.getMiniServerIdFromScoreboard
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.block.state.IBlockState
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.network.NetHandlerPlayClient
import net.minecraft.init.Blocks import net.minecraft.init.Blocks
import net.minecraft.util.BlockPos 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.World
import net.minecraft.world.chunk.Chunk import net.minecraft.world.chunk.Chunk
import net.minecraftforge.client.event.RenderWorldLastEvent import net.minecraftforge.client.event.RenderWorldLastEvent
import net.minecraftforge.event.entity.EntityJoinWorldEvent
import net.minecraftforge.event.world.ChunkEvent import net.minecraftforge.event.world.ChunkEvent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.InputEvent
import net.minecraftforge.fml.common.gameevent.TickEvent
object GrottoFinder { 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 targetBlock: Block = Blocks.stained_glass
private val targetMeta: Int = 2 // Magenta stained glass private val targetMeta: Int = 2 // Magenta stained glass
private var lastWorldLoadTime = 0L private var magentaGlassList: ArrayList<ChestNode> = ArrayList()
private var lastMiniServerId: String? = null
private var renderBlockList: ArrayList<BlockPos> = ArrayList() private val minBounds = BlockPos(201, 30, 201)
private var jasperCrystalsList: ArrayList<BlockPos> = ArrayList() private val maxBounds = BlockPos(824, 189, 824)
fun enableAutoDetect() { var isEnabled: Boolean = true
autoDetectEnabled = true
fun clear() {
magentaGlassList.clear()
} }
fun disableAutoDetect() { fun checkWorldForMagentaStainedGlass(world: World) {
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 (chunkX in minBounds.x / 16..maxBounds.x / 16) {
for (chunkZ in minBounds.z / 16..maxBounds.z / 16) { for (chunkZ in minBounds.z / 16..maxBounds.z / 16) {
val chunk = world.getChunkFromChunkCoords(chunkX, chunkZ) val chunk = world.getChunkFromChunkCoords(chunkX, chunkZ)
@ -95,10 +51,13 @@ object GrottoFinder {
val block = blockState.block val block = blockState.block
val meta = block.getMetaFromState(blockState) val meta = block.getMetaFromState(blockState)
if (block === targetBlock && meta == targetMeta) { if (block === Blocks.stained_glass && meta == 2) {
if (!magentaNodeExists(blockPos.x, blockPos.y, blockPos.z)) { if (!magentaNodeExists(blockPos.x, blockPos.y, blockPos.z)) {
renderBlockList.add(BlockPos(blockPos.x, blockPos.y, blockPos.z)) magentaGlassList.add(ChestNode(blockPos.x, blockPos.y, blockPos.z))
} }
println("Found magenta stained glass at $blockPos")
val msg = SimpleChatMsg("Found magenta stained glass at $blockPos").light_purple()
Chatterbox.say(msg)
} }
} }
} }
@ -106,233 +65,24 @@ object GrottoFinder {
} }
} }
} }
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 @SubscribeEvent
fun renderBlockOverlay(event: RenderWorldLastEvent) { fun renderBlockOverlay(event: RenderWorldLastEvent) {
val player = Minecraft.getMinecraft().thePlayer val player = Minecraft.getMinecraft().thePlayer
for (glass: BlockPos in renderBlockList) { for (glass: ChestNode in magentaGlassList) {
val pos = BlockPos(glass.x, glass.y, glass.z) val pos = BlockPos(glass.x, glass.y, glass.z)
BlockOutlineRenderer.drawBlockOutline(player, pos, event.partialTicks) RenderFuncs.drawBlockOutline(player, pos, event.partialTicks)
BlockOutlineRenderer.drawBlockLine(pos, event.partialTicks) RenderFuncs.drawBlockLine(pos, event.partialTicks)
} }
} }
private fun isInCrystalHollowsFromTab(): Boolean { private fun magentaNodeExists(x: Int, y: Int, z: Int): Boolean {
val infoList = Minecraft.getMinecraft().thePlayer?.sendQueue?.playerInfoMap ?: return false return magentaGlassList.any { it.x == x && it.y == y && it.z == z }
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

@ -0,0 +1,20 @@
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

@ -1,121 +0,0 @@
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

@ -0,0 +1,83 @@
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

@ -0,0 +1,199 @@
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

@ -0,0 +1,127 @@
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

@ -0,0 +1,28 @@
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

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

View File

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

View File

@ -0,0 +1,18 @@
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,6 +4,7 @@ import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.TickEvent import net.minecraftforge.fml.common.gameevent.TickEvent
class DelayedFunction(private val function: () -> Unit, private val delayTicks: Int) { class DelayedFunction(private val function: () -> Unit, private val delayTicks: Int) {
private var ticksLeft: Int = delayTicks private var ticksLeft: Int = delayTicks

View File

@ -0,0 +1,22 @@
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

@ -1,19 +0,0 @@
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

@ -0,0 +1,4 @@
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,7 +1,6 @@
key.searchGrotto=Search Grotto key.categories.sbt=SkyBlock Tweaks
key.clearGrotto=Clear Grotto key.addWaypoint=Add Route Point
key.addRouteNode=Add Route Node key.finishWaypoints=Finish Route
key.removeLastNode=Remove Last Node key.clearWaypoints=Clear Route Points
key.finishRoute=Finish Route key.openRoutesGui=Open Route GUI
key.xrayToggle=Toggle ChestESP
key.categories.sbt=SkyBlock Tweaks

View File

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