Make pickaxe ability display use AbilityUtils

[no changelog]
This commit is contained in:
Linnea Gräf
2024-10-13 17:32:10 +02:00
parent daa63bd914
commit e6142bb936
24 changed files with 1433 additions and 681 deletions

View File

@@ -28,6 +28,7 @@ import moe.nea.firmament.mixins.accessor.AccessorHandledScreen
import moe.nea.firmament.util.ClipboardUtils
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.focusedItemStack
import moe.nea.firmament.util.mc.SNbtFormatter.Companion.toPrettyString
import moe.nea.firmament.util.mc.displayNameAccordingToNbt
import moe.nea.firmament.util.mc.loreAccordingToNbt
import moe.nea.firmament.util.skyBlockId
@@ -44,6 +45,7 @@ object PowerUserTools : FirmamentFeature {
val copyLoreData by keyBindingWithDefaultUnbound("copy-lore")
val copySkullTexture by keyBindingWithDefaultUnbound("copy-skull-texture")
val copyEntityData by keyBindingWithDefaultUnbound("entity-data")
val copyItemStack by keyBindingWithDefaultUnbound("copy-item-stack")
}
override val config
@@ -125,7 +127,7 @@ object PowerUserTools : FirmamentFeature {
Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.modelid", model.toString()))
} else if (it.matches(TConfig.copyNbtData)) {
// TODO: copy full nbt
val nbt = item.get(DataComponentTypes.CUSTOM_DATA)?.nbt?.toString() ?: "<empty>"
val nbt = item.get(DataComponentTypes.CUSTOM_DATA)?.nbt?.toPrettyString() ?: "<empty>"
ClipboardUtils.setTextContent(nbt)
lastCopiedStack = Pair(item, Text.translatable("firmament.tooltip.copied.nbt"))
} else if (it.matches(TConfig.copyLoreData)) {
@@ -157,6 +159,9 @@ object PowerUserTools : FirmamentFeature {
Text.stringifiedTranslatable("firmament.tooltip.copied.skull-id", skullTexture.toString())
)
println("Copied skull id: $skullTexture")
} else if (it.matches(TConfig.copyItemStack)) {
ClipboardUtils.setTextContent(item.encode(MC.currentOrDefaultRegistries).toPrettyString())
lastCopiedStack = Pair(item, Text.stringifiedTranslatable("firmament.tooltip.copied.stack"))
}
}

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.features.mining
import java.util.regex.Pattern
@@ -30,155 +29,146 @@ import moe.nea.firmament.util.parseShortNumber
import moe.nea.firmament.util.parseTimePattern
import moe.nea.firmament.util.render.RenderCircleProgress
import moe.nea.firmament.util.render.lerp
import moe.nea.firmament.util.skyblock.AbilityUtils
import moe.nea.firmament.util.toShedaniel
import moe.nea.firmament.util.unformattedString
import moe.nea.firmament.util.useMatch
object PickaxeAbility : FirmamentFeature {
override val identifier: String
get() = "pickaxe-info"
override val identifier: String
get() = "pickaxe-info"
object TConfig : ManagedConfig(identifier) {
val cooldownEnabled by toggle("ability-cooldown") { true }
val cooldownScale by integer("ability-scale", 16, 64) { 16 }
val drillFuelBar by toggle("fuel-bar") { true }
}
object TConfig : ManagedConfig(identifier) {
val cooldownEnabled by toggle("ability-cooldown") { true }
val cooldownScale by integer("ability-scale", 16, 64) { 16 }
val drillFuelBar by toggle("fuel-bar") { true }
}
var lobbyJoinTime = TimeMark.farPast()
var lastUsage = mutableMapOf<String, TimeMark>()
var abilityOverride: String? = null
var defaultAbilityDurations = mutableMapOf<String, Duration>(
"Mining Speed Boost" to 120.seconds,
"Pickobulus" to 110.seconds,
"Gemstone Infusion" to 140.seconds,
"Hazardous Miner" to 140.seconds,
"Maniac Miner" to 59.seconds,
"Vein Seeker" to 60.seconds
)
var lobbyJoinTime = TimeMark.farPast()
var lastUsage = mutableMapOf<String, TimeMark>()
var abilityOverride: String? = null
var defaultAbilityDurations = mutableMapOf<String, Duration>(
"Mining Speed Boost" to 120.seconds,
"Pickobulus" to 110.seconds,
"Gemstone Infusion" to 140.seconds,
"Hazardous Miner" to 140.seconds,
"Maniac Miner" to 59.seconds,
"Vein Seeker" to 60.seconds
)
override val config: ManagedConfig
get() = TConfig
override val config: ManagedConfig
get() = TConfig
fun getCooldownPercentage(name: String, cooldown: Duration): Double {
val sinceLastUsage = lastUsage[name]?.passedTime() ?: Duration.INFINITE
val sinceLobbyJoin = lobbyJoinTime.passedTime()
fun getCooldownPercentage(name: String, cooldown: Duration): Double {
val sinceLastUsage = lastUsage[name]?.passedTime() ?: Duration.INFINITE
val sinceLobbyJoin = lobbyJoinTime.passedTime()
if (SBData.skyblockLocation == SkyBlockIsland.MINESHAFT) {
if (sinceLobbyJoin < sinceLastUsage) {
return 1.0
}
}
if (sinceLastUsage < cooldown)
return sinceLastUsage / cooldown
return 1.0
}
if (sinceLastUsage < cooldown)
return sinceLastUsage / cooldown
return 1.0
}
@Subscribe
fun onSlotClick(it: SlotClickEvent) {
if (MC.screen?.title?.unformattedString == "Heart of the Mountain") {
val name = it.stack.displayNameAccordingToNbt?.unformattedString ?: return
val cooldown = it.stack.loreAccordingToNbt.firstNotNullOfOrNull {
cooldownPattern.useMatch(it.unformattedString) {
parseTimePattern(group("cooldown"))
}
} ?: return
defaultAbilityDurations[name] = cooldown
}
}
@Subscribe
fun onSlotClick(it: SlotClickEvent) {
if (MC.screen?.title?.unformattedString == "Heart of the Mountain") {
val name = it.stack.displayNameAccordingToNbt.unformattedString
val cooldown = it.stack.loreAccordingToNbt.firstNotNullOfOrNull {
cooldownPattern.useMatch(it.unformattedString) {
parseTimePattern(group("cooldown"))
}
} ?: return
defaultAbilityDurations[name] = cooldown
}
}
@Subscribe
fun onDurabilityBar(it: DurabilityBarEvent) {
if (!TConfig.drillFuelBar) return
val lore = it.item.loreAccordingToNbt
if (lore.lastOrNull()?.unformattedString?.contains("DRILL") != true) return
val maxFuel = lore.firstNotNullOfOrNull {
fuelPattern.useMatch(it.unformattedString) {
parseShortNumber(group("maxFuel"))
}
} ?: return
val extra = it.item.extraAttributes
if (!extra.contains("drill_fuel")) return
val fuel = extra.getInt("drill_fuel")
val percentage = fuel / maxFuel.toFloat()
it.barOverride = DurabilityBarEvent.DurabilityBar(
lerp(
DyeColor.RED.toShedaniel(),
DyeColor.GREEN.toShedaniel(),
percentage
), percentage
)
}
@Subscribe
fun onDurabilityBar(it: DurabilityBarEvent) {
if (!TConfig.drillFuelBar) return
val lore = it.item.loreAccordingToNbt
if (lore.lastOrNull()?.unformattedString?.contains("DRILL") != true) return
val maxFuel = lore.firstNotNullOfOrNull {
fuelPattern.useMatch(it.unformattedString) {
parseShortNumber(group("maxFuel"))
}
} ?: return
val extra = it.item.extraAttributes
if (!extra.contains("drill_fuel")) return
val fuel = extra.getInt("drill_fuel")
val percentage = fuel / maxFuel.toFloat()
it.barOverride = DurabilityBarEvent.DurabilityBar(
lerp(
DyeColor.RED.toShedaniel(),
DyeColor.GREEN.toShedaniel(),
percentage
), percentage
)
}
@Subscribe
fun onChatMessage(it: ProcessChatEvent) {
abilityUsePattern.useMatch(it.unformattedString) {
lastUsage[group("name")] = TimeMark.now()
}
abilitySwitchPattern.useMatch(it.unformattedString) {
abilityOverride = group("ability")
}
}
@Subscribe
fun onChatMessage(it: ProcessChatEvent) {
abilityUsePattern.useMatch(it.unformattedString) {
lastUsage[group("name")] = TimeMark.now()
}
abilitySwitchPattern.useMatch(it.unformattedString) {
abilityOverride = group("ability")
}
}
@Subscribe
fun onWorldReady(event: WorldReadyEvent) {
lobbyJoinTime = TimeMark.now()
abilityOverride = null
}
@Subscribe
fun onWorldReady(event: WorldReadyEvent) {
lobbyJoinTime = TimeMark.now()
abilityOverride = null
}
@Subscribe
fun onProfileSwitch(event: ProfileSwitchEvent) {
lastUsage.clear()
}
val abilityUsePattern = Pattern.compile("You used your (?<name>.*) Pickaxe Ability!")
val fuelPattern = Pattern.compile("Fuel: .*/(?<maxFuel>$SHORT_NUMBER_FORMAT)")
val abilityUsePattern = Pattern.compile("You used your (?<name>.*) Pickaxe Ability!")
val fuelPattern = Pattern.compile("Fuel: .*/(?<maxFuel>$SHORT_NUMBER_FORMAT)")
val pickaxeAbilityCooldownPattern = Pattern.compile("Your pickaxe ability is on cooldown for (?<remainingCooldown>$TIME_PATTERN)\\.")
data class PickaxeAbilityData(
val name: String,
val cooldown: Duration,
)
data class PickaxeAbilityData(
val name: String,
val cooldown: Duration,
)
fun getCooldownFromLore(itemStack: ItemStack): PickaxeAbilityData? {
val lore = itemStack.loreAccordingToNbt
if (!lore.any { it.unformattedString.contains("Breaking Power") })
return null
val cooldown = lore.firstNotNullOfOrNull {
cooldownPattern.useMatch(it.unformattedString) {
parseTimePattern(group("cooldown"))
}
} ?: return null
val name = lore.firstNotNullOfOrNull {
abilityPattern.useMatch(it.unformattedString) {
group("name")
}
} ?: return null
return PickaxeAbilityData(name, cooldown)
}
fun getCooldownFromLore(itemStack: ItemStack): PickaxeAbilityData? {
val lore = itemStack.loreAccordingToNbt
if (!lore.any { it.unformattedString.contains("Breaking Power") })
return null
val ability = AbilityUtils.getAbilities(itemStack).firstOrNull() ?: return null
return PickaxeAbilityData(ability.name, ability.cooldown ?: return null)
}
val cooldownPattern = Pattern.compile("Cooldown: (?<cooldown>$TIME_PATTERN)")
val abilitySwitchPattern =
Pattern.compile("You selected (?<ability>.*) as your Pickaxe Ability\\. This ability will apply to all of your pickaxes!")
val cooldownPattern = Pattern.compile("Cooldown: (?<cooldown>$TIME_PATTERN)")
val abilityPattern = Pattern.compile("(⦾ )?Ability: (?<name>.*) {2}RIGHT CLICK")
val abilitySwitchPattern =
Pattern.compile("You selected (?<ability>.*) as your Pickaxe Ability\\. This ability will apply to all of your pickaxes!")
@Subscribe
fun renderHud(event: HudRenderEvent) {
if (!TConfig.cooldownEnabled) return
var ability = getCooldownFromLore(MC.player?.getStackInHand(Hand.MAIN_HAND) ?: return) ?: return
defaultAbilityDurations[ability.name] = ability.cooldown
val ao = abilityOverride
if (ao != ability.name && ao != null) {
ability = PickaxeAbilityData(ao, defaultAbilityDurations[ao] ?: 120.seconds)
}
event.context.matrices.push()
event.context.matrices.translate(MC.window.scaledWidth / 2F, MC.window.scaledHeight / 2F, 0F)
event.context.matrices.scale(TConfig.cooldownScale.toFloat(), TConfig.cooldownScale.toFloat(), 1F)
RenderCircleProgress.renderCircle(
event.context, Identifier.of("firmament", "textures/gui/circle.png"),
getCooldownPercentage(ability.name, ability.cooldown).toFloat(),
0f, 1f, 0f, 1f
)
event.context.matrices.pop()
}
@Subscribe
fun renderHud(event: HudRenderEvent) {
if (!TConfig.cooldownEnabled) return
if (!event.isRenderingCursor) return
var ability = getCooldownFromLore(MC.player?.getStackInHand(Hand.MAIN_HAND) ?: return) ?: return
defaultAbilityDurations[ability.name] = ability.cooldown
val ao = abilityOverride
if (ao != ability.name && ao != null) {
ability = PickaxeAbilityData(ao, defaultAbilityDurations[ao] ?: 120.seconds)
}
event.context.matrices.push()
event.context.matrices.translate(MC.window.scaledWidth / 2F, MC.window.scaledHeight / 2F, 0F)
event.context.matrices.scale(TConfig.cooldownScale.toFloat(), TConfig.cooldownScale.toFloat(), 1F)
RenderCircleProgress.renderCircle(
event.context, Identifier.of("firmament", "textures/gui/circle.png"),
getCooldownPercentage(ability.name, ability.cooldown).toFloat(),
0f, 1f, 0f, 1f
)
event.context.matrices.pop()
}
}

View File

@@ -1,4 +1,3 @@
package moe.nea.firmament.features.texturepack
import com.google.gson.JsonElement
@@ -6,120 +5,120 @@ import com.google.gson.JsonPrimitive
import moe.nea.firmament.util.useMatch
abstract class NumberMatcher {
abstract fun test(number: Number): Boolean
abstract fun test(number: Number): Boolean
companion object {
fun parse(jsonElement: JsonElement): NumberMatcher? {
if (jsonElement is JsonPrimitive) {
if (jsonElement.isString) {
val string = jsonElement.asString
return parseRange(string) ?: parseOperator(string)
}
if (jsonElement.isNumber) {
val number = jsonElement.asNumber
val hasDecimals = (number.toString().contains("."))
return MatchNumberExact(if (hasDecimals) number.toLong() else number.toDouble())
}
}
return null
}
companion object {
fun parse(jsonElement: JsonElement): NumberMatcher? {
if (jsonElement is JsonPrimitive) {
if (jsonElement.isString) {
val string = jsonElement.asString
return parseRange(string) ?: parseOperator(string)
}
if (jsonElement.isNumber) {
val number = jsonElement.asNumber
val hasDecimals = (number.toString().contains("."))
return MatchNumberExact(if (hasDecimals) number.toLong() else number.toDouble())
}
}
return null
}
private val intervalSpec =
"(?<beginningOpen>[\\[\\(])(?<beginning>[0-9.]+)?,(?<ending>[0-9.]+)?(?<endingOpen>[\\]\\)])"
.toPattern()
private val intervalSpec =
"(?<beginningOpen>[\\[\\(])(?<beginning>[0-9.]+)?,(?<ending>[0-9.]+)?(?<endingOpen>[\\]\\)])"
.toPattern()
fun parseRange(string: String): RangeMatcher? {
intervalSpec.useMatch<Nothing>(string) {
// Open in the set-theory sense, meaning does not include its end.
val beginningOpen = group("beginningOpen") == "("
val endingOpen = group("endingOpen") == ")"
val beginning = group("beginning")?.toDouble()
val ending = group("ending")?.toDouble()
return RangeMatcher(beginning, !beginningOpen, ending, !endingOpen)
}
return null
}
fun parseRange(string: String): RangeMatcher? {
intervalSpec.useMatch<Nothing>(string) {
// Open in the set-theory sense, meaning does not include its end.
val beginningOpen = group("beginningOpen") == "("
val endingOpen = group("endingOpen") == ")"
val beginning = group("beginning")?.toDouble()
val ending = group("ending")?.toDouble()
return RangeMatcher(beginning, !beginningOpen, ending, !endingOpen)
}
return null
}
enum class Operator(val operator: String) {
LESS("<") {
override fun matches(comparisonResult: Int): Boolean {
return comparisonResult < 0
}
},
LESS_EQUALS("<=") {
override fun matches(comparisonResult: Int): Boolean {
return comparisonResult <= 0
}
},
GREATER(">") {
override fun matches(comparisonResult: Int): Boolean {
return comparisonResult > 0
}
},
GREATER_EQUALS(">=") {
override fun matches(comparisonResult: Int): Boolean {
return comparisonResult >= 0
}
},
;
enum class Operator(val operator: String) {
LESS("<") {
override fun matches(comparisonResult: Int): Boolean {
return comparisonResult < 0
}
},
LESS_EQUALS("<=") {
override fun matches(comparisonResult: Int): Boolean {
return comparisonResult <= 0
}
},
GREATER(">") {
override fun matches(comparisonResult: Int): Boolean {
return comparisonResult > 0
}
},
GREATER_EQUALS(">=") {
override fun matches(comparisonResult: Int): Boolean {
return comparisonResult >= 0
}
},
;
abstract fun matches(comparisonResult: Int): Boolean
}
abstract fun matches(comparisonResult: Int): Boolean
}
private val operatorPattern = "(?<operator>${Operator.entries.joinToString("|") {it.operator}})(?<value>[0-9.]+)".toPattern()
private val operatorPattern =
"(?<operator>${Operator.entries.joinToString("|") { it.operator }})(?<value>[0-9.]+)".toPattern()
fun parseOperator(string: String): OperatorMatcher? {
operatorPattern.useMatch<Nothing>(string) {
val operatorName = group("operator")
val operator = Operator.entries.find { it.operator == operatorName }!!
val value = group("value").toDouble()
return OperatorMatcher(operator, value)
}
return null
}
fun parseOperator(string: String): OperatorMatcher? {
return operatorPattern.useMatch(string) {
val operatorName = group("operator")
val operator = Operator.entries.find { it.operator == operatorName }!!
val value = group("value").toDouble()
OperatorMatcher(operator, value)
}
}
data class OperatorMatcher(val operator: Operator, val value: Double) : NumberMatcher() {
override fun test(number: Number): Boolean {
return operator.matches(number.toDouble().compareTo(value))
}
}
data class OperatorMatcher(val operator: Operator, val value: Double) : NumberMatcher() {
override fun test(number: Number): Boolean {
return operator.matches(number.toDouble().compareTo(value))
}
}
data class MatchNumberExact(val number: Number) : NumberMatcher() {
override fun test(number: Number): Boolean {
return when (this.number) {
is Double -> number.toDouble() == this.number.toDouble()
else -> number.toLong() == this.number.toLong()
}
}
}
data class MatchNumberExact(val number: Number) : NumberMatcher() {
override fun test(number: Number): Boolean {
return when (this.number) {
is Double -> number.toDouble() == this.number.toDouble()
else -> number.toLong() == this.number.toLong()
}
}
}
data class RangeMatcher(
val beginning: Double?,
val beginningInclusive: Boolean,
val ending: Double?,
val endingInclusive: Boolean,
) : NumberMatcher() {
override fun test(number: Number): Boolean {
val value = number.toDouble()
if (beginning != null) {
if (beginningInclusive) {
if (value < beginning) return false
} else {
if (value <= beginning) return false
}
}
if (ending != null) {
if (endingInclusive) {
if (value > ending) return false
} else {
if (value >= ending) return false
}
}
return true
}
}
}
data class RangeMatcher(
val beginning: Double?,
val beginningInclusive: Boolean,
val ending: Double?,
val endingInclusive: Boolean,
) : NumberMatcher() {
override fun test(number: Number): Boolean {
val value = number.toDouble()
if (beginning != null) {
if (beginningInclusive) {
if (value < beginning) return false
} else {
if (value <= beginning) return false
}
}
if (ending != null) {
if (endingInclusive) {
if (value > ending) return false
} else {
if (value >= ending) return false
}
}
return true
}
}
}
}