Add shiny pig tracker
This commit is contained in:
@@ -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.mixins;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import moe.nea.firmament.events.EntityDespawnEvent;
|
||||||
|
import net.minecraft.client.world.ClientWorld;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
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(ClientWorld.class)
|
||||||
|
public class EntityDespawnPatch {
|
||||||
|
@Inject(method = "removeEntity", at = @At(value = "TAIL"))
|
||||||
|
private void onRemoved(int entityId, Entity.RemovalReason removalReason, CallbackInfo ci, @Local @Nullable Entity entity) {
|
||||||
|
EntityDespawnEvent.Companion.publish(new EntityDespawnEvent(entity, entityId, removalReason));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.mixins;
|
||||||
|
|
||||||
|
import moe.nea.firmament.events.EntityInteractionEvent;
|
||||||
|
import net.minecraft.client.network.ClientPlayerInteractionManager;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.util.ActionResult;
|
||||||
|
import net.minecraft.util.Hand;
|
||||||
|
import net.minecraft.util.hit.EntityHitResult;
|
||||||
|
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;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
@Mixin(ClientPlayerInteractionManager.class)
|
||||||
|
public class EntityInteractEventPatch {
|
||||||
|
@Inject(method = "attackEntity", at = @At("HEAD"))
|
||||||
|
private void onAttack(PlayerEntity player, Entity target, CallbackInfo ci) {
|
||||||
|
EntityInteractionEvent.Companion.publish(new EntityInteractionEvent(EntityInteractionEvent.InteractionKind.ATTACK, target, Hand.MAIN_HAND));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "interactEntity", at = @At("HEAD"))
|
||||||
|
private void onInteract(PlayerEntity player, Entity entity, Hand hand, CallbackInfoReturnable<ActionResult> cir) {
|
||||||
|
EntityInteractionEvent.Companion.publish(new EntityInteractionEvent(EntityInteractionEvent.InteractionKind.INTERACT, entity, hand));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "interactEntityAtLocation", at = @At("HEAD"))
|
||||||
|
private void onInteractAtLocation(PlayerEntity player, Entity entity, EntityHitResult hitResult, Hand hand, CallbackInfoReturnable<ActionResult> cir) {
|
||||||
|
EntityInteractionEvent.Companion.publish(new EntityInteractionEvent(EntityInteractionEvent.InteractionKind.INTERACT_AT_LOCATION, entity, hand));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -19,8 +19,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(InGameHud.class)
|
@Mixin(InGameHud.class)
|
||||||
public class HudRenderEvents {
|
public class HudRenderEventsPatch {
|
||||||
@Inject(method = "renderSleepOverlay", at = @At(value = "TAIL"))
|
@Inject(method = "renderSleepOverlay", at = @At(value = "HEAD"))
|
||||||
public void renderCallBack(DrawContext context, float tickDelta, CallbackInfo ci) {
|
public void renderCallBack(DrawContext context, float tickDelta, CallbackInfo ci) {
|
||||||
HudRenderEvent.Companion.publish(new HudRenderEvent(context, tickDelta));
|
HudRenderEvent.Companion.publish(new HudRenderEvent(context, tickDelta));
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ package moe.nea.firmament.commands
|
|||||||
|
|
||||||
import com.mojang.brigadier.CommandDispatcher
|
import com.mojang.brigadier.CommandDispatcher
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType.string
|
import com.mojang.brigadier.arguments.StringArgumentType.string
|
||||||
import io.ktor.client.statement.*
|
import io.ktor.client.statement.bodyAsText
|
||||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
|
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import moe.nea.firmament.apis.UrsaManager
|
import moe.nea.firmament.apis.UrsaManager
|
||||||
@@ -195,6 +195,13 @@ fun firmamentCommand() = literal("firmament") {
|
|||||||
FairySouls.TConfig.showConfigEditor()
|
FairySouls.TConfig.showConfigEditor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
thenLiteral("simulate") {
|
||||||
|
thenArgument("message", RestArgumentType) { message ->
|
||||||
|
thenExecute {
|
||||||
|
MC.instance.messageHandler.onGameMessage(Text.literal(get(message)), false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
thenLiteral("sbdata") {
|
thenLiteral("sbdata") {
|
||||||
thenExecute {
|
thenExecute {
|
||||||
source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.profile", SBData.profileId))
|
source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.profile", SBData.profileId))
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.events
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity
|
||||||
|
|
||||||
|
data class EntityDespawnEvent(
|
||||||
|
val entity: Entity?, val entityId: Int,
|
||||||
|
val reason: Entity.RemovalReason,
|
||||||
|
) : FirmamentEvent() {
|
||||||
|
companion object: FirmamentEventBus<EntityDespawnEvent>()
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.events
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity
|
||||||
|
import net.minecraft.util.Hand
|
||||||
|
|
||||||
|
data class EntityInteractionEvent(
|
||||||
|
val kind: InteractionKind,
|
||||||
|
val entity: Entity,
|
||||||
|
val hand: Hand,
|
||||||
|
) : FirmamentEvent() {
|
||||||
|
companion object : FirmamentEventBus<EntityInteractionEvent>()
|
||||||
|
enum class InteractionKind {
|
||||||
|
/**
|
||||||
|
* Is sent when left-clicking an entity
|
||||||
|
*/
|
||||||
|
ATTACK,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is a fallback when [INTERACT_AT_LOCATION] fails
|
||||||
|
*/
|
||||||
|
INTERACT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is tried first on right click
|
||||||
|
*/
|
||||||
|
INTERACT_AT_LOCATION,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import moe.nea.firmament.features.debug.DeveloperFeatures
|
|||||||
import moe.nea.firmament.features.debug.MinorTrolling
|
import moe.nea.firmament.features.debug.MinorTrolling
|
||||||
import moe.nea.firmament.features.debug.PowerUserTools
|
import moe.nea.firmament.features.debug.PowerUserTools
|
||||||
import moe.nea.firmament.features.diana.DianaWaypoints
|
import moe.nea.firmament.features.diana.DianaWaypoints
|
||||||
|
import moe.nea.firmament.features.events.anniversity.AnniversaryFeatures
|
||||||
import moe.nea.firmament.features.fixes.CompatibliltyFeatures
|
import moe.nea.firmament.features.fixes.CompatibliltyFeatures
|
||||||
import moe.nea.firmament.features.fixes.Fixes
|
import moe.nea.firmament.features.fixes.Fixes
|
||||||
import moe.nea.firmament.features.inventory.CraftingOverlay
|
import moe.nea.firmament.features.inventory.CraftingOverlay
|
||||||
@@ -70,6 +71,7 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
|
|||||||
loadFeature(ChatLinks)
|
loadFeature(ChatLinks)
|
||||||
loadFeature(InventoryButtons)
|
loadFeature(InventoryButtons)
|
||||||
loadFeature(CompatibliltyFeatures)
|
loadFeature(CompatibliltyFeatures)
|
||||||
|
loadFeature(AnniversaryFeatures)
|
||||||
loadFeature(QuickCommands)
|
loadFeature(QuickCommands)
|
||||||
loadFeature(SaveCursorPosition)
|
loadFeature(SaveCursorPosition)
|
||||||
loadFeature(CustomSkyBlockTextures)
|
loadFeature(CustomSkyBlockTextures)
|
||||||
|
|||||||
@@ -10,12 +10,16 @@ package moe.nea.firmament.features.debug
|
|||||||
import net.minecraft.block.SkullBlock
|
import net.minecraft.block.SkullBlock
|
||||||
import net.minecraft.block.entity.SkullBlockEntity
|
import net.minecraft.block.entity.SkullBlockEntity
|
||||||
import net.minecraft.component.DataComponentTypes
|
import net.minecraft.component.DataComponentTypes
|
||||||
|
import net.minecraft.entity.Entity
|
||||||
|
import net.minecraft.entity.LivingEntity
|
||||||
import net.minecraft.item.ItemStack
|
import net.minecraft.item.ItemStack
|
||||||
import net.minecraft.item.Items
|
import net.minecraft.item.Items
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import net.minecraft.util.hit.BlockHitResult
|
import net.minecraft.util.hit.BlockHitResult
|
||||||
|
import net.minecraft.util.hit.EntityHitResult
|
||||||
import net.minecraft.util.hit.HitResult
|
import net.minecraft.util.hit.HitResult
|
||||||
import moe.nea.firmament.annotations.Subscribe
|
import moe.nea.firmament.annotations.Subscribe
|
||||||
|
import moe.nea.firmament.events.CommandEvent
|
||||||
import moe.nea.firmament.events.CustomItemModelEvent
|
import moe.nea.firmament.events.CustomItemModelEvent
|
||||||
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
|
import moe.nea.firmament.events.HandledScreenKeyPressedEvent
|
||||||
import moe.nea.firmament.events.ItemTooltipEvent
|
import moe.nea.firmament.events.ItemTooltipEvent
|
||||||
@@ -41,6 +45,7 @@ object PowerUserTools : FirmamentFeature {
|
|||||||
val copyTexturePackId by keyBindingWithDefaultUnbound("copy-texture-pack-id")
|
val copyTexturePackId by keyBindingWithDefaultUnbound("copy-texture-pack-id")
|
||||||
val copyNbtData by keyBindingWithDefaultUnbound("copy-nbt-data")
|
val copyNbtData by keyBindingWithDefaultUnbound("copy-nbt-data")
|
||||||
val copySkullTexture by keyBindingWithDefaultUnbound("copy-skull-texture")
|
val copySkullTexture by keyBindingWithDefaultUnbound("copy-skull-texture")
|
||||||
|
val copyEntityData by keyBindingWithDefaultUnbound("entity-data")
|
||||||
}
|
}
|
||||||
|
|
||||||
override val config
|
override val config
|
||||||
@@ -65,6 +70,37 @@ object PowerUserTools : FirmamentFeature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun debugFormat(itemStack: ItemStack): Text {
|
||||||
|
return Text.literal(itemStack.skyBlockId?.toString() ?: itemStack.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onEntityInfo(event: WorldKeyboardEvent) {
|
||||||
|
if (!event.matches(TConfig.copyEntityData)) return
|
||||||
|
val target = (MC.instance.crosshairTarget as? EntityHitResult)?.entity
|
||||||
|
if (target == null) {
|
||||||
|
MC.sendChat(Text.translatable("firmament.poweruser.entity.fail"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
showEntity(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showEntity(target: Entity) {
|
||||||
|
MC.sendChat(Text.translatable("firmament.poweruser.entity.type", target.type))
|
||||||
|
MC.sendChat(Text.translatable("firmament.poweruser.entity.name", target.name))
|
||||||
|
if (target is LivingEntity) {
|
||||||
|
MC.sendChat(Text.translatable("firmament.poweruser.entity.armor"))
|
||||||
|
for (armorItem in target.armorItems) {
|
||||||
|
MC.sendChat(Text.translatable("firmament.poweruser.entity.armor.item", debugFormat(armorItem)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MC.sendChat(Text.stringifiedTranslatable("firmament.poweruser.entity.passengers", target.passengerList.size))
|
||||||
|
target.passengerList.forEach {
|
||||||
|
showEntity(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
fun copyInventoryInfo(it: HandledScreenKeyPressedEvent) {
|
fun copyInventoryInfo(it: HandledScreenKeyPressedEvent) {
|
||||||
if (it.screen !is AccessorHandledScreen) return
|
if (it.screen !is AccessorHandledScreen) return
|
||||||
|
|||||||
@@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.features.events.anniversity
|
||||||
|
|
||||||
|
import io.github.notenoughupdates.moulconfig.observer.ObservableList
|
||||||
|
import io.github.notenoughupdates.moulconfig.xml.Bind
|
||||||
|
import moe.nea.jarvis.api.Point
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
import net.minecraft.entity.passive.PigEntity
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import moe.nea.firmament.annotations.Subscribe
|
||||||
|
import moe.nea.firmament.events.EntityInteractionEvent
|
||||||
|
import moe.nea.firmament.events.ProcessChatEvent
|
||||||
|
import moe.nea.firmament.events.TickEvent
|
||||||
|
import moe.nea.firmament.events.WorldReadyEvent
|
||||||
|
import moe.nea.firmament.features.FirmamentFeature
|
||||||
|
import moe.nea.firmament.gui.config.ManagedConfig
|
||||||
|
import moe.nea.firmament.gui.hud.MoulConfigHud
|
||||||
|
import moe.nea.firmament.rei.SBItemEntryDefinition
|
||||||
|
import moe.nea.firmament.repo.ItemNameLookup
|
||||||
|
import moe.nea.firmament.util.MC
|
||||||
|
import moe.nea.firmament.util.SHORT_NUMBER_FORMAT
|
||||||
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
import moe.nea.firmament.util.TimeMark
|
||||||
|
import moe.nea.firmament.util.parseShortNumber
|
||||||
|
import moe.nea.firmament.util.useMatch
|
||||||
|
|
||||||
|
object AnniversaryFeatures : FirmamentFeature {
|
||||||
|
override val identifier: String
|
||||||
|
get() = "anniversary"
|
||||||
|
|
||||||
|
object TConfig : ManagedConfig(identifier) {
|
||||||
|
val enableShinyPigTracker by toggle("shiny-pigs") {true}
|
||||||
|
val trackPigCooldown by position("pig-hud", 200, 300) { Point(0.1, 0.2) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override val config: ManagedConfig?
|
||||||
|
get() = TConfig
|
||||||
|
|
||||||
|
data class ClickedPig(
|
||||||
|
val clickedAt: TimeMark,
|
||||||
|
val startLocation: BlockPos,
|
||||||
|
val pigEntity: PigEntity
|
||||||
|
) {
|
||||||
|
@Bind("timeLeft")
|
||||||
|
fun getTimeLeft(): Double = 1 - clickedAt.passedTime() / pigDuration
|
||||||
|
}
|
||||||
|
|
||||||
|
val clickedPigs = ObservableList<ClickedPig>(mutableListOf())
|
||||||
|
var lastClickedPig: PigEntity? = null
|
||||||
|
|
||||||
|
val pigDuration = 90.seconds
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onTick(event: TickEvent) {
|
||||||
|
clickedPigs.removeIf { it.clickedAt.passedTime() > pigDuration }
|
||||||
|
}
|
||||||
|
|
||||||
|
val pattern = "SHINY! You extracted (?<reward>.*) from the piglet's orb!".toPattern()
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onChat(event: ProcessChatEvent) {
|
||||||
|
if(!TConfig.enableShinyPigTracker)return
|
||||||
|
if (event.unformattedString == "Oink! Bring the pig back to the Shiny Orb!") {
|
||||||
|
val pig = lastClickedPig ?: return
|
||||||
|
// TODO: store proper location based on the orb location, maybe
|
||||||
|
val startLocation = pig.blockPos ?: return
|
||||||
|
clickedPigs.add(ClickedPig(TimeMark.now(), startLocation, pig))
|
||||||
|
lastClickedPig = null
|
||||||
|
}
|
||||||
|
if (event.unformattedString == "SHINY! The orb is charged! Click on it for loot!") {
|
||||||
|
val player = MC.player ?: return
|
||||||
|
val lowest =
|
||||||
|
clickedPigs.minByOrNull { it.startLocation.getSquaredDistance(player.pos) } ?: return
|
||||||
|
clickedPigs.remove(lowest)
|
||||||
|
}
|
||||||
|
pattern.useMatch(event.unformattedString) {
|
||||||
|
val reward = group("reward")
|
||||||
|
val parsedReward = parseReward(reward)
|
||||||
|
addReward(parsedReward)
|
||||||
|
PigCooldown.rewards.atOnce {
|
||||||
|
PigCooldown.rewards.clear()
|
||||||
|
rewards.mapTo(PigCooldown.rewards) { PigCooldown.DisplayReward(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addReward(reward: Reward) {
|
||||||
|
val it = rewards.listIterator()
|
||||||
|
while (it.hasNext()) {
|
||||||
|
val merged = reward.mergeWith(it.next()) ?: continue
|
||||||
|
it.set(merged)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rewards.add(reward)
|
||||||
|
}
|
||||||
|
|
||||||
|
val rewards = mutableListOf<Reward>()
|
||||||
|
|
||||||
|
fun <T> ObservableList<T>.atOnce(block: () -> Unit) {
|
||||||
|
val oldObserver = observer
|
||||||
|
observer = null
|
||||||
|
block()
|
||||||
|
observer = oldObserver
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface Reward {
|
||||||
|
fun mergeWith(other: Reward): Reward?
|
||||||
|
data class EXP(val amount: Double, val skill: String) : Reward {
|
||||||
|
override fun mergeWith(other: Reward): Reward? {
|
||||||
|
if (other is EXP && other.skill == skill)
|
||||||
|
return EXP(amount + other.amount, skill)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Coins(val amount: Double) : Reward {
|
||||||
|
override fun mergeWith(other: Reward): Reward? {
|
||||||
|
if (other is Coins)
|
||||||
|
return Coins(other.amount + amount)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Items(val amount: Int, val item: SkyblockId) : Reward {
|
||||||
|
override fun mergeWith(other: Reward): Reward? {
|
||||||
|
if (other is Items && other.item == item)
|
||||||
|
return Items(amount + other.amount, item)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Unknown(val text: String) : Reward {
|
||||||
|
override fun mergeWith(other: Reward): Reward? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val expReward = "\\+(?<exp>$SHORT_NUMBER_FORMAT) (?<kind>[^ ]+) XP".toPattern()
|
||||||
|
val coinReward = "\\+(?<amount>$SHORT_NUMBER_FORMAT) coins".toPattern()
|
||||||
|
val itemReward = "(?:(?<amount>[0-9]+)x )?(?<name>.*)".toPattern()
|
||||||
|
fun parseReward(string: String): Reward {
|
||||||
|
expReward.useMatch<Unit>(string) {
|
||||||
|
val exp = parseShortNumber(group("exp"))
|
||||||
|
val kind = group("kind")
|
||||||
|
return Reward.EXP(exp, kind)
|
||||||
|
}
|
||||||
|
coinReward.useMatch<Unit>(string) {
|
||||||
|
val coins = parseShortNumber(group("amount"))
|
||||||
|
return Reward.Coins(coins)
|
||||||
|
}
|
||||||
|
itemReward.useMatch(string) {
|
||||||
|
val amount = group("amount")?.toIntOrNull() ?: 1
|
||||||
|
val name = group("name")
|
||||||
|
val item = ItemNameLookup.guessItemByName(name, false) ?: return@useMatch
|
||||||
|
return Reward.Items(amount, item)
|
||||||
|
}
|
||||||
|
return Reward.Unknown(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onWorldClear(event: WorldReadyEvent) {
|
||||||
|
lastClickedPig = null
|
||||||
|
clickedPigs.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun onEntityClick(event: EntityInteractionEvent) {
|
||||||
|
if (event.entity is PigEntity) {
|
||||||
|
lastClickedPig = event.entity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
fun init(event: WorldReadyEvent) {
|
||||||
|
PigCooldown.forceInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
object PigCooldown : MoulConfigHud("anniversary_pig", TConfig.trackPigCooldown) {
|
||||||
|
override fun shouldRender(): Boolean {
|
||||||
|
return clickedPigs.isNotEmpty() && TConfig.enableShinyPigTracker
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bind("pigs")
|
||||||
|
fun getPigs() = clickedPigs
|
||||||
|
|
||||||
|
class DisplayReward(val backedBy: Reward) {
|
||||||
|
@Bind
|
||||||
|
fun count(): String {
|
||||||
|
return when (backedBy) {
|
||||||
|
is Reward.Coins -> backedBy.amount
|
||||||
|
is Reward.EXP -> backedBy.amount
|
||||||
|
is Reward.Items -> backedBy.amount
|
||||||
|
is Reward.Unknown -> 0
|
||||||
|
}.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
val itemStack = if (backedBy is Reward.Items) {
|
||||||
|
SBItemEntryDefinition.getEntry(backedBy.item, backedBy.amount)
|
||||||
|
} else {
|
||||||
|
SBItemEntryDefinition.getEntry(SkyblockId.NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bind
|
||||||
|
fun name(): String {
|
||||||
|
return when (backedBy) {
|
||||||
|
is Reward.Coins -> "Coins"
|
||||||
|
is Reward.EXP -> backedBy.skill
|
||||||
|
is Reward.Items -> itemStack.value.asItemStack().name.string
|
||||||
|
is Reward.Unknown -> backedBy.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bind
|
||||||
|
fun isKnown() = backedBy !is Reward.Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Bind("rewards")
|
||||||
|
val rewards = ObservableList<DisplayReward>(mutableListOf())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -58,7 +58,7 @@ class BarComponent(
|
|||||||
ScreenDrawing.texturedRect(context, x, y, 4, 8, texture, emptyColor.color)
|
ScreenDrawing.texturedRect(context, x, y, 4, 8, texture, emptyColor.color)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val increasePerPixel = (sectionEnd - sectionStart / 4)
|
val increasePerPixel = (sectionEnd - sectionStart) / width
|
||||||
var valueAtPixel = sectionStart
|
var valueAtPixel = sectionStart
|
||||||
for (i in (0 until width)) {
|
for (i in (0 until width)) {
|
||||||
ScreenDrawing.texturedRect(
|
ScreenDrawing.texturedRect(
|
||||||
|
|||||||
@@ -30,11 +30,16 @@ abstract class MoulConfigHud(
|
|||||||
|
|
||||||
private var fragment: GuiContext? = null
|
private var fragment: GuiContext? = null
|
||||||
|
|
||||||
|
fun forceInit() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
open fun shouldRender(): Boolean {
|
open fun shouldRender(): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
require(name.matches("^[a-z_/]+$".toRegex()))
|
||||||
HudRenderEvent.subscribe {
|
HudRenderEvent.subscribe {
|
||||||
if (!shouldRender()) return@subscribe
|
if (!shouldRender()) return@subscribe
|
||||||
val renderContext = componentWrapper.createContext(it.context)
|
val renderContext = componentWrapper.createContext(it.context)
|
||||||
|
|||||||
103
src/main/kotlin/moe/nea/firmament/repo/ItemNameLookup.kt
Normal file
103
src/main/kotlin/moe/nea/firmament/repo/ItemNameLookup.kt
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.repo
|
||||||
|
|
||||||
|
import io.github.moulberry.repo.IReloadable
|
||||||
|
import io.github.moulberry.repo.NEURepository
|
||||||
|
import io.github.moulberry.repo.data.NEUItem
|
||||||
|
import java.util.NavigableMap
|
||||||
|
import java.util.TreeMap
|
||||||
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
import moe.nea.firmament.util.removeColorCodes
|
||||||
|
import moe.nea.firmament.util.skyblockId
|
||||||
|
|
||||||
|
object ItemNameLookup : IReloadable {
|
||||||
|
|
||||||
|
fun getItemNameChunks(name: String): Set<String> {
|
||||||
|
return name.removeColorCodes().split(" ").filterTo(mutableSetOf()) { it.isNotBlank() }
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameMap: NavigableMap<String, out Set<SkyblockId>> = TreeMap()
|
||||||
|
|
||||||
|
override fun reload(repository: NEURepository) {
|
||||||
|
val nameMap = TreeMap<String, MutableSet<SkyblockId>>()
|
||||||
|
repository.items.items.values.forEach { item ->
|
||||||
|
getAllNamesForItem(item).forEach { name ->
|
||||||
|
val chunks = getItemNameChunks(name)
|
||||||
|
chunks.forEach { chunk ->
|
||||||
|
val set = nameMap.getOrPut(chunk, ::mutableSetOf)
|
||||||
|
set.add(item.skyblockId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.nameMap = nameMap
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllNamesForItem(item: NEUItem): Set<String> {
|
||||||
|
val names = mutableSetOf<String>()
|
||||||
|
names.add(item.displayName)
|
||||||
|
if (item.displayName.contains("Enchanted Book")) {
|
||||||
|
val enchantName = item.lore.firstOrNull()
|
||||||
|
if (enchantName != null) {
|
||||||
|
names.add(enchantName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findItemCandidatesByName(name: String): MutableSet<SkyblockId> {
|
||||||
|
val candidates = mutableSetOf<SkyblockId>()
|
||||||
|
for (chunk in getItemNameChunks(name)) {
|
||||||
|
val set = nameMap[chunk] ?: emptySet()
|
||||||
|
candidates.addAll(set)
|
||||||
|
}
|
||||||
|
return candidates
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun guessItemByName(
|
||||||
|
/**
|
||||||
|
* The display name of the item. Color codes will be ignored.
|
||||||
|
*/
|
||||||
|
name: String,
|
||||||
|
/**
|
||||||
|
* Whether the [name] may contain other text, such as reforges, master stars and such.
|
||||||
|
*/
|
||||||
|
mayBeMangled: Boolean
|
||||||
|
): SkyblockId? {
|
||||||
|
val cleanName = name.removeColorCodes()
|
||||||
|
return findBestItemFromCandidates(
|
||||||
|
findItemCandidatesByName(cleanName),
|
||||||
|
cleanName,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findBestItemFromCandidates(
|
||||||
|
candidates: Iterable<SkyblockId>,
|
||||||
|
name: String, mayBeMangled: Boolean
|
||||||
|
): SkyblockId? {
|
||||||
|
val expectedClean = name.removeColorCodes()
|
||||||
|
var bestMatch: SkyblockId? = null
|
||||||
|
var bestMatchLength = -1
|
||||||
|
for (candidate in candidates) {
|
||||||
|
val item = RepoManager.getNEUItem(candidate) ?: continue
|
||||||
|
for (name in getAllNamesForItem(item)) {
|
||||||
|
val actualClean = name.removeColorCodes()
|
||||||
|
val matches = if (mayBeMangled) expectedClean == actualClean
|
||||||
|
else expectedClean.contains(actualClean)
|
||||||
|
if (!matches) continue
|
||||||
|
if (actualClean.length > bestMatchLength) {
|
||||||
|
bestMatch = candidate
|
||||||
|
bestMatchLength = actualClean.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -63,6 +63,7 @@ object RepoManager {
|
|||||||
val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply {
|
val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply {
|
||||||
registerReloadListener(ItemCache)
|
registerReloadListener(ItemCache)
|
||||||
registerReloadListener(ExpLadders)
|
registerReloadListener(ExpLadders)
|
||||||
|
registerReloadListener(ItemNameLookup)
|
||||||
registerReloadListener {
|
registerReloadListener {
|
||||||
Firmament.coroutineScope.launch(MinecraftDispatcher) {
|
Firmament.coroutineScope.launch(MinecraftDispatcher) {
|
||||||
if (!trySendClientboundUpdateRecipesPacket()) {
|
if (!trySendClientboundUpdateRecipesPacket()) {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import org.w3c.dom.Element
|
|||||||
import moe.nea.firmament.gui.BarComponent
|
import moe.nea.firmament.gui.BarComponent
|
||||||
|
|
||||||
object MoulConfigUtils {
|
object MoulConfigUtils {
|
||||||
val firmUrl = "http://nea.moe/Firmament"
|
val firmUrl = "http://firmament.nea.moe/moulconfig"
|
||||||
val universe = XMLUniverse.getDefaultUniverse().also { uni ->
|
val universe = XMLUniverse.getDefaultUniverse().also { uni ->
|
||||||
uni.registerMapper(java.awt.Color::class.java) {
|
uni.registerMapper(java.awt.Color::class.java) {
|
||||||
if (it.startsWith("#")) {
|
if (it.startsWith("#")) {
|
||||||
|
|||||||
35
src/main/resources/assets/firmament/gui/anniversary_pig.xml
Normal file
35
src/main/resources/assets/firmament/gui/anniversary_pig.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
-->
|
||||||
|
|
||||||
|
<Root xmlns="http://notenoughupdates.org/moulconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://notenoughupdates.org/moulconfig https://raw.githubusercontent.com/NotEnoughUpdates/MoulConfig/master/MoulConfig.xsd"
|
||||||
|
xmlns:firm="http://firmament.nea.moe/moulconfig"
|
||||||
|
>
|
||||||
|
<Column>
|
||||||
|
<Array data="@pigs">
|
||||||
|
<Row>
|
||||||
|
<Text text="Pig: "/>
|
||||||
|
<firm:Bar progress="@timeLeft" total="1" fillColor="#ffb6c1" emptyColor="#db7093"/>
|
||||||
|
</Row>
|
||||||
|
</Array>
|
||||||
|
<Text text="Profits:"/>
|
||||||
|
<Array data="@rewards">
|
||||||
|
<When condition="@isKnown">
|
||||||
|
<Row>
|
||||||
|
<Text text="@count"/>
|
||||||
|
<Text text="x "/>
|
||||||
|
<Text text="@name"/>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Text text="Unknown reward: "/>
|
||||||
|
<Text text="@name"/>
|
||||||
|
</Row>
|
||||||
|
</When>
|
||||||
|
</Array>
|
||||||
|
</Column>
|
||||||
|
</Root>
|
||||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<Root xmlns="http://notenoughupdates.org/moulconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<Root xmlns="http://notenoughupdates.org/moulconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns:firm="http://nea.moe/Firmament"
|
xmlns:firm="http://firmament.nea.moe/moulconfig"
|
||||||
xsi:schemaLocation="http://notenoughupdates.org/moulconfig https://raw.githubusercontent.com/NotEnoughUpdates/MoulConfig/master/MoulConfig.xsd">
|
xsi:schemaLocation="http://notenoughupdates.org/moulconfig https://raw.githubusercontent.com/NotEnoughUpdates/MoulConfig/master/MoulConfig.xsd">
|
||||||
<Column>
|
<Column>
|
||||||
<Row>
|
<Row>
|
||||||
|
|||||||
@@ -13,6 +13,15 @@
|
|||||||
"firmament.command.waypoint.remove.error": "Could not find waypoint with that index to delete.",
|
"firmament.command.waypoint.remove.error": "Could not find waypoint with that index to delete.",
|
||||||
"firmament.command.waypoint.skip.error": "Could not skip a waypoint. Are you in ordered waypoint mode with waypoints loaded?",
|
"firmament.command.waypoint.skip.error": "Could not skip a waypoint. Are you in ordered waypoint mode with waypoints loaded?",
|
||||||
"firmament.command.waypoint.skip": "Skipped 1 waypoint",
|
"firmament.command.waypoint.skip": "Skipped 1 waypoint",
|
||||||
|
"firmament.poweruser.entity.fail": "No entity found under cursor",
|
||||||
|
"firmament.poweruser.entity.type": "Entity Type: %s",
|
||||||
|
"firmament.poweruser.entity.name": "Entity Name: %s",
|
||||||
|
"firmament.poweruser.entity.armor": "Entity Armor:",
|
||||||
|
"firmament.poweruser.entity.armor.item": " - %s",
|
||||||
|
"firmament.poweruser.entity.passengers": "%s Passengers",
|
||||||
|
"firmament.config.anniversary": "Anniversary Features",
|
||||||
|
"firmament.config.anniversary.shiny-pigs": "Shiny Pigs Tracker",
|
||||||
|
"firmament.config.anniversary.pig-hud": "Pig Tracker Hud",
|
||||||
"firmament.pristine-profit.collection": "Collection: %s/h",
|
"firmament.pristine-profit.collection": "Collection: %s/h",
|
||||||
"firmament.pristine-profit.money": "Money: %s/h",
|
"firmament.pristine-profit.money": "Money: %s/h",
|
||||||
"firmament.toggle.true": "On",
|
"firmament.toggle.true": "On",
|
||||||
@@ -156,6 +165,7 @@
|
|||||||
"firmament.modapi.event": "Received mod API event: %s",
|
"firmament.modapi.event": "Received mod API event: %s",
|
||||||
"firmament.config.power-user.copy-texture-pack-id": "Copy Texture Pack Id",
|
"firmament.config.power-user.copy-texture-pack-id": "Copy Texture Pack Id",
|
||||||
"firmament.config.power-user.copy-skull-texture": "Copy Placed Skull Id",
|
"firmament.config.power-user.copy-skull-texture": "Copy Placed Skull Id",
|
||||||
|
"firmament.config.power-user.entity-data": "Show Entity Data",
|
||||||
"firmament.config.power-user.copy-nbt-data": "Copy NBT data",
|
"firmament.config.power-user.copy-nbt-data": "Copy NBT data",
|
||||||
"firmament.config.power-user": "Power Users",
|
"firmament.config.power-user": "Power Users",
|
||||||
"firmament.tooltip.skyblockid": "SkyBlock Id: %s",
|
"firmament.tooltip.skyblockid": "SkyBlock Id: %s",
|
||||||
|
|||||||
Reference in New Issue
Block a user