diff options
Diffstat (limited to 'src/main')
19 files changed, 576 insertions, 115 deletions
diff --git a/src/main/java/com/ambientaddons/mixin/MixinGuiContainer.java b/src/main/java/com/ambientaddons/mixin/MixinGuiContainer.java new file mode 100644 index 0000000..ee4b432 --- /dev/null +++ b/src/main/java/com/ambientaddons/mixin/MixinGuiContainer.java @@ -0,0 +1,37 @@ +package com.ambientaddons.mixin; + +import com.ambientaddons.events.GuiContainerEvent; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; +import net.minecraftforge.common.MinecraftForge; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +// credit Harry282/Skyblock-Client, under AGPL 3.0 +@Mixin(GuiContainer.class) +public class MixinGuiContainer extends GuiScreen { + private final GuiContainer gui = (GuiContainer) (Object) this; + + @Shadow + public Container inventorySlots; + + @Inject(method = "drawSlot", at = @At("HEAD"), cancellable = true) + private void onDrawSlot(Slot slot, CallbackInfo ci) { + if (MinecraftForge.EVENT_BUS.post(new GuiContainerEvent.DrawSlotEvent(inventorySlots, gui, slot))) { + ci.cancel(); + } + } + + @Inject(method = "handleMouseClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/PlayerControllerMP;windowClick(IIIILnet/minecraft/entity/player/EntityPlayer;)Lnet/minecraft/item/ItemStack;"), cancellable = true) + private void onMouseClick(Slot slot, int slotId, int clickedButton, int clickType, CallbackInfo ci) { + System.out.println("Slot click"); + if (MinecraftForge.EVENT_BUS.post(new GuiContainerEvent.SlotClickEvent(inventorySlots, gui, slot, slotId))) { + ci.cancel(); + } + } +} diff --git a/src/main/java/com/ambientaddons/mixin/MixinNetworkManager.java b/src/main/java/com/ambientaddons/mixin/MixinNetworkManager.java new file mode 100644 index 0000000..f13b41c --- /dev/null +++ b/src/main/java/com/ambientaddons/mixin/MixinNetworkManager.java @@ -0,0 +1,20 @@ +package com.ambientaddons.mixin; + +import com.ambientaddons.events.ReceivePacketEvent; +import io.netty.channel.ChannelHandlerContext; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.Packet; +import net.minecraftforge.common.MinecraftForge; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +// credit Harry282/Skyblock-Client, under AGPL 3.0 +@Mixin(NetworkManager.class) +public class MixinNetworkManager { + @Inject(method = "channelRead0*", at = @At("HEAD"), cancellable = true) + private void onReceivePacket(ChannelHandlerContext context, Packet<?> packet, CallbackInfo ci) { + if (MinecraftForge.EVENT_BUS.post(new ReceivePacketEvent(packet))) ci.cancel(); + } +} diff --git a/src/main/java/com/examplemod/mixin/MixinGuiMainMenu.java b/src/main/java/com/examplemod/mixin/MixinGuiMainMenu.java deleted file mode 100644 index 79c5db6..0000000 --- a/src/main/java/com/examplemod/mixin/MixinGuiMainMenu.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.examplemod.mixin; - -import net.minecraft.client.gui.GuiMainMenu; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(GuiMainMenu.class) -public class MixinGuiMainMenu { - - @Inject(method = "initGui", at = @At("HEAD")) - public void onInitGui(CallbackInfo ci) { - System.out.println("Hello from Main Menu!"); - } -} diff --git a/src/main/kotlin/com/examplemod/ExampleMod.kt b/src/main/kotlin/com/ambientaddons/AmbientAddons.kt index 50434a1..bf8e503 100644 --- a/src/main/kotlin/com/examplemod/ExampleMod.kt +++ b/src/main/kotlin/com/ambientaddons/AmbientAddons.kt @@ -1,6 +1,8 @@ -import com.examplemod.commands.ExampleCommand -import com.examplemod.config.Config -import com.examplemod.config.PersistentData +import com.ambientaddons.commands.AmbientCommand +import com.ambientaddons.config.Config +import com.ambientaddons.config.PersistentData +import com.ambientaddons.features.dungeon.AutoBuyChest +import com.ambientaddons.utils.LocationUtils import net.minecraft.client.Minecraft import net.minecraft.client.gui.GuiScreen import net.minecraftforge.client.ClientCommandHandler @@ -14,18 +16,18 @@ import net.minecraftforge.fml.common.gameevent.TickEvent import java.io.File @Mod( - modid = "examplemod", - name = "ExampleMod", - version = "1.0", + modid = "ambientaddons", + name = "AmbientAddons", + version = "0.1", useMetadata = true, clientSideOnly = true ) -class ExampleMod { +class AmbientAddons { @Mod.EventHandler fun preInit(event: FMLPreInitializationEvent) { metadata = event.modMetadata - val directory = File(event.modConfigurationDirectory, event.modMetadata.modId) + val directory = File(event.modConfigurationDirectory, "ambientaddons-forge") directory.mkdirs() configDirectory = directory persistentData = PersistentData.load() @@ -34,10 +36,12 @@ class ExampleMod { @Mod.EventHandler fun onInit(event: FMLInitializationEvent) { - ClientCommandHandler.instance.registerCommand(ExampleCommand()) + ClientCommandHandler.instance.registerCommand(AmbientCommand()) listOf( - this + this, + LocationUtils, + AutoBuyChest ).forEach(MinecraftForge.EVENT_BUS::register) } diff --git a/src/main/kotlin/com/ambientaddons/commands/AmbientCommand.kt b/src/main/kotlin/com/ambientaddons/commands/AmbientCommand.kt new file mode 100644 index 0000000..5bfd92b --- /dev/null +++ b/src/main/kotlin/com/ambientaddons/commands/AmbientCommand.kt @@ -0,0 +1,28 @@ +package com.ambientaddons.commands + +import AmbientAddons +import com.ambientaddons.config.Config +import com.ambientaddons.utils.Extensions.withModPrefix +import com.ambientaddons.utils.LocationUtils +import gg.essential.universal.UChat +import net.minecraft.command.CommandBase +import net.minecraft.command.ICommandSender + +class AmbientCommand : CommandBase() { + override fun getCommandName() = "ambientaddons" + + override fun getCommandAliases() = listOf("aa") + + override fun getCommandUsage(sender: ICommandSender?) = "/$commandName" + + override fun getRequiredPermissionLevel() = 0 + + override fun processCommand(sender: ICommandSender?, args: Array<String>) { + when (args.getOrNull(0)) { + null -> AmbientAddons.currentGui = Config.gui() + "location" -> UChat.chat(LocationUtils.toString().withModPrefix()) + "buy" -> AutoBuyCommand.processCommand(args.drop(1)) + else -> UChat.chat("§cUnknown argument!") + } + } +}
\ No newline at end of file diff --git a/src/main/kotlin/com/ambientaddons/commands/AutoBuyCommand.kt b/src/main/kotlin/com/ambientaddons/commands/AutoBuyCommand.kt new file mode 100644 index 0000000..c7ff9a7 --- /dev/null +++ b/src/main/kotlin/com/ambientaddons/commands/AutoBuyCommand.kt @@ -0,0 +1,43 @@ +package com.ambientaddons.commands + +import AmbientAddons.Companion.persistentData +import com.ambientaddons.utils.Extensions.withModPrefix +import gg.essential.universal.UChat + +object AutoBuyCommand { + fun processCommand(args: List<String>) { + when (args.getOrNull(0)) { + "add" -> { + val item = args[1] + val newPrice = args.getOrNull(2)?.toIntOrNull() + persistentData.autoBuyItems[item] = newPrice + persistentData.save() + UChat.chat("§aAdded item §a§l$item §awith ${if (newPrice == null) "no minimum price." else " minimum price §a§l$newPrice"}".withModPrefix()) + } + "remove" -> { + val item = args[1] + if (persistentData.autoBuyItems.contains(item)) { + persistentData.autoBuyItems.remove(item) + persistentData.save() + UChat.chat("§aRemoved item §a§l$item.".withModPrefix()) + } else UChat.chat("§cItem §a§l$item does not exist!".withModPrefix()) + } + "list" -> { + UChat.chat("§2§lItems".withModPrefix()) + persistentData.autoBuyItems.forEach { + if (it.value == null) { + UChat.chat(" §b${it.key}") + } else { + UChat.chat(" §b${it.key} §7(max price §a${it.value} §7coins)") + } + } + } + else -> { + UChat.chat("§2§lUsage".withModPrefix()) + UChat.chat(" §aAdd item: §b/ambient buy add <Skyblock ID> [max allowable price]") + UChat.chat(" §aRemove item: §b/ambient buy remove <Skyblock ID>") + UChat.chat(" §aList: §b/ambient buy list") + } + } + } +}
\ No newline at end of file diff --git a/src/main/kotlin/com/ambientaddons/config/Config.kt b/src/main/kotlin/com/ambientaddons/config/Config.kt new file mode 100644 index 0000000..b283abb --- /dev/null +++ b/src/main/kotlin/com/ambientaddons/config/Config.kt @@ -0,0 +1,36 @@ +package com.ambientaddons.config + +import gg.essential.vigilance.Vigilant +import java.awt.Color +import java.io.File + + +object Config : Vigilant( + File(AmbientAddons.configDirectory, "config.toml"), + AmbientAddons.metadata.name +) { + var blockLowReroll = false + var autoBuyChest = 0 + + init { + category("Pre/Post Dungeon") { + subcategory("Chest QOL") { + switch ( + ::blockLowReroll, + name = "Block rerolling low chests", + description = "Prevents rerolling non-Bedrock chests (or Obsidian on M4)." + ) + selector( + ::autoBuyChest, + name = "Dungeon Reward Chests", + description = "Either blocks rerolls or automatically buys dungeon reward chests containing certain items.", + options = listOf("Off", "Block Reroll", "Autobuy") + ) + } + + } + } + + + +}
\ No newline at end of file diff --git a/src/main/kotlin/com/examplemod/config/PersistentData.kt b/src/main/kotlin/com/ambientaddons/config/PersistentData.kt index 58e0ac0..87bb287 100644 --- a/src/main/kotlin/com/examplemod/config/PersistentData.kt +++ b/src/main/kotlin/com/ambientaddons/config/PersistentData.kt @@ -1,7 +1,6 @@ -package com.examplemod.config +package com.ambientaddons.config import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient import kotlinx.serialization.json.Json import java.io.File import kotlinx.serialization.encodeToString @@ -9,8 +8,22 @@ import kotlinx.serialization.decodeFromString @Serializable data class PersistentData( - var exampleData: Map<String, String> = mapOf("key1" to "entry1"), - var moreExampleData: Int = 5 + var autoBuyItems: MutableMap<String, Int?> = mutableMapOf( + "RECOMBOBULATOR_3000" to 5000000, + "FIRST_MASTER_STAR" to null, + "SECOND_MASTER_STAR" to null, + "THIRD_MASTER_STAR" to null, + "FOURTH_MASTER_STAR" to null, + "FIFTH_MASTER_STAR" to null, + "SPIRIT_WING" to null, + "SPIRIT_STONE" to 1000000, + "SHADOW_ASSASSIN_CHESTPLATE" to null, + "GIANTS_SWORD" to null, + "DARK_CLAYMORE" to null, + "THUNDERLORD_7" to null, + "WITHER_CHESTPLATE" to null, + "ULTIMATE_ONE_FOR_ALL_1" to null + ) ) { fun save() { @@ -18,7 +31,7 @@ data class PersistentData( } companion object { - private val configFile: File = File(ExampleMod.configDirectory,"data.json") + private val configFile: File = File(AmbientAddons.configDirectory,"data.json") fun load(): PersistentData { val data = if (!configFile.exists()) { diff --git a/src/main/kotlin/com/ambientaddons/events/GuiContainerEvent.kt b/src/main/kotlin/com/ambientaddons/events/GuiContainerEvent.kt new file mode 100644 index 0000000..276abc3 --- /dev/null +++ b/src/main/kotlin/com/ambientaddons/events/GuiContainerEvent.kt @@ -0,0 +1,17 @@ +package com.ambientaddons.events + +import net.minecraft.client.gui.inventory.GuiContainer +import net.minecraft.inventory.Container +import net.minecraft.inventory.Slot +import net.minecraftforge.fml.common.eventhandler.Cancelable +import net.minecraftforge.fml.common.eventhandler.Event + +// credit Harry282/Skyblock-Client, under AGPL 3.0 +open class GuiContainerEvent(val container: Container, val gui: GuiContainer) : Event() { + @Cancelable + class DrawSlotEvent(container: Container, gui: GuiContainer, var slot: Slot) : GuiContainerEvent(container, gui) + + @Cancelable + class SlotClickEvent(container: Container, gui: GuiContainer, var slot: Slot?, var slotId: Int) : + GuiContainerEvent(container, gui) +}
\ No newline at end of file diff --git a/src/main/kotlin/com/ambientaddons/events/ReceivePacketEvent.kt b/src/main/kotlin/com/ambientaddons/events/ReceivePacketEvent.kt new file mode 100644 index 0000000..d69fefe --- /dev/null +++ b/src/main/kotlin/com/ambientaddons/events/ReceivePacketEvent.kt @@ -0,0 +1,9 @@ +package com.ambientaddons.events + +import net.minecraft.network.Packet +import net.minecraftforge.fml.common.eventhandler.Cancelable +import net.minecraftforge.fml.common.eventhandler.Event + +// credit Harry282/Skyblock-Client, under AGPL 3.0 +@Cancelable +class ReceivePacketEvent(packet: Packet<*>) : Event()
\ No newline at end of file diff --git a/src/main/kotlin/com/ambientaddons/features/dungeon/AutoBuyChest.kt b/src/main/kotlin/com/ambientaddons/features/dungeon/AutoBuyChest.kt new file mode 100644 index 0000000..36f0b94 --- /dev/null +++ b/src/main/kotlin/com/ambientaddons/features/dungeon/AutoBuyChest.kt @@ -0,0 +1,114 @@ +package com.ambientaddons.features.dungeon + +import AmbientAddons.Companion.config +import AmbientAddons.Companion.mc +import AmbientAddons.Companion.persistentData +import com.ambientaddons.events.GuiContainerEvent +import com.ambientaddons.utils.Extensions.chest +import com.ambientaddons.utils.Extensions.enchants +import com.ambientaddons.utils.Extensions.items +import com.ambientaddons.utils.Extensions.lore +import com.ambientaddons.utils.Extensions.skyblockID +import com.ambientaddons.utils.Extensions.stripControlCodes +import com.ambientaddons.utils.Extensions.withModPrefix +import com.ambientaddons.utils.LocationUtils +import gg.essential.universal.UChat +import net.minecraft.item.ItemStack +import net.minecraftforge.client.event.GuiOpenEvent +import net.minecraftforge.client.event.GuiScreenEvent +import net.minecraftforge.event.world.WorldEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +object AutoBuyChest { + private const val BUY_SLOT_INDEX = 31 + private const val KISMET_SLOT_INDEX = 50 + private var rewardChest: RewardChest? = null + private var hasOpenedChest = false + private var hasLookedAtChest = false + + @SubscribeEvent + fun onWorldUnload(event: WorldEvent.Unload) { + hasOpenedChest = false + } + + @SubscribeEvent + fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) { + UChat.chat(event.slotId) + if (rewardChest == null) return + if (event.slotId == BUY_SLOT_INDEX) { + hasOpenedChest = true + } else if (event.slotId == KISMET_SLOT_INDEX) { + if (config.blockLowReroll && rewardChest != RewardChest.Bedrock && (rewardChest != RewardChest.Obsidian || LocationUtils.dungeonFloor.toString() != "M4")) { + UChat.chat("§cBlocked reroll! This low-tier chest should not be rerolled.".withModPrefix()) + event.isCanceled = true + return + } + if (config.autoBuyChest == 1) { + val items = event.gui.chest?.lowerChestInventory?.items ?: return + if (getShouldOpen(items)) { + UChat.chat("§cBlocked reroll! Profitable content in chest.".withModPrefix()) + event.isCanceled = true + } + } + } + } + + @SubscribeEvent + fun onGuiOpen(event: GuiOpenEvent) { + if (event.gui == null) return + val chest = event.gui.chest?.lowerChestInventory?.name + + rewardChest = when (chest) { + "Wood Chest" -> RewardChest.Wood + "Gold Chest" -> RewardChest.Gold + "Emerald Chest" -> RewardChest.Emerald + "Diamond Chest" -> RewardChest.Diamond + "Obsidian Chest" -> RewardChest.Obsidian + "Bedrock Chest" -> RewardChest.Bedrock + else -> null + } + + hasLookedAtChest = false + } + + @SubscribeEvent + fun onGuiDraw(event: GuiScreenEvent.DrawScreenEvent) { + if (config.autoBuyChest != 2 || rewardChest == null || hasLookedAtChest) return + if (rewardChest == RewardChest.Wood) return + val chest = event.gui?.chest ?: return + val items = chest.lowerChestInventory.items + if (items.last() != null) { + hasLookedAtChest = true + if (getShouldOpen(items)) { + mc.playerController.windowClick(chest.windowId, BUY_SLOT_INDEX, 0, 0, mc.thePlayer) + hasOpenedChest = true + mc.thePlayer.closeScreen() + } + } + } + + private fun getShouldOpen(items: List<ItemStack?>): Boolean { + val chestPrice = + items[BUY_SLOT_INDEX]?.lore?.getOrNull(6)?.stripControlCodes()?.filter { it.isDigit() }?.toIntOrNull() ?: 0 + + val lootItems = items.subList(9, 18).mapNotNull { itemStack -> + if (itemStack?.skyblockID == "ENCHANTED_BOOK") { + val enchants = itemStack.enchants + enchants?.entries?.singleOrNull()?.let { + "${it.key.uppercase()}_${it.value}" + } + } else itemStack?.skyblockID + } + + val maxPrice = lootItems.sumOf { + persistentData.autoBuyItems.getOrDefault(it, 0) ?: 100000000 + } + + return chestPrice <= maxPrice + } + + enum class RewardChest { + Wood, Gold, Emerald, Diamond, Obsidian, Bedrock + } + +}
\ No newline at end of file diff --git a/src/main/kotlin/com/ambientaddons/utils/DungeonFloor.kt b/src/main/kotlin/com/ambientaddons/utils/DungeonFloor.kt new file mode 100644 index 0000000..a64fed0 --- /dev/null +++ b/src/main/kotlin/com/ambientaddons/utils/DungeonFloor.kt @@ -0,0 +1,32 @@ +package com.ambientaddons.utils + +data class DungeonFloor( + val mode: Mode, + val floor: Int +) { + override fun toString(): String { + return when { + floor == 0 -> "E" + mode == Mode.Normal -> "F$floor" + else -> "M$floor" + } + } + + companion object { + fun String.toDungeonFloor(): DungeonFloor? { + if (this.isEmpty()) return null + if (this == "E") return DungeonFloor(Mode.Normal, 0) + val floorInt = this.last().digitToIntOrNull() ?: return null + if (floorInt !in 1..7) return null + return when (this.first()) { + 'F' -> DungeonFloor(Mode.Normal, floorInt) + 'M' -> DungeonFloor(Mode.Master, floorInt) + else -> null + } + } + } +} + +enum class Mode { Normal, Master } + + diff --git a/src/main/kotlin/com/ambientaddons/utils/Extensions.kt b/src/main/kotlin/com/ambientaddons/utils/Extensions.kt new file mode 100644 index 0000000..3da5767 --- /dev/null +++ b/src/main/kotlin/com/ambientaddons/utils/Extensions.kt @@ -0,0 +1,73 @@ +package com.ambientaddons.utils + +import com.ambientaddons.utils.Extensions.enchants +import com.ambientaddons.utils.Extensions.extraAttributes +import com.ambientaddons.utils.Extensions.skyblockID +import net.minecraft.client.gui.GuiScreen +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.inventory.ContainerChest +import net.minecraft.inventory.IInventory +import net.minecraft.item.Item +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.StringUtils + +object Extensions { + fun String.substringBetween(start: String, end: String): String { + return this + .substringAfter("(", "") + .substringBefore(")", "") + } + + fun String.stripControlCodes(): String { + return StringUtils.stripControlCodes(this) + } + + fun String.cleanSB(): String { + return StringUtils.stripControlCodes(this) + ?.toCharArray() + ?.filter { it.code in 21..126 } + ?.joinToString("") + ?: "" + } + + fun String.withModPrefix(): String { + return "§9§lAmbient §7» §r${this}" + } + + val GuiScreen.chest: ContainerChest? + get() = (this as? GuiChest)?.inventorySlots as? ContainerChest + + val IInventory.items: List<ItemStack?> + get() = (0 until this.sizeInventory).map { this.getStackInSlot(it) } + + + private val ItemStack.extraAttributes: NBTTagCompound? + get() { + if (!this.hasTagCompound()) return null + return this.getSubCompound("ExtraAttributes", false) ?: return null + } + + val ItemStack.skyblockID: String? + get() = this.extraAttributes?.let { + if (!it.hasKey("id", 8)) return null + return it.getString("id") + } + + val ItemStack.enchants: Map<String, Int>? + get() = this.extraAttributes?.let { extraAttributes -> + if (!extraAttributes.hasKey("enchantments", 10)) return null + val enchantments = extraAttributes.getCompoundTag("enchantments") + enchantments.keySet.associateWith { enchantments.getInteger(it) } + } + + val ItemStack.lore: List<String>? + get() { + if (!this.hasTagCompound()) return null + val displayTag = this.getSubCompound("display", false) ?: return null + if (!displayTag.hasKey("Lore", 9)) return null + val loreList = displayTag.getTagList("Lore", 8) + return (0 until loreList.tagCount()).map { loreList.getStringTagAt(it) } + } + +} diff --git a/src/main/kotlin/com/ambientaddons/utils/LocationUtils.kt b/src/main/kotlin/com/ambientaddons/utils/LocationUtils.kt new file mode 100644 index 0000000..d185a08 --- /dev/null +++ b/src/main/kotlin/com/ambientaddons/utils/LocationUtils.kt @@ -0,0 +1,87 @@ +package com.ambientaddons.utils + +import AmbientAddons.Companion.mc +import com.ambientaddons.utils.DungeonFloor.Companion.toDungeonFloor +import com.ambientaddons.utils.Extensions.cleanSB +import com.ambientaddons.utils.Extensions.stripControlCodes +import com.ambientaddons.utils.Extensions.substringBetween +import com.ambientaddons.utils.TabListUtils.fetchTabEntries +import net.minecraft.scoreboard.Score +import net.minecraft.scoreboard.ScorePlayerTeam +import net.minecraftforge.event.world.WorldEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import net.minecraftforge.fml.common.network.FMLNetworkEvent + +object LocationUtils { + private var areaRegex = Regex("^(?:Area|Dungeon): ([\\w ].+)\$") + private var onHypixel = false + var inSkyblock = false + var location: String? = null + var dungeonFloor: DungeonFloor? = null + var ticks = 0 + + @SubscribeEvent + fun onWorldUnload(event: WorldEvent.Unload) { + inSkyblock = false + dungeonFloor = null + location = null + } + + @SubscribeEvent + fun onConnect(event: FMLNetworkEvent.ClientConnectedToServerEvent) { + onHypixel = mc.runCatching { + !event.isLocal && ((thePlayer?.clientBrand?.lowercase()?.contains("hypixel") + ?: currentServerData?.serverIP?.lowercase()?.contains("hypixel")) == true) + }.getOrDefault(false) + } + + @SubscribeEvent + fun onDisconnect(event: FMLNetworkEvent.ClientDisconnectionFromServerEvent) { + onHypixel = false + } + + // from Skytils, under AGPL 3.0 + fun fetchScoreboardLines(): List<String> { + val scoreboard = mc.theWorld?.scoreboard ?: return emptyList() + val objective = scoreboard.getObjectiveInDisplaySlot(1) ?: return emptyList() + val scores = scoreboard.getSortedScores(objective).filter { input: Score? -> + input != null && input.playerName != null && !input.playerName + .startsWith("#") + }.take(15) + return scores.map { + ScorePlayerTeam.formatPlayerName(scoreboard.getPlayersTeam(it.playerName), it.playerName).cleanSB() + }.asReversed() + } + + // modified from Harry282/Skyblock-Client, under AGPL 3.0 + @SubscribeEvent + fun onTick(event: TickEvent.ClientTickEvent) { + if (!onHypixel || event.phase != TickEvent.Phase.START) return + if (ticks % 10 == 0) { + val title = mc.theWorld?.scoreboard?.getObjectiveInDisplaySlot(1)?.displayName?.cleanSB() + if (!inSkyblock) { + inSkyblock = title?.contains("SKYBLOCK") == true + } + if (inSkyblock) { + if (location == null) { + val tab = fetchTabEntries() + location = tab.firstNotNullOfOrNull { areaRegex.find(it.text.stripControlCodes()) }?.let { + it.groupValues.getOrNull(1) + } + } + if (location == "Catacombs" && dungeonFloor == null) { + val dungeonLine = fetchScoreboardLines().find { + it.run { contains("The Catacombs (") && !contains("Queue") } + } + dungeonFloor = dungeonLine?.substringBetween("(", ")")?.toDungeonFloor() + } + } + } + ticks++ + } + + override fun toString(): String = + "onHypixel: $onHypixel, inSkyblock: $inSkyblock, location: $location, floor: $dungeonFloor" + +}
\ No newline at end of file diff --git a/src/main/kotlin/com/ambientaddons/utils/TabListUtils.kt b/src/main/kotlin/com/ambientaddons/utils/TabListUtils.kt new file mode 100644 index 0000000..6fd7fef --- /dev/null +++ b/src/main/kotlin/com/ambientaddons/utils/TabListUtils.kt @@ -0,0 +1,38 @@ +package com.ambientaddons.utils + +import AmbientAddons.Companion.mc +import com.google.common.collect.ComparisonChain +import com.google.common.collect.Ordering +import net.minecraft.client.network.NetworkPlayerInfo +import net.minecraft.world.WorldSettings + +// From SkytilsMod/Skytils, under AGPL 3.0 +val NetworkPlayerInfo.text: String + get() = mc.ingameGUI.tabList.getPlayerName(this) + +object TabListUtils { + private val playerInfoOrdering = object : Ordering<NetworkPlayerInfo>() { + override fun compare(p_compare_1_: NetworkPlayerInfo?, p_compare_2_: NetworkPlayerInfo?): Int { + val scorePlayerTeam = p_compare_1_?.playerTeam + val scorePlayerTeam1 = p_compare_2_?.playerTeam + if (p_compare_1_ != null) { + if (p_compare_2_ != null) { + return ComparisonChain.start().compareTrueFirst( + p_compare_1_.gameType != WorldSettings.GameType.SPECTATOR, + p_compare_2_.gameType != WorldSettings.GameType.SPECTATOR + ).compare( + if (scorePlayerTeam != null) scorePlayerTeam.registeredName else "", + if (scorePlayerTeam1 != null) scorePlayerTeam1.registeredName else "" + ).compare(p_compare_1_.gameProfile.name, p_compare_2_.gameProfile.name).result() + } + return 0 + } + return -1 + } + } + + fun fetchTabEntries(): List<NetworkPlayerInfo> = + if (mc.thePlayer == null) emptyList() else playerInfoOrdering.sortedCopy( + mc.thePlayer.sendQueue.playerInfoMap + ) +}
\ No newline at end of file diff --git a/src/main/kotlin/com/examplemod/commands/ExampleCommand.kt b/src/main/kotlin/com/examplemod/commands/ExampleCommand.kt deleted file mode 100644 index 12538ca..0000000 --- a/src/main/kotlin/com/examplemod/commands/ExampleCommand.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.examplemod.commands - -import ExampleMod -import com.examplemod.config.Config -import net.minecraft.command.CommandBase -import net.minecraft.command.ICommandSender -import net.minecraft.util.ChatComponentText -import net.minecraft.util.IChatComponent - -class ExampleCommand : CommandBase() { - override fun getCommandName() = "examplemod" - - override fun getCommandAliases() = listOf("example") - - override fun getCommandUsage(sender: ICommandSender?) = "/$commandName" - - override fun getRequiredPermissionLevel() = 0 - - override fun processCommand(sender: ICommandSender?, args: Array<out String>?) { - sender?.addChatMessage(ChatComponentText("Example command run!")) - ExampleMod.currentGui = Config.gui() - } -}
\ No newline at end of file diff --git a/src/main/kotlin/com/examplemod/config/Config.kt b/src/main/kotlin/com/examplemod/config/Config.kt deleted file mode 100644 index 3008de3..0000000 --- a/src/main/kotlin/com/examplemod/config/Config.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.examplemod.config - -import gg.essential.vigilance.Vigilant -import java.awt.Color -import java.io.File - - -object Config : Vigilant( - File(ExampleMod.configDirectory, "config.toml"), - ExampleMod.metadata.name -) { - var demoSwitch = false - var demoSelector = 0 - var demoColor : Color = Color.WHITE - var demoText = "" - - init { - category("One category") { - switch( - ::demoSwitch, - name = "Switch", - description = "This is a switch" - ) - - subcategory("An additional category") { - selector( - ::demoSelector, - name = "Selector", - description = "This is a selector", - options = listOf("Option 1", "Option 2", "Option 3") - ) - color( - ::demoColor, - name = "Color", - description = "This sets a color" - ) - } - } - - category("Another category") { - text( - ::demoText, - name = "Text", - description = "This is text", - placeholder = "This is some placeholder text." - ) - - } - } - - -}
\ No newline at end of file diff --git a/src/main/resources/mixins.ambientaddons.json b/src/main/resources/mixins.ambientaddons.json new file mode 100644 index 0000000..235c713 --- /dev/null +++ b/src/main/resources/mixins.ambientaddons.json @@ -0,0 +1,10 @@ +{ + "package": "com.ambientaddons.mixin", + "refmap": "mixins.ambientaddons.refmap.json", + "minVersion": "0.7", + "compatibilityLevel": "JAVA_8", + "client": [ + "MixinGuiContainer", + "MixinNetworkManager" + ] +} diff --git a/src/main/resources/mixins.examplemod.json b/src/main/resources/mixins.examplemod.json deleted file mode 100644 index 1d28cda..0000000 --- a/src/main/resources/mixins.examplemod.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "package": "com.examplemod.mixin", - "refmap": "mixins.examplemod.refmap.json", - "minVersion": "0.7", - "compatibilityLevel": "JAVA_8", - "client": [ - "MixinGuiMainMenu" - ] -} |