Add fuel durability bar

This commit is contained in:
Linnea Gräf
2024-04-26 10:11:23 +02:00
parent c264ca9e8f
commit 041da7c7d1
6 changed files with 179 additions and 15 deletions

View File

@@ -0,0 +1,58 @@
/*
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.mixins;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Share;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import moe.nea.firmament.util.DurabilityBarEvent;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(DrawContext.class)
public class CustomDurabilityBarPatch {
@WrapOperation(
method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;isItemBarVisible()Z")
)
private boolean onIsItemBarVisible(
ItemStack instance, Operation<Boolean> original,
@Share("barOverride") LocalRef<DurabilityBarEvent.DurabilityBar> barOverride
) {
if (original.call(instance))
return true;
DurabilityBarEvent event = new DurabilityBarEvent(instance);
DurabilityBarEvent.Companion.publish(event);
barOverride.set(event.getBarOverride());
return barOverride.get() != null;
}
@WrapOperation(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemBarStep()I"))
private int overrideItemStep(
ItemStack instance, Operation<Integer> original,
@Share("barOverride") LocalRef<DurabilityBarEvent.DurabilityBar> barOverride
) {
if (barOverride.get() != null)
return Math.round(barOverride.get().getPercentage() * 13);
return original.call(instance);
}
@WrapOperation(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItemBarColor()I"))
private int overrideItemColor(
ItemStack instance, Operation<Integer> original,
@Share("barOverride") LocalRef<DurabilityBarEvent.DurabilityBar> barOverride
) {
if (barOverride.get() != null)
return barOverride.get().getColor().getColor();
return original.call(instance);
}
}

View File

@@ -7,11 +7,10 @@
package moe.nea.firmament.features.mining package moe.nea.firmament.features.mining
import java.util.regex.Pattern import java.util.regex.Pattern
import org.intellij.lang.annotations.Language
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.util.DyeColor
import net.minecraft.util.Hand import net.minecraft.util.Hand
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import moe.nea.firmament.events.HudRenderEvent import moe.nea.firmament.events.HudRenderEvent
@@ -20,11 +19,19 @@ import moe.nea.firmament.events.SlotClickEvent
import moe.nea.firmament.events.WorldReadyEvent import moe.nea.firmament.events.WorldReadyEvent
import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.util.DurabilityBarEvent
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SHORT_NUMBER_FORMAT
import moe.nea.firmament.util.TIME_PATTERN
import moe.nea.firmament.util.TimeMark import moe.nea.firmament.util.TimeMark
import moe.nea.firmament.util.extraAttributes
import moe.nea.firmament.util.item.displayNameAccordingToNbt import moe.nea.firmament.util.item.displayNameAccordingToNbt
import moe.nea.firmament.util.item.loreAccordingToNbt import moe.nea.firmament.util.item.loreAccordingToNbt
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.RenderCircleProgress
import moe.nea.firmament.util.render.lerp
import moe.nea.firmament.util.toShedaniel
import moe.nea.firmament.util.unformattedString import moe.nea.firmament.util.unformattedString
import moe.nea.firmament.util.useMatch import moe.nea.firmament.util.useMatch
@@ -36,6 +43,7 @@ object PickaxeAbility : FirmamentFeature {
object TConfig : ManagedConfig(identifier) { object TConfig : ManagedConfig(identifier) {
val cooldownEnabled by toggle("ability-cooldown") { true } val cooldownEnabled by toggle("ability-cooldown") { true }
val cooldownScale by integer("ability-scale", 16, 64) { 16 } val cooldownScale by integer("ability-scale", 16, 64) { 16 }
val drillFuelBar by toggle("fuel-bar") { true }
} }
var lobbyJoinTime = TimeMark.farPast() var lobbyJoinTime = TimeMark.farPast()
@@ -73,13 +81,36 @@ object PickaxeAbility : FirmamentFeature {
abilityOverride = null abilityOverride = null
} }
ProcessChatEvent.subscribe { ProcessChatEvent.subscribe {
pattern.useMatch(it.unformattedString) { abilityUsePattern.useMatch(it.unformattedString) {
lastUsage[group("name")] = TimeMark.now() lastUsage[group("name")] = TimeMark.now()
} }
abilitySwitchPattern.useMatch(it.unformattedString) { abilitySwitchPattern.useMatch(it.unformattedString) {
abilityOverride = group("ability") abilityOverride = group("ability")
} }
} }
DurabilityBarEvent.subscribe {
if (!TConfig.drillFuelBar) return@subscribe
val lore = it.item.loreAccordingToNbt
if (lore.lastOrNull()?.value?.unformattedString?.contains("DRILL") != true) return@subscribe
val maxFuel = lore.firstNotNullOfOrNull {
fuelPattern.useMatch(
it.value?.unformattedString ?: return@firstNotNullOfOrNull null
) {
parseShortNumber(group("maxFuel"))
}
} ?: return@subscribe
val extra = it.item.extraAttributes
if (!extra.contains("drill_fuel")) return@subscribe
val fuel = extra.getInt("drill_fuel")
val percentage = fuel / maxFuel.toFloat()
it.barOverride = DurabilityBarEvent.DurabilityBar(
lerp(
DyeColor.RED.toShedaniel(),
DyeColor.GREEN.toShedaniel(),
percentage
), percentage
)
}
SlotClickEvent.subscribe { SlotClickEvent.subscribe {
if (MC.screen?.title?.unformattedString == "Heart of the Mountain") { if (MC.screen?.title?.unformattedString == "Heart of the Mountain") {
val name = it.stack.displayNameAccordingToNbt?.unformattedString ?: return@subscribe val name = it.stack.displayNameAccordingToNbt?.unformattedString ?: return@subscribe
@@ -93,7 +124,8 @@ object PickaxeAbility : FirmamentFeature {
} }
} }
val pattern = Pattern.compile("You used your (?<name>.*) Pickaxe Ability!") val abilityUsePattern = Pattern.compile("You used your (?<name>.*) Pickaxe Ability!")
val fuelPattern = Pattern.compile("Fuel: .*/(?<maxFuel>$SHORT_NUMBER_FORMAT)")
data class PickaxeAbilityData( data class PickaxeAbilityData(
val name: String, val name: String,
@@ -117,21 +149,12 @@ object PickaxeAbility : FirmamentFeature {
return PickaxeAbilityData(name, cooldown) return PickaxeAbilityData(name, cooldown)
} }
@Language("RegExp")
val TIME_PATTERN = "[0-9]+[ms]"
val cooldownPattern = Pattern.compile("Cooldown: (?<cooldown>$TIME_PATTERN)") val cooldownPattern = Pattern.compile("Cooldown: (?<cooldown>$TIME_PATTERN)")
val abilityPattern = Pattern.compile("Ability: (?<name>.*) {2}RIGHT CLICK") val abilityPattern = Pattern.compile("Ability: (?<name>.*) {2}RIGHT CLICK")
val abilitySwitchPattern = val abilitySwitchPattern =
Pattern.compile("You selected (?<ability>.*) as your Pickaxe Ability\\. This ability will apply to all of your pickaxes!") Pattern.compile("You selected (?<ability>.*) as your Pickaxe Ability\\. This ability will apply to all of your pickaxes!")
fun parseTimePattern(text: String): Duration {
val length = text.dropLast(1).toInt()
return when (text.last()) {
'm' -> length.minutes
's' -> length.seconds
else -> error("Invalid pattern for time $text")
}
}
private fun renderHud(event: HudRenderEvent) { private fun renderHud(event: HudRenderEvent) {
if (!TConfig.cooldownEnabled) return if (!TConfig.cooldownEnabled) return

View File

@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.util
import me.shedaniel.math.Color
import net.minecraft.item.ItemStack
import moe.nea.firmament.events.FirmamentEvent
import moe.nea.firmament.events.FirmamentEventBus
data class DurabilityBarEvent(
val item: ItemStack,
) : FirmamentEvent() {
data class DurabilityBar(
val color: Color,
val percentage: Float,
)
var barOverride: DurabilityBar? = null
companion object : FirmamentEventBus<DurabilityBarEvent>()
}

View File

@@ -1,5 +1,6 @@
/* /*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe> * SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
* *
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
@@ -8,6 +9,10 @@ package moe.nea.firmament.util
import java.util.regex.Matcher import java.util.regex.Matcher
import java.util.regex.Pattern import java.util.regex.Pattern
import org.intellij.lang.annotations.Language
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
inline fun <T> String.ifMatches(regex: Regex, block: (MatchResult) -> T): T? = inline fun <T> String.ifMatches(regex: Regex, block: (MatchResult) -> T): T? =
regex.matchEntire(this)?.let(block) regex.matchEntire(this)?.let(block)
@@ -16,3 +21,40 @@ inline fun <T> Pattern.useMatch(string: String, block: Matcher.() -> T): T? =
matcher(string) matcher(string)
.takeIf(Matcher::matches) .takeIf(Matcher::matches)
?.let(block) ?.let(block)
@Language("RegExp")
val TIME_PATTERN = "[0-9]+[ms]"
@Language("RegExp")
val SHORT_NUMBER_FORMAT = "[0-9]+(?:,[0-9]+)*(?:\\.[0-9]+)?[kKmMbB]?"
val siScalars = mapOf(
'k' to 1_000.0,
'K' to 1_000.0,
'm' to 1_000_000.0,
'M' to 1_000_000.0,
'b' to 1_000_000_000.0,
'B' to 1_000_000_000.0,
)
fun parseTimePattern(text: String): Duration {
val length = text.dropLast(1).toInt()
return when (text.last()) {
'm' -> length.minutes
's' -> length.seconds
else -> error("Invalid pattern for time $text")
}
}
fun parseShortNumber(string: String): Double {
var k = string.replace(",", "")
val scalar = k.last()
var scalarMultiplier = siScalars[scalar]
if (scalarMultiplier == null) {
scalarMultiplier = 1.0
} else {
k = k.dropLast(1)
}
return k.toDouble() * scalarMultiplier
}

View File

@@ -6,6 +6,8 @@
package moe.nea.firmament.util.render package moe.nea.firmament.util.render
import me.shedaniel.math.Color
val pi = Math.PI val pi = Math.PI
val tau = Math.PI * 2 val tau = Math.PI * 2
fun lerpAngle(a: Float, b: Float, progress: Float): Float { fun lerpAngle(a: Float, b: Float, progress: Float): Float {
@@ -17,7 +19,20 @@ fun lerpAngle(a: Float, b: Float, progress: Float): Float {
fun lerp(a: Float, b: Float, progress: Float): Float { fun lerp(a: Float, b: Float, progress: Float): Float {
return a + (b - a) * progress return a + (b - a) * progress
} }
fun lerp(a: Int, b: Int, progress: Float): Int {
return (a + (b - a) * progress).toInt()
}
fun ilerp(a: Float, b: Float, value: Float): Float { fun ilerp(a: Float, b: Float, value: Float): Float {
return (value - a) / (b - a) return (value - a) / (b - a)
} }
fun lerp(a: Color, b: Color, progress: Float): Color {
return Color.ofRGBA(
lerp(a.red, b.red, progress),
lerp(a.green, b.green, progress),
lerp(a.blue, b.blue, progress),
lerp(a.alpha, b.alpha, progress),
)
}

View File

@@ -167,5 +167,6 @@
"firmament.quick-commands.join.unknown-catacombs": "Unknown catacombs floor %s", "firmament.quick-commands.join.unknown-catacombs": "Unknown catacombs floor %s",
"firmament.config.pickaxe-info": "Pickaxes", "firmament.config.pickaxe-info": "Pickaxes",
"firmament.config.pickaxe-info.ability-cooldown": "Pickaxe Ability Cooldown", "firmament.config.pickaxe-info.ability-cooldown": "Pickaxe Ability Cooldown",
"firmament.config.pickaxe-info.ability-scale": "Ability Cooldown Scale" "firmament.config.pickaxe-info.ability-scale": "Ability Cooldown Scale",
"firmament.config.pickaxe-info.fuel-bar": "Drill Fuel Durability Bar"
} }