summaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/utils
diff options
context:
space:
mode:
authorLorenz <ESs95s3P5z8Pheb>2022-07-14 12:06:07 +0200
committerLorenz <ESs95s3P5z8Pheb>2022-07-14 12:06:07 +0200
commita5c540d977a3510812cac7fac340fe17e7d10983 (patch)
treedbbe5b208e6871378a10868d1206d1d78beeb950 /src/main/java/at/hannibal2/skyhanni/utils
parentd6c99ed30a2b1cb228b2fdc3d3178cf1f369dc53 (diff)
downloadskyhanni-a5c540d977a3510812cac7fac340fe17e7d10983.tar.gz
skyhanni-a5c540d977a3510812cac7fac340fe17e7d10983.tar.bz2
skyhanni-a5c540d977a3510812cac7fac340fe17e7d10983.zip
renamed mod to SkyHanni
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/utils')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt48
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/BlockUtils.kt23
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/GuiRender.kt32
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/ItemUtil.kt213
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt72
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzColor.kt27
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzDebug.kt16
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzLogger.kt70
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt113
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt61
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt152
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt355
12 files changed, 1182 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt
new file mode 100644
index 000000000..c321cf2d5
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt
@@ -0,0 +1,48 @@
+package at.hannibal2.skyhanni.utils
+
+import com.google.gson.JsonObject
+import com.google.gson.JsonParser
+import org.apache.http.client.config.RequestConfig
+import org.apache.http.client.methods.HttpGet
+import org.apache.http.impl.client.HttpClientBuilder
+import org.apache.http.impl.client.HttpClients
+import org.apache.http.message.BasicHeader
+import org.apache.http.util.EntityUtils
+
+
+object APIUtil {
+ private val parser = JsonParser()
+
+ val builder: HttpClientBuilder =
+ HttpClients.custom().setUserAgent("SkyHanni")
+ .setDefaultHeaders(
+ mutableListOf(
+ BasicHeader("Pragma", "no-cache"),
+ BasicHeader("Cache-Control", "no-cache")
+ )
+ )
+ .setDefaultRequestConfig(
+ RequestConfig.custom()
+ .build()
+ )
+ .useSystemProperties()
+
+ fun getJSONResponse(urlString: String): JsonObject {
+ val client = builder.build()
+ try {
+ client.execute(HttpGet(urlString)).use { response ->
+ val entity = response.entity
+ if (entity != null) {
+ val retSrc = EntityUtils.toString(entity)
+ return parser.parse(retSrc) as JsonObject
+ }
+ }
+ } catch (ex: Throwable) {
+ ex.printStackTrace()
+ LorenzUtils.error("SkyHanni ran into an ${ex::class.simpleName ?: "error"} whilst fetching a resource. See logs for more details.")
+ } finally {
+ client.close()
+ }
+ return JsonObject()
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/BlockUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/BlockUtils.kt
new file mode 100644
index 000000000..368b06daf
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/BlockUtils.kt
@@ -0,0 +1,23 @@
+package at.hannibal2.skyhanni.utils
+
+import net.minecraft.block.Block
+import net.minecraft.client.Minecraft
+import net.minecraft.tileentity.TileEntitySkull
+import net.minecraft.util.BlockPos
+import net.minecraftforge.common.util.Constants
+
+object BlockUtils {
+
+ fun LorenzVec.getBlockAt(): Block =
+ Minecraft.getMinecraft().theWorld.getBlockState(toBlocPos()).block
+
+ fun LorenzVec.isInLoadedChunk(): Boolean =
+ Minecraft.getMinecraft().theWorld.chunkProvider.provideChunk(toBlocPos()).isLoaded
+
+ fun getSkinFromSkull(position: BlockPos?): String? {
+ val entity = Minecraft.getMinecraft().theWorld.getTileEntity(position) as TileEntitySkull
+ val serializeNBT = entity.serializeNBT()
+ return serializeNBT.getCompoundTag("Owner").getCompoundTag("Properties")
+ .getTagList("textures", Constants.NBT.TAG_COMPOUND).getCompoundTagAt(0).getString("Value")
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/GuiRender.kt b/src/main/java/at/hannibal2/skyhanni/utils/GuiRender.kt
new file mode 100644
index 000000000..221cbd9a2
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/GuiRender.kt
@@ -0,0 +1,32 @@
+package at.hannibal2.skyhanni.utils
+
+import at.hannibal2.skyhanni.config.core.config.Position
+import at.hannibal2.skyhanni.utils.LorenzUtils.removeColorCodes
+import net.minecraft.client.Minecraft
+import net.minecraft.client.gui.ScaledResolution
+import net.minecraft.client.renderer.GlStateManager
+
+object GuiRender {
+
+ fun Position.renderString(string: String) {
+ val textToRender = "§f$string"
+
+ GlStateManager.pushMatrix()
+ val resolution = ScaledResolution(Minecraft.getMinecraft())
+
+ val renderer = Minecraft.getMinecraft().renderManager.fontRenderer
+
+ val offsetX = (200 - renderer.getStringWidth(textToRender.removeColorCodes())) / 2
+
+ val x = getAbsX(resolution, 200) + offsetX
+ val y = getAbsY(resolution, 16)
+
+
+
+ GlStateManager.translate(x + 1.0, y + 1.0, 0.0)
+ renderer.drawStringWithShadow(textToRender, 0f, 0f, 0)
+
+
+ GlStateManager.popMatrix()
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtil.kt
new file mode 100644
index 000000000..d10adc06c
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtil.kt
@@ -0,0 +1,213 @@
+package at.hannibal2.skyhanni.utils
+
+import net.minecraft.init.Items
+import net.minecraft.item.ItemStack
+import net.minecraft.nbt.NBTTagCompound
+import net.minecraft.nbt.NBTTagList
+import net.minecraft.nbt.NBTTagString
+import net.minecraftforge.common.util.Constants
+import java.util.*
+
+object ItemUtil {
+ private val PET_PATTERN = "§7\\[Lvl \\d+] (?<color>§[0-9a-fk-or]).+".toRegex()
+ const val NBT_INTEGER = 3
+ private const val NBT_STRING = 8
+ private const val NBT_LIST = 9
+ private const val NBT_COMPOUND = 10
+
+ /**
+ * Returns the display name of a given item
+ * @author Mojang
+ * @param item the Item to get the display name of
+ * @return the display name of the item
+ */
+ @JvmStatic
+ fun getDisplayName(item: ItemStack): String {
+ var s = item.item.getItemStackDisplayName(item)
+ if (item.tagCompound != null && item.tagCompound.hasKey("display", 10)) {
+ val nbtTagCompound = item.tagCompound.getCompoundTag("display")
+ if (nbtTagCompound.hasKey("Name", 8)) {
+ s = nbtTagCompound.getString("Name")
+ }
+ }
+ return s
+ }
+
+ /**
+ * Returns the Skyblock Item ID of a given Skyblock item
+ *
+ * @author BiscuitDevelopment
+ * @param item the Skyblock item to check
+ * @return the Skyblock Item ID of this item or `null` if this isn't a valid Skyblock item
+ */
+ @JvmStatic
+ fun getSkyBlockItemID(item: ItemStack?): String? {
+ if (item == null) {
+ return null
+ }
+ val extraAttributes = getExtraAttributes(item) ?: return null
+ return if (!extraAttributes.hasKey("id", NBT_STRING)) {
+ null
+ } else extraAttributes.getString("id")
+ }
+
+ /**
+ * Returns the `ExtraAttributes` compound tag from the item's NBT data.
+ *
+ * @author BiscuitDevelopment
+ * @param item the item to get the tag from
+ * @return the item's `ExtraAttributes` compound tag or `null` if the item doesn't have one
+ */
+ @JvmStatic
+ fun getExtraAttributes(item: ItemStack?): NBTTagCompound? {
+ return if (item == null || !item.hasTagCompound()) {
+ null
+ } else item.getSubCompound("ExtraAttributes", false)
+ }
+
+ /**
+ * Returns the Skyblock Item ID of a given Skyblock Extra Attributes NBT Compound
+ *
+ * @author BiscuitDevelopment
+ * @param extraAttributes the NBT to check
+ * @return the Skyblock Item ID of this item or `null` if this isn't a valid Skyblock NBT
+ */
+ @JvmStatic
+ fun getSkyBlockItemID(extraAttributes: NBTTagCompound?): String? {
+ if (extraAttributes != null) {
+ val itemId = extraAttributes.getString("id")
+ if (itemId.isNotEmpty()) {
+ return itemId
+ }
+ }
+ return null
+ }
+
+ /**
+ * Returns a string list containing the nbt lore of an ItemStack, or
+ * an empty list if this item doesn't have a lore. The returned lore
+ * list is unmodifiable since it has been converted from an NBTTagList.
+ *
+ * @author BiscuitDevelopment
+ * @param itemStack the ItemStack to get the lore from
+ * @return the lore of an ItemStack as a string list
+ */
+ @JvmStatic
+ fun getItemLore(itemStack: ItemStack): List<String> {
+ if (itemStack.hasTagCompound() && itemStack.tagCompound.hasKey("display", NBT_COMPOUND)) {
+ val display = itemStack.tagCompound.getCompoundTag("display")
+ if (display.hasKey("Lore", NBT_LIST)) {
+ val lore = display.getTagList("Lore", NBT_STRING)
+ val loreAsList = ArrayList<String>(lore.tagCount())
+ for (lineNumber in 0 until lore.tagCount()) {
+ loreAsList.add(lore.getStringTagAt(lineNumber))
+ }
+ return Collections.unmodifiableList(loreAsList)
+ }
+ }
+ return emptyList()
+ }
+
+// @JvmStatic
+// fun hasRightClickAbility(itemStack: ItemStack): Boolean {
+// for (line in getItemLore(itemStack)) {
+// val stripped = line.stripControlCodes()
+// if (stripped.startsWith("Item Ability:") && stripped.endsWith("RIGHT CLICK")) return true
+// }
+// return false
+// }
+
+// /**
+// * Returns the rarity of a given Skyblock item
+// * Modified
+// * @author BiscuitDevelopment
+// * @param item the Skyblock item to check
+// * @return the rarity of the item if a valid rarity is found, `null` if no rarity is found, `null` if item is `null`
+// */
+// fun getRarity(item: ItemStack?): ItemRarity {
+// if (item == null || !item.hasTagCompound()) {
+// return ItemRarity.NONE
+// }
+// val display = item.getSubCompound("display", false)
+// if (display == null || !display.hasKey("Lore")) {
+// return ItemRarity.NONE
+// }
+// val lore = display.getTagList("Lore", Constants.NBT.TAG_STRING)
+// val name = display.getString("Name")
+//
+// // Determine the item's rarity
+// for (i in (lore.tagCount() - 1) downTo 0) {
+// val currentLine = lore.getStringTagAt(i)
+// val rarityMatcher = RARITY_PATTERN.find(currentLine)
+// if (rarityMatcher != null) {
+// val rarity = rarityMatcher.groups["rarity"]?.value ?: continue
+// ItemRarity.values().find {
+// it.rarityName == rarity.stripControlCodes().substringAfter("SHINY ")
+// }?.let {
+// return it
+// }
+// }
+// }
+// val petRarityMatcher = PET_PATTERN.find(name)
+// if (petRarityMatcher != null) {
+// val color = petRarityMatcher.groupValues.getOrNull(1) ?: return ItemRarity.NONE
+// return ItemRarity.byBaseColor(color) ?: ItemRarity.NONE
+// }
+//
+// // If the item doesn't have a valid rarity, return null
+// return ItemRarity.NONE
+// }
+
+ fun isPet(item: ItemStack?): Boolean {
+ if (item == null || !item.hasTagCompound()) {
+ return false
+ }
+ val display = item.getSubCompound("display", false)
+ if (display == null || !display.hasKey("Lore")) {
+ return false
+ }
+ val name = display.getString("Name")
+
+ return PET_PATTERN.matches(name)
+ }
+
+ fun setSkullTexture(item: ItemStack, texture: String, SkullOwner: String): ItemStack {
+ val textureTagCompound = NBTTagCompound()
+ textureTagCompound.setString("Value", texture)
+
+ val textures = NBTTagList()
+ textures.appendTag(textureTagCompound)
+
+ val properties = NBTTagCompound()
+ properties.setTag("textures", textures)
+
+ val skullOwner = NBTTagCompound()
+ skullOwner.setString("Id", SkullOwner)
+ skullOwner.setTag("Properties", properties)
+
+ val nbtTag = NBTTagCompound()
+ nbtTag.setTag("SkullOwner", skullOwner)
+
+ item.tagCompound = nbtTag
+ return item
+ }
+
+ fun getSkullTexture(item: ItemStack): String? {
+ if (item.item != Items.skull) return null
+ val nbt = item.tagCompound
+ if (!nbt.hasKey("SkullOwner")) return null
+ return nbt.getCompoundTag("SkullOwner").getCompoundTag("Properties")
+ .getTagList("textures", Constants.NBT.TAG_COMPOUND).getCompoundTagAt(0).getString("Value")
+ }
+
+ fun ItemStack.setLore(lines: List<String>): ItemStack {
+ setTagInfo("display", getSubCompound("display", true).apply {
+ setTag("Lore", NBTTagList().apply {
+ for (line in lines) appendTag(NBTTagString(line))
+ })
+ })
+ return this
+ }
+
+ fun NBTTagList.asStringSet() = (0..tagCount()).mapTo(hashSetOf()) { getStringTagAt(it) }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt
new file mode 100644
index 000000000..b077c4121
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt
@@ -0,0 +1,72 @@
+package at.hannibal2.skyhanni.utils
+
+import at.hannibal2.skyhanni.utils.LorenzUtils.matchRegex
+import at.hannibal2.skyhanni.utils.LorenzUtils.removeColorCodes
+import net.minecraft.client.Minecraft
+import net.minecraft.client.gui.inventory.GuiChest
+import net.minecraft.item.ItemStack
+
+object ItemUtils {
+
+ fun ItemStack.cleanName() = this.displayName.removeColorCodes()
+
+ fun getItemsInOpenChest(): List<ItemStack> {
+ val list = mutableListOf<ItemStack>()
+ val guiChest = Minecraft.getMinecraft().currentScreen as GuiChest
+ val inventorySlots = guiChest.inventorySlots.inventorySlots
+ val skipAt = inventorySlots.size - 9 * 4
+ var i = 0
+ for (slot in inventorySlots) {
+ val stack = slot.stack
+ if (stack != null) {
+ list.add(stack)
+ }
+ i++
+ if (i == skipAt) break
+ }
+ return list
+ }
+
+ fun isSack(name: String): Boolean = name.endsWith(" Sack")
+
+ fun ItemStack.getLore() = ItemUtil.getItemLore(this)
+
+ fun isCoOpSoulBound(stack: ItemStack): Boolean = stack.getLore().any { it.contains("Co-op Soulbound") }
+
+ fun isRecombobulated(stack: ItemStack): Boolean = stack.getLore().any { it.contains("§k") }
+
+ fun isPet(name: String): Boolean = name.matchRegex("\\[Lvl (.*)] (.*)") && !listOf(
+ "Archer",
+ "Berserk",
+ "Mage",
+ "Tank",
+ "Healer",
+ "➡",
+ ).any { name.contains(it) }
+
+ fun maxPetLevel(name: String) = if (name.contains("Golden Dragon")) 200 else 100
+
+ fun getItemsInInventoryWithSlots(withCursorItem: Boolean = false): Map<ItemStack, Int> {
+ val map: LinkedHashMap<ItemStack, Int> = LinkedHashMap()
+ val player = Minecraft.getMinecraft().thePlayer
+ if (player == null) {
+ LorenzUtils.warning("getItemsInInventoryWithSlots: player is null!")
+ return map
+ }
+ for (slot in player.openContainer.inventorySlots) {
+ if (slot.hasStack) {
+ map[slot.stack] = slot.slotNumber
+ }
+ }
+
+ if (withCursorItem) {
+ if (player.inventory != null) {
+ if (player.inventory.itemStack != null) {
+ map[player.inventory.itemStack] = -1
+ }
+ }
+ }
+
+ return map
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzColor.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzColor.kt
new file mode 100644
index 000000000..961ed57ec
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzColor.kt
@@ -0,0 +1,27 @@
+package at.hannibal2.skyhanni.utils
+
+import java.awt.Color
+
+enum class LorenzColor(private var chatColorCode: Char, private val color: Color) {
+ BLACK('0', Color(0, 0, 0)),
+ DARK_BLUE('1', Color(0, 0, 170)),
+ DARK_GREEN('2', Color(0, 170, 0)),
+ DARK_AQUA('3', Color(0, 170, 170)),
+ DARK_RED('4', Color(170, 0, 0)),
+ DARK_PURPLE('5', Color(170, 0, 170)),
+ GOLD('6', Color(255, 170, 0)),
+ GRAY('7', Color(170, 170, 170)),
+ DARK_GRAY('8', Color(85, 85, 85)),
+ BLUE('9', Color(85, 85, 255)),
+ GREEN('a', Color(85, 255, 85)),
+ AQUA('b', Color(85, 255, 255)),
+ RED('c', Color(255, 85, 85)),
+ LIGHT_PURPLE('d', Color(255, 85, 255)),
+ YELLOW('e', Color(255, 255, 85)),
+ WHITE('f', Color(255, 255, 255)),
+ ;
+
+ fun getChatColor(): String = "§$chatColorCode"
+
+ fun toColor(): Color = color
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzDebug.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzDebug.kt
new file mode 100644
index 000000000..5212dfc95
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzDebug.kt
@@ -0,0 +1,16 @@
+package at.hannibal2.skyhanni.utils
+
+object LorenzDebug {
+
+ private val logger = LorenzLogger("debug")
+
+ fun log(text: String) {
+ logger.log(text)
+ println("debug logger: $text")
+ }
+
+ fun writeAndLog(text: String) {
+ LorenzUtils.debug(text)
+ log(text)
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzLogger.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzLogger.kt
new file mode 100644
index 000000000..23ca8df25
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzLogger.kt
@@ -0,0 +1,70 @@
+package at.hannibal2.skyhanni.utils
+
+import at.hannibal2.skyhanni.utils.LorenzUtils.formatCurrentTime
+import java.io.File
+import java.io.IOException
+import java.text.SimpleDateFormat
+import java.util.logging.FileHandler
+import java.util.logging.Formatter
+import java.util.logging.LogRecord
+import java.util.logging.Logger
+
+class LorenzLogger(filePath: String) {
+ private val format = SimpleDateFormat("HH:mm:ss")
+ private val fileName = "$PREFIX_PATH$filePath.log"
+
+ companion object {
+ private var PREFIX_PATH: String
+
+ init {
+ val format = SimpleDateFormat("yyyy_MM_dd/HH_mm_ss").formatCurrentTime()
+ PREFIX_PATH = "mods/SkyHanni/logs/$format/"
+ }
+ }
+
+ private lateinit var logger: Logger
+
+ private fun getLogger(): Logger {
+ if (::logger.isInitialized) {
+ return logger
+ }
+
+ val initLogger = initLogger()
+ this.logger = initLogger
+ return initLogger
+ }
+
+ private fun initLogger(): Logger {
+ val logger = Logger.getLogger("" + System.nanoTime())
+ try {
+ createParent(File(fileName))
+ val handler = FileHandler(fileName)
+ handler.encoding ="utf-8"
+ logger.addHandler(handler)
+ handler.formatter = object : Formatter() {
+ override fun format(logRecord: LogRecord): String {
+ val message = logRecord.message
+ return format.formatCurrentTime() + " $message\n"
+ }
+ }
+ } catch (e: SecurityException) {
+ e.printStackTrace()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ return logger
+ }
+
+ private fun createParent(file: File) {
+ val parent = file.parentFile
+ if (parent != null) {
+ if (!parent.isDirectory) {
+ parent.mkdirs()
+ }
+ }
+ }
+
+ fun log(text: String?) {
+ getLogger().info(text)
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
new file mode 100644
index 000000000..13afe8ac2
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
@@ -0,0 +1,113 @@
+package at.hannibal2.skyhanni.utils
+
+import at.hannibal2.skyhanni.misc.HypixelData
+import net.minecraft.client.Minecraft
+import net.minecraft.entity.EntityLivingBase
+import net.minecraft.entity.SharedMonsterAttributes
+import net.minecraft.util.ChatComponentText
+import org.intellij.lang.annotations.Language
+import java.text.DecimalFormat
+import java.text.SimpleDateFormat
+
+object LorenzUtils {
+
+ val isOnHypixel: Boolean
+ get() = HypixelData.hypixel
+
+ val inSkyblock: Boolean
+ get() = HypixelData.hypixel && HypixelData.skyblock
+
+ val inDungeons: Boolean
+ get() = HypixelData.hypixel && HypixelData.skyblock && HypixelData.dungeon
+
+ const val DEBUG_PREFIX = "[Debug] §7"
+
+ fun debug(message: String) {
+ internalChat(DEBUG_PREFIX + message)
+ }
+
+ fun warning(message: String) {
+ internalChat("§cWarning! $message")
+ }
+
+ fun error(message: String) {
+ internalChat("§4$message")
+ }
+
+ fun chat(message: String) {
+ internalChat(message)
+ }
+
+ private fun internalChat(message: String) {
+ val minecraft = Minecraft.getMinecraft()
+ if (minecraft == null) {
+ println(message)
+ return
+ }
+
+ val thePlayer = minecraft.thePlayer
+ if (thePlayer == null) {
+ println(message)
+ return
+ }
+
+ thePlayer.addChatMessage(ChatComponentText(message))
+ }
+
+ fun String.matchRegex(@Language("RegExp") regex: String): Boolean = regex.toRegex().matches(this)
+
+ fun String.removeColorCodes(): String {
+ val builder = StringBuilder()
+ var skipNext = false
+ for (c in this.toCharArray()) {
+ if (c == '§') {
+ skipNext = true
+ continue
+ }
+ if (skipNext) {
+ skipNext = false
+ continue
+ }
+ builder.append(c)
+ }
+
+ return builder.toString()
+ }
+
+ fun SimpleDateFormat.formatCurrentTime(): String = this.format(System.currentTimeMillis())
+
+ fun stripVanillaMessage(originalMessage: String): String {
+ var message = originalMessage
+
+ while (message.startsWith("§r")) {
+ message = message.substring(2)
+ }
+ while (message.endsWith("§r")) {
+ message = message.substring(0, message.length - 2)
+ }
+ return message
+ }
+
+ fun Double.round(decimals: Int): Double {
+ var multiplier = 1.0
+ repeat(decimals) { multiplier *= 10 }
+ return kotlin.math.round(this * multiplier) / multiplier
+ }
+
+ fun String.between(start: String, end: String): String = this.split(start, end)[1]
+
+ val EntityLivingBase.baseMaxHealth: Double
+ get() = this.getEntityAttribute(SharedMonsterAttributes.maxHealth).baseValue
+
+ fun formatPercentage(percentage: Double): String = formatPercentage(percentage, "0.00")
+
+ fun formatPercentage(percentage: Double, format: String?): String =
+ DecimalFormat(format).format(percentage * 100).replace(',', '.') + "%"
+
+ fun formatInteger(i: Int): String = DecimalFormat("#,##0").format(i.toLong()).replace(',', '.')
+
+ fun formatDouble(d: Double, format: String?): String =
+ DecimalFormat(format).format(d).replace(',', 'x').replace('.', ',').replace('x', '.')
+
+ fun formatDouble(d: Double): String = formatDouble(d, "#,##0.0")
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt
new file mode 100644
index 000000000..bfc5f7228
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt
@@ -0,0 +1,61 @@
+package at.hannibal2.skyhanni.utils
+
+import net.minecraft.entity.Entity
+import net.minecraft.util.BlockPos
+import net.minecraft.util.Vec3
+import kotlin.math.pow
+
+data class LorenzVec(
+ val x: Double,
+ val y: Double,
+ val z: Double
+) {
+ constructor(x: Int, y: Int, z: Int) : this(x.toDouble(), y.toDouble(), z.toDouble())
+
+ fun toBlocPos(): BlockPos = BlockPos(x, y, z)
+
+ fun toVec3(): Vec3 = Vec3(x, y, z)
+
+ fun distance(other: LorenzVec): Double = distanceSq(other).pow(0.5)
+
+ fun distanceSq(x: Double, y: Double, z: Double): Double = distanceSq(LorenzVec(x, y, z))
+
+ fun distance(x: Double, y: Double, z: Double): Double = distance(LorenzVec(x, y, z))
+
+ fun distanceSq(other: LorenzVec): Double {
+ val dx = (other.x - x)
+ val dy = (other.y - y)
+ val dz = (other.z - z)
+ return (dx * dx + dy * dy + dz * dz)
+ }
+
+ fun add(x: Double, y: Double, z: Double): LorenzVec = LorenzVec(this.x + x, this.y + y, this.z + z)
+
+ fun add(x: Int, y: Int, z: Int): LorenzVec = LorenzVec(this.x + x, this.y + y, this.z + z)
+
+ override fun toString(): String {
+ return "LorenzVec{" +
+ "x=" + x +
+ ", y=" + y +
+ ", z=" + z +
+ '}'
+ }
+
+ fun multiply(d: Double): LorenzVec = LorenzVec(x multiplyZeroSave d, y multiplyZeroSave d, z multiplyZeroSave d)
+
+ fun multiply(d: Int): LorenzVec =
+ LorenzVec(x multiplyZeroSave d.toDouble(), y multiplyZeroSave d.toDouble(), z multiplyZeroSave d.toDouble())
+
+ fun add(other: LorenzVec) = LorenzVec(x + other.x, y + other.y, z + other.z)
+}
+
+private infix fun Double.multiplyZeroSave(other: Double): Double {
+ val result = this * other
+ return if (result == -0.0) 0.0 else result
+}
+
+fun BlockPos.toLorenzVec(): LorenzVec = LorenzVec(x, y, z)
+
+fun Entity.getLorenzVec(): LorenzVec = LorenzVec(posX, posY, posZ)
+
+fun Vec3.toLorenzVec(): LorenzVec = LorenzVec(xCoord, yCoord, zCoord) \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt
new file mode 100644
index 000000000..891826a91
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt
@@ -0,0 +1,152 @@
+package at.hannibal2.skyhanni.utils
+
+import java.text.NumberFormat
+import java.util.*
+import kotlin.math.pow
+import kotlin.math.roundToInt
+
+object NumberUtil {
+ @JvmField
+ val nf: NumberFormat = NumberFormat.getInstance(Locale.US)
+ private val suffixes = TreeMap<Long, String>().apply {
+ this[1000L] = "k"
+ this[1000000L] = "M"
+ this[1000000000L] = "B"
+ this[1000000000000L] = "T"
+ this[1000000000000000L] = "P"
+ this[1000000000000000000L] = "E"
+ }
+ private val romanSymbols = TreeMap(
+ mapOf(
+ 1000 to "M",
+ 900 to "CM",
+ 500 to "D",
+ 400 to "CD",
+ 100 to "C",
+ 90 to "XC",
+ 50 to "L",
+ 40 to "XL",
+ 10 to "X",
+ 9 to "IX",
+ 5 to "V",
+ 4 to "IV",
+ 1 to "I",
+ )
+ )
+
+ /**
+ * This code was unmodified and taken under CC BY-SA 3.0 license
+ * @link https://stackoverflow.com/a/30661479
+ * @author assylias
+ */
+ @JvmStatic
+ fun format(value: Number): String {
+ @Suppress("NAME_SHADOWING")
+ val value = value.toLong()
+ //Long.MIN_VALUE == -Long.MIN_VALUE so we need an adjustment here
+ if (value == Long.MIN_VALUE) return format(Long.MIN_VALUE + 1)
+ if (value < 0) return "-" + format(-value)
+ if (value < 1000) return value.toString() //deal with easy case
+ val (divideBy, suffix) = suffixes.floorEntry(value)
+ val truncated = value / (divideBy / 10) //the number part of the output times 10
+ val hasDecimal = truncated < 100 && truncated / 10.0 != (truncated / 10).toDouble()
+ return if (hasDecimal) (truncated / 10.0).toString() + suffix else (truncated / 10).toString() + suffix
+ }
+
+ @JvmStatic
+ fun unformat(value: String): Long {
+ val suffix = value.filter { !it.isDigit() }.lowercase()
+ val num = value.filter { it.isDigit() }.toLong()
+ return num * (suffixes.entries.find { it.value.lowercase() == suffix }?.key ?: 1)
+ }
+
+ /**
+ * This code was unmodified and taken under CC BY-SA 3.0 license
+ * @link https://stackoverflow.com/a/22186845
+ * @author jpdymond
+ */
+ fun Double.roundToPrecision(precision: Int): Double {
+ val scale = 10.0.pow(precision).toInt()
+ return (this * scale).roundToInt().toDouble() / scale
+ }
+
+ /**
+ * This code was unmodified and taken under CC BY-SA 3.0 license
+ * @link https://stackoverflow.com/a/22186845
+ * @author jpdymond
+ */
+ fun Float.roundToPrecision(precision: Int): Float {
+ val scale = 10.0.pow(precision).toInt()
+ return (this * scale).roundToInt().toFloat() / scale
+ }
+
+ fun Number.addSuffix(): String {
+ val long = this.toLong()
+ if (long in 11..13) return "${this}th"
+ return when (long % 10) {
+ 1L -> "${this}st"
+ 2L -> "${this}nd"
+ 3L -> "${this}rd"
+ else -> "${this}th"
+ }
+ }
+
+ /**
+ * This code was converted to Kotlin and taken under CC BY-SA 3.0 license
+ * @link https://stackoverflow.com/a/9073310
+ */
+ fun String.romanToDecimal(): Int {
+ var decimal = 0
+ var lastNumber = 0
+ val romanNumeral = this.uppercase()
+ for (x in romanNumeral.length - 1 downTo 0) {
+ when (romanNumeral[x]) {
+ 'M' -> {
+ decimal = processDecimal(1000, lastNumber, decimal)
+ lastNumber = 1000
+ }
+ 'D' -> {
+ decimal = processDecimal(500, lastNumber, decimal)
+ lastNumber = 500
+ }
+ 'C' -> {
+ decimal = processDecimal(100, lastNumber, decimal)
+ lastNumber = 100
+ }
+ 'L' -> {
+ decimal = processDecimal(50, lastNumber, decimal)
+ lastNumber = 50
+ }
+ 'X' -> {
+ decimal = processDecimal(10, lastNumber, decimal)
+ lastNumber = 10
+ }
+ 'V' -> {
+ decimal = processDecimal(5, lastNumber, decimal)
+ lastNumber = 5
+ }
+ 'I' -> {
+ decimal = processDecimal(1, lastNumber, decimal)
+ lastNumber = 1
+ }
+ }
+ }
+ return decimal
+ }
+
+ fun Int.toRoman(): String {
+ if (this <= 0) error("$this must be positive!")
+ val l = romanSymbols.floorKey(this)
+ return if (this == l) {
+ romanSymbols[this]!!
+ } else romanSymbols[l] + (this - l).toRoman()
+ }
+
+ private fun processDecimal(decimal: Int, lastNumber: Int, lastDecimal: Int): Int {
+ return if (lastNumber > decimal) {