diff options
Diffstat (limited to 'src/main/kotlin/features/events')
4 files changed, 297 insertions, 232 deletions
diff --git a/src/main/kotlin/features/events/anniversity/AnniversaryFeatures.kt b/src/main/kotlin/features/events/anniversity/AnniversaryFeatures.kt index 5151862..955d23b 100644 --- a/src/main/kotlin/features/events/anniversity/AnniversaryFeatures.kt +++ b/src/main/kotlin/features/events/anniversity/AnniversaryFeatures.kt @@ -1,224 +1,224 @@ - 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 org.joml.Vector2i import kotlin.time.Duration.Companion.seconds -import net.minecraft.entity.passive.PigEntity -import net.minecraft.util.math.BlockPos +import net.minecraft.world.entity.animal.Pig +import net.minecraft.network.chat.Component +import net.minecraft.core.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.repo.ExpensiveItemCacheApi import moe.nea.firmament.repo.ItemNameLookup import moe.nea.firmament.repo.SBItemStack 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.data.Config +import moe.nea.firmament.util.data.ManagedConfig 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, Category.EVENTS) { - val enableShinyPigTracker by toggle("shiny-pigs") {true} - val trackPigCooldown by position("pig-hud", 200, 300) { Point(0.1, 0.2) } - } +object AnniversaryFeatures { + val identifier: String + get() = "anniversary" - override val config: ManagedConfig? - get() = TConfig + @Config + object TConfig : ManagedConfig(identifier, Category.EVENTS) { + val enableShinyPigTracker by toggle("shiny-pigs") { true } + val trackPigCooldown by position("pig-hud", 200, 300) { Vector2i(100, 200) } + } - data class ClickedPig( + 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) { - SBItemStack(backedBy.item, backedBy.amount) - } else { - SBItemStack(SkyblockId.NULL) - } - - @Bind - fun name(): String { - return when (backedBy) { - is Reward.Coins -> "Coins" - is Reward.EXP -> backedBy.skill - is Reward.Items -> itemStack.asImmutableItemStack().name.string - is Reward.Unknown -> backedBy.text - } - } - - @Bind - fun isKnown() = backedBy !is Reward.Unknown - } - - @get:Bind("rewards") - val rewards = ObservableList<DisplayReward>(mutableListOf()) - - } + val pigEntity: Pig + ) { + @Bind("timeLeft") + fun getTimeLeft(): Double = 1 - clickedAt.passedTime() / pigDuration + } + + val clickedPigs = ObservableList<ClickedPig>(mutableListOf()) + var lastClickedPig: Pig? = 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.blockPosition() ?: 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.distToCenterSqr(player.position) } ?: 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 = "(?i)\\+(?<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 Pig) { + 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) { + SBItemStack(backedBy.item, backedBy.amount) + } else { + SBItemStack(SkyblockId.NULL) + } + + @OptIn(ExpensiveItemCacheApi::class) + @Bind + fun name(): Component { + return when (backedBy) { + is Reward.Coins -> Component.literal("Coins") + is Reward.EXP -> Component.literal(backedBy.skill) + is Reward.Items -> itemStack.asImmutableItemStack().hoverName + is Reward.Unknown -> Component.literal(backedBy.text) + } + } + + @Bind + fun isKnown() = backedBy !is Reward.Unknown + } + + @get:Bind("rewards") + val rewards = ObservableList<DisplayReward>(mutableListOf()) + + } } diff --git a/src/main/kotlin/features/events/anniversity/CenturyRaffleFeatures.kt b/src/main/kotlin/features/events/anniversity/CenturyRaffleFeatures.kt new file mode 100644 index 0000000..9b87cc6 --- /dev/null +++ b/src/main/kotlin/features/events/anniversity/CenturyRaffleFeatures.kt @@ -0,0 +1,65 @@ +package moe.nea.firmament.features.events.anniversity + +import java.util.Optional +import me.shedaniel.math.Color +import kotlin.jvm.optionals.getOrNull +import net.minecraft.world.entity.player.Player +import net.minecraft.network.chat.Style +import net.minecraft.ChatFormatting +import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.EntityRenderTintEvent +import moe.nea.firmament.util.MC +import moe.nea.firmament.util.SkyblockId +import moe.nea.firmament.util.data.Config +import moe.nea.firmament.util.data.ManagedConfig +import moe.nea.firmament.util.render.TintedOverlayTexture +import moe.nea.firmament.util.skyBlockId +import moe.nea.firmament.util.skyblock.SkyBlockItems + +object CenturyRaffleFeatures { + @Config + object TConfig : ManagedConfig("centuryraffle", Category.EVENTS) { + val highlightPlayersForSlice by toggle("highlight-cake-players") { true } +// val highlightAllPlayers by toggle("highlight-all-cake-players") { true } + } + + val cakeIcon = "⛃" + + val cakeColors = listOf( + CakeTeam(SkyBlockItems.SLICE_OF_BLUEBERRY_CAKE, ChatFormatting.BLUE), + CakeTeam(SkyBlockItems.SLICE_OF_CHEESECAKE, ChatFormatting.YELLOW), + CakeTeam(SkyBlockItems.SLICE_OF_GREEN_VELVET_CAKE, ChatFormatting.GREEN), + CakeTeam(SkyBlockItems.SLICE_OF_RED_VELVET_CAKE, ChatFormatting.RED), + CakeTeam(SkyBlockItems.SLICE_OF_STRAWBERRY_SHORTCAKE, ChatFormatting.LIGHT_PURPLE), + ) + + data class CakeTeam( + val id: SkyblockId, + val formatting: ChatFormatting, + ) { + val searchedTextRgb = formatting.color!! + val brightenedRgb = Color.ofOpaque(searchedTextRgb)//.brighter(2.0) + val tintOverlay by lazy { + TintedOverlayTexture().setColor(brightenedRgb) + } + } + + val sliceToColor = cakeColors.associateBy { it.id } + + @Subscribe + fun onEntityRender(event: EntityRenderTintEvent) { + if (!TConfig.highlightPlayersForSlice) return + val requestedCakeTeam = sliceToColor[MC.stackInHand?.skyBlockId] ?: return + // TODO: cache the requested color + val player = event.entity as? Player ?: return + val cakeColor: Style = player.feedbackDisplayName.visit( + { style, text -> + if (text == cakeIcon) Optional.of(style) + else Optional.empty() + }, Style.EMPTY).getOrNull() ?: return + if (cakeColor.color?.value == requestedCakeTeam.searchedTextRgb) { + event.renderState.overlayTexture_firmament = requestedCakeTeam.tintOverlay + } + } + +} diff --git a/src/main/kotlin/features/events/carnival/CarnivalFeatures.kt b/src/main/kotlin/features/events/carnival/CarnivalFeatures.kt index 840fb8c..3f149ff 100644 --- a/src/main/kotlin/features/events/carnival/CarnivalFeatures.kt +++ b/src/main/kotlin/features/events/carnival/CarnivalFeatures.kt @@ -1,17 +1,16 @@ package moe.nea.firmament.features.events.carnival -import moe.nea.firmament.features.FirmamentFeature -import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.util.data.Config +import moe.nea.firmament.util.data.ManagedConfig -object CarnivalFeatures : FirmamentFeature { - object TConfig : ManagedConfig(identifier, Category.EVENTS) { +object CarnivalFeatures { + @Config + object TConfig : ManagedConfig(identifier, Category.EVENTS) { val enableBombSolver by toggle("bombs-solver") { true } val displayTutorials by toggle("tutorials") { true } } - override val config: ManagedConfig? - get() = TConfig - override val identifier: String + val identifier: String get() = "carnival" } diff --git a/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt b/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt index 1824225..64b3814 100644 --- a/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt +++ b/src/main/kotlin/features/events/carnival/MinesweeperHelper.kt @@ -2,17 +2,17 @@ package moe.nea.firmament.features.events.carnival import io.github.notenoughupdates.moulconfig.observer.ObservableList -import io.github.notenoughupdates.moulconfig.platform.ModernItemStack +import io.github.notenoughupdates.moulconfig.platform.MoulConfigPlatform import io.github.notenoughupdates.moulconfig.xml.Bind import java.util.UUID -import net.minecraft.block.Blocks -import net.minecraft.item.Item -import net.minecraft.item.ItemStack -import net.minecraft.item.Items -import net.minecraft.text.ClickEvent -import net.minecraft.text.Text -import net.minecraft.util.math.BlockPos -import net.minecraft.world.WorldAccess +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraft.network.chat.ClickEvent +import net.minecraft.network.chat.Component +import net.minecraft.core.BlockPos +import net.minecraft.world.level.LevelAccessor import moe.nea.firmament.annotations.Subscribe import moe.nea.firmament.commands.thenExecute import moe.nea.firmament.events.AttackBlockEvent @@ -45,7 +45,6 @@ object MinesweeperHelper { enum class Piece( - @get:Bind("fruitName") val fruitName: String, val points: Int, val specialAbility: String, @@ -118,24 +117,26 @@ object MinesweeperHelper { val textureUrl = "http://textures.minecraft.net/texture/$textureHash" val itemStack = createSkullItem(UUID.randomUUID(), textureUrl) .setSkyBlockFirmamentUiId("MINESWEEPER_$name") + @get:Bind("fruitName") + val textFruitName = Component.literal(fruitName) @Bind - fun getIcon() = ModernItemStack.of(itemStack) + fun getIcon() = MoulConfigPlatform.wrap(itemStack) - @Bind - fun pieceLabel() = fruitColor.formattingCode + fruitName + @get:Bind("pieceLabel") + val pieceLabel = Component.literal(fruitColor.formattingCode + fruitName) - @Bind - fun boardLabel() = "§a$totalPerBoard§7/§rboard" + @get:Bind("boardLabel") + val boardLabel = Component.literal("§a$totalPerBoard§7/§rboard") - @Bind("description") - fun getDescription() = buildString { + @get:Bind("description") + val getDescription = Component.literal(buildString { append(specialAbility) if (points >= 0) { append(" Default points: $points.") } } - } +) } object TutorialScreen { @get:Bind("pieces") @@ -158,7 +159,7 @@ object MinesweeperHelper { ; @Bind("itemType") - fun getItemStack() = ModernItemStack.of(ItemStack(itemType)) + fun getItemStack() = MoulConfigPlatform.wrap(ItemStack(itemType)) companion object { val id = SkyblockId("CARNIVAL_SHOVEL") @@ -175,10 +176,10 @@ object MinesweeperHelper { ) { fun toBlockPos() = BlockPos(sandBoxLow.x + x, sandBoxLow.y, sandBoxLow.z + y) - fun getBlock(world: WorldAccess) = world.getBlockState(toBlockPos()).block - fun isUnopened(world: WorldAccess) = getBlock(world) == Blocks.SAND - fun isOpened(world: WorldAccess) = getBlock(world) == Blocks.SANDSTONE - fun isScorched(world: WorldAccess) = getBlock(world) == Blocks.SANDSTONE_STAIRS + fun getBlock(world: LevelAccessor) = world.getBlockState(toBlockPos()).block + fun isUnopened(world: LevelAccessor) = getBlock(world) == Blocks.SAND + fun isOpened(world: LevelAccessor) = getBlock(world) == Blocks.SANDSTONE + fun isScorched(world: LevelAccessor) = getBlock(world) == Blocks.SANDSTONE_STAIRS companion object { fun fromBlockPos(blockPos: BlockPos): BoardPosition? { @@ -221,8 +222,8 @@ object MinesweeperHelper { @Subscribe fun onChat(event: ProcessChatEvent) { if (CarnivalFeatures.TConfig.displayTutorials && event.unformattedString == startGameQuestion) { - MC.sendChat(Text.translatable("firmament.carnival.tutorial.minesweeper").styled { - it.withClickEvent(ClickEvent(ClickEvent.Action.RUN_COMMAND, "/firm minesweepertutorial")) + MC.sendChat(Component.translatable("firmament.carnival.tutorial.minesweeper").withStyle { + it.withClickEvent(ClickEvent.RunCommand("/firm minesweepertutorial")) }) } if (!CarnivalFeatures.TConfig.enableBombSolver) { @@ -259,7 +260,7 @@ object MinesweeperHelper { val boardPosition = BoardPosition.fromBlockPos(event.blockPos) log.log { "Breaking block at ${event.blockPos} ($boardPosition)" } gs.lastClickedPosition = boardPosition - gs.lastDowsingMode = DowsingMode.fromItem(event.player.inventory.mainHandStack) + gs.lastDowsingMode = DowsingMode.fromItem(event.player.mainHandItem) } @Subscribe @@ -267,7 +268,7 @@ object MinesweeperHelper { val gs = gameState ?: return RenderInWorldContext.renderInWorld(event) { for ((pos, bombCount) in gs.nearbyBombs) { - this.text(pos.toBlockPos().up().toCenterPos(), Text.literal("§a$bombCount \uD83D\uDCA3")) + this.text(pos.toBlockPos().above().center, Component.literal("§a$bombCount \uD83D\uDCA3")) } } } |
