From 88cb9468b4432f68d1197f512f68c951fdbdf3dd Mon Sep 17 00:00:00 2001 From: nea Date: Tue, 30 May 2023 22:47:56 +0200 Subject: Forge recipes and coin items --- .../moe/nea/firmament/recipes/SBForgeRecipe.kt | 36 +++++++++---- .../moe/nea/firmament/rei/SBItemEntryDefinition.kt | 15 +++--- src/main/kotlin/moe/nea/firmament/rei/math.kt | 8 +++ .../kotlin/moe/nea/firmament/repo/ItemCache.kt | 61 +++++++++++++++++++--- .../kotlin/moe/nea/firmament/util/SkyblockId.kt | 1 + .../moe/nea/firmament/util/item/SkullItemData.kt | 39 ++++++++++++++ .../firmament/util/json/DashlessUUIDSerializer.kt | 24 +++++++++ .../firmament/util/json/InstantAsLongSerializer.kt | 20 +++++++ src/main/kotlin/moe/nea/firmament/util/uuid.kt | 10 ++++ .../resources/assets/firmament/lang/en_us.json | 3 +- 10 files changed, 193 insertions(+), 24 deletions(-) create mode 100644 src/main/kotlin/moe/nea/firmament/rei/math.kt create mode 100644 src/main/kotlin/moe/nea/firmament/util/item/SkullItemData.kt create mode 100644 src/main/kotlin/moe/nea/firmament/util/json/DashlessUUIDSerializer.kt create mode 100644 src/main/kotlin/moe/nea/firmament/util/json/InstantAsLongSerializer.kt create mode 100644 src/main/kotlin/moe/nea/firmament/util/uuid.kt (limited to 'src') diff --git a/src/main/kotlin/moe/nea/firmament/recipes/SBForgeRecipe.kt b/src/main/kotlin/moe/nea/firmament/recipes/SBForgeRecipe.kt index 9f13cca..d70ebcd 100644 --- a/src/main/kotlin/moe/nea/firmament/recipes/SBForgeRecipe.kt +++ b/src/main/kotlin/moe/nea/firmament/recipes/SBForgeRecipe.kt @@ -27,10 +27,14 @@ import me.shedaniel.rei.api.client.gui.widgets.Widgets import me.shedaniel.rei.api.client.registry.display.DisplayCategory import me.shedaniel.rei.api.common.category.CategoryIdentifier import me.shedaniel.rei.api.common.util.EntryStacks +import kotlin.math.cos +import kotlin.math.sin +import kotlin.time.Duration.Companion.seconds import net.minecraft.block.Blocks import net.minecraft.text.Text import moe.nea.firmament.Firmament import moe.nea.firmament.rei.SBItemEntryDefinition +import moe.nea.firmament.rei.plus class SBForgeRecipe(override val neuRecipe: NEUForgeRecipe) : SBRecipe() { override fun getCategoryIdentifier(): CategoryIdentifier<*> = Category.categoryIdentifier @@ -41,27 +45,39 @@ class SBForgeRecipe(override val neuRecipe: NEUForgeRecipe) : SBRecipe() { override fun getTitle(): Text = Text.literal("Forge Recipes") override fun getDisplayHeight(): Int { - return super.getDisplayHeight() + return 104 } override fun getIcon(): Renderer = EntryStacks.of(Blocks.ANVIL) override fun setupDisplay(display: SBForgeRecipe, bounds: Rectangle): List { return buildList { - // TODO: proper gui for this (possibly inspired by the old circular gui) add(Widgets.createRecipeBase(bounds)) - val resultSlot = Point(bounds.centerX, bounds.centerY + 5) - add(Widgets.createResultSlotBackground(resultSlot)) - val ingredientsCenter = Point(bounds.centerX, bounds.centerY - 20) + add(Widgets.createResultSlotBackground(Point(bounds.minX + 124, bounds.minY + 46))) + val arrow = Widgets.createArrow(Point(bounds.minX + 90, bounds.minY + 54 - 18 / 2)) + add(arrow) + add(Widgets.createTooltip(arrow.bounds, Text.translatable("firmament.recipe.forge.time", display.neuRecipe.duration.seconds))) + val ingredientsCenter = Point(bounds.minX + 49 - 8, bounds.minY + 54 - 8) val count = display.neuRecipe.inputs.size - display.neuRecipe.inputs.forEachIndexed { idx, ingredient -> + if (count == 1) { add( - Widgets.createSlot( - Point(ingredientsCenter.x + 12 - count * 24 / 2 + idx * 24, ingredientsCenter.y) - ).markInput().entry(SBItemEntryDefinition.getEntry(ingredient)) + Widgets.createSlot(Point(ingredientsCenter.x, ingredientsCenter.y)).markInput() + .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.inputs.single())) ) + } else { + display.neuRecipe.inputs.forEachIndexed { idx, ingredient -> + val rad = Math.PI * 2 * idx / count + add( + Widgets.createSlot( + Point( + cos(rad) * 30, + sin(rad) * 30, + ) + ingredientsCenter + ).markInput().entry(SBItemEntryDefinition.getEntry(ingredient)) + ) + } } add( - Widgets.createSlot(resultSlot).markOutput().disableBackground() + Widgets.createSlot(Point(bounds.minX + 124, bounds.minY + 46)).markOutput().disableBackground() .entry(SBItemEntryDefinition.getEntry(display.neuRecipe.outputStack)) ) } diff --git a/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt b/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt index 1e5ae4c..ee6b673 100644 --- a/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt +++ b/src/main/kotlin/moe/nea/firmament/rei/SBItemEntryDefinition.kt @@ -33,6 +33,7 @@ import net.minecraft.registry.tag.TagKey import net.minecraft.text.Text import net.minecraft.util.Identifier import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry +import moe.nea.firmament.repo.ItemCache import moe.nea.firmament.repo.ItemCache.asItemStack import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.util.SkyblockId @@ -43,15 +44,17 @@ data class SBItemStack( val skyblockId: SkyblockId, val neuItem: NEUItem?, val stackSize: Int, -) +) { + fun asItemStack(): ItemStack? { + if (skyblockId == SkyblockId.COINS) + return ItemCache.coinItem(stackSize) + return neuItem.asItemStack(idHint = skyblockId).copyWithCount(stackSize) + } +} object SBItemEntryDefinition : EntryDefinition { override fun equals(o1: SBItemStack, o2: SBItemStack, context: ComparisonContext): Boolean { - if (!context.isFuzzy) { - if (o1.stackSize != o2.stackSize) - return false - } - return o1.skyblockId == o2.skyblockId + return o1.skyblockId == o2.skyblockId && o1.stackSize == o2.stackSize } override fun cheatsAs(entry: EntryStack?, value: SBItemStack): ItemStack { diff --git a/src/main/kotlin/moe/nea/firmament/rei/math.kt b/src/main/kotlin/moe/nea/firmament/rei/math.kt new file mode 100644 index 0000000..e3504a1 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/rei/math.kt @@ -0,0 +1,8 @@ +package moe.nea.firmament.rei + +import me.shedaniel.math.Point + +operator fun Point.plus(other: Point): Point = Point( + this.x + other.x, + this.y + other.y, +) diff --git a/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt b/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt index ab6034f..5452d33 100644 --- a/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt +++ b/src/main/kotlin/moe/nea/firmament/repo/ItemCache.kt @@ -18,29 +18,38 @@ package moe.nea.firmament.repo +import com.mojang.authlib.GameProfile +import com.mojang.authlib.minecraft.MinecraftProfileTexture import com.mojang.serialization.Dynamic import io.github.cottonmc.cotton.gui.client.CottonHud import io.github.moulberry.repo.IReloadable import io.github.moulberry.repo.NEURepository import io.github.moulberry.repo.data.NEUItem +import java.text.NumberFormat +import java.util.UUID import java.util.concurrent.ConcurrentHashMap import org.apache.logging.log4j.LogManager import kotlinx.coroutines.Job import kotlinx.coroutines.launch import net.minecraft.SharedConstants +import net.minecraft.block.entity.SkullBlockEntity import net.minecraft.client.resource.language.I18n import net.minecraft.datafixer.Schemas import net.minecraft.datafixer.TypeReferences import net.minecraft.item.ItemStack import net.minecraft.item.Items import net.minecraft.nbt.NbtCompound +import net.minecraft.nbt.NbtElement +import net.minecraft.nbt.NbtHelper import net.minecraft.nbt.NbtOps import net.minecraft.text.Text import moe.nea.firmament.Firmament -import moe.nea.firmament.rei.SBItemStack import moe.nea.firmament.util.LegacyTagParser import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.appendLore +import moe.nea.firmament.util.item.MinecraftProfileTextureKt +import moe.nea.firmament.util.item.MinecraftTexturesPayloadKt +import moe.nea.firmament.util.item.setTextures import moe.nea.firmament.util.skyblockId object ItemCache : IReloadable { @@ -73,7 +82,7 @@ object ItemCache : IReloadable { fun brokenItemStack(neuItem: NEUItem?, idHint: SkyblockId? = null): ItemStack { return ItemStack(Items.PAINTING).apply { - setCustomName(Text.literal(neuItem?.displayName ?: idHint?.toString() ?: "null")) + setCustomName(Text.literal(neuItem?.displayName ?: idHint?.neuItem ?: "null")) appendLore(listOf(Text.translatable("firmament.repo.brokenitem", neuItem?.skyblockItemId ?: idHint))) } } @@ -94,11 +103,6 @@ object ItemCache : IReloadable { } } - fun SBItemStack.asItemStack(): ItemStack { - return this.neuItem.asItemStack(idHint = this.skyblockId) - .let { if (this.stackSize != 1) it.copyWithCount(this.stackSize) else it } - } - fun NEUItem?.asItemStack(idHint: SkyblockId? = null): ItemStack { if (this == null) return brokenItemStack(null, idHint) var s = cache[this.skyblockItemId] @@ -139,4 +143,47 @@ object ItemCache : IReloadable { CottonHud.remove(RepoManager.progressBar) } } + + fun coinItem(coinAmount: Int): ItemStack { + var uuid = UUID.fromString("2070f6cb-f5db-367a-acd0-64d39a7e5d1b") + var texture = + "http://textures.minecraft.net/texture/538071721cc5b4cd406ce431a13f86083a8973e1064d2f8897869930ee6e5237" + if (coinAmount >= 100000) { + uuid = UUID.fromString("94fa2455-2881-31fe-bb4e-e3e24d58dbe3") + texture = + "http://textures.minecraft.net/texture/c9b77999fed3a2758bfeaf0793e52283817bea64044bf43ef29433f954bb52f6" + } + if (coinAmount >= 10000000) { + uuid = UUID.fromString("0af8df1f-098c-3b72-ac6b-65d65fd0b668") + texture = + "http://textures.minecraft.net/texture/7b951fed6a7b2cbc2036916dec7a46c4a56481564d14f945b6ebc03382766d3b" + } + val itemStack = ItemStack(Items.PLAYER_HEAD) + itemStack.setCustomName(Text.literal("§r§6" + NumberFormat.getInstance().format(coinAmount) + " Coins")) + val nbt: NbtCompound = itemStack.orCreateNbt + nbt[SkullBlockEntity.SKULL_OWNER_KEY] = NbtHelper.writeGameProfile( + NbtCompound(), + GameProfile(uuid, "CoolGuy123").also { + it.setTextures( + MinecraftTexturesPayloadKt( + mapOf( + MinecraftProfileTexture.Type.SKIN to MinecraftProfileTextureKt(texture), + ), + uuid, + "CoolGuy123" + ) + ) + } + ) + return itemStack + } +} + + +operator fun NbtCompound.set(key: String, value: String) { + putString(key, value) +} + +operator fun NbtCompound.set(key: String, value: NbtElement) { + put(key, value) } diff --git a/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt b/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt index f0998a7..b3c0e9d 100644 --- a/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt +++ b/src/main/kotlin/moe/nea/firmament/util/SkyblockId.kt @@ -56,6 +56,7 @@ value class SkyblockId(val neuItem: String) { } companion object { + val COINS: SkyblockId = SkyblockId("SKYBLOCK_COIN") private val bazaarEnchantmentRegex = "ENCHANTMENT_(\\D*)_(\\d+)".toRegex() val NULL: SkyblockId = SkyblockId("null") } diff --git a/src/main/kotlin/moe/nea/firmament/util/item/SkullItemData.kt b/src/main/kotlin/moe/nea/firmament/util/item/SkullItemData.kt new file mode 100644 index 0000000..ad9d388 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/util/item/SkullItemData.kt @@ -0,0 +1,39 @@ +@file:UseSerializers(DashlessUUIDSerializer::class, InstantAsLongSerializer::class) + +package moe.nea.firmament.util.item + +import com.mojang.authlib.GameProfile +import com.mojang.authlib.minecraft.MinecraftProfileTexture +import com.mojang.authlib.properties.Property +import java.util.UUID +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers +import kotlinx.serialization.encodeToString +import net.minecraft.client.texture.PlayerSkinProvider +import moe.nea.firmament.Firmament +import moe.nea.firmament.util.json.DashlessUUIDSerializer +import moe.nea.firmament.util.json.InstantAsLongSerializer + +@Serializable +data class MinecraftProfileTextureKt( + val url: String, + val metadata: Map = mapOf(), +) + +@Serializable +data class MinecraftTexturesPayloadKt( + val textures: Map, + val profileId: UUID, + val profileName: String, + val isPublic: Boolean = true, + val timestamp: Instant = Clock.System.now(), +) + +fun GameProfile.setTextures(textures: MinecraftTexturesPayloadKt) { + val json = Firmament.json.encodeToString(textures) + val encoded = java.util.Base64.getEncoder().encodeToString(json.encodeToByteArray()) + properties.put(PlayerSkinProvider.TEXTURES, Property(PlayerSkinProvider.TEXTURES, encoded)) +} + diff --git a/src/main/kotlin/moe/nea/firmament/util/json/DashlessUUIDSerializer.kt b/src/main/kotlin/moe/nea/firmament/util/json/DashlessUUIDSerializer.kt new file mode 100644 index 0000000..c95b343 --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/util/json/DashlessUUIDSerializer.kt @@ -0,0 +1,24 @@ +package moe.nea.firmament.util.json + +import java.util.UUID +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import moe.nea.firmament.util.parseDashlessUUID + +object DashlessUUIDSerializer : KSerializer { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("DashlessUUIDSerializer", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): UUID { + return parseDashlessUUID(decoder.decodeString()) + } + + override fun serialize(encoder: Encoder, value: UUID) { + encoder.encodeString(value.toString().replace("-", "")) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/util/json/InstantAsLongSerializer.kt b/src/main/kotlin/moe/nea/firmament/util/json/InstantAsLongSerializer.kt new file mode 100644 index 0000000..988c7dd --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/util/json/InstantAsLongSerializer.kt @@ -0,0 +1,20 @@ +package moe.nea.firmament.util.json + +import kotlinx.datetime.Instant +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +object InstantAsLongSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("InstantAsLongSerializer", PrimitiveKind.LONG) + override fun deserialize(decoder: Decoder): Instant { + return Instant.fromEpochMilliseconds(decoder.decodeLong()) + } + + override fun serialize(encoder: Encoder, value: Instant) { + encoder.encodeLong(value.toEpochMilliseconds()) + } +} diff --git a/src/main/kotlin/moe/nea/firmament/util/uuid.kt b/src/main/kotlin/moe/nea/firmament/util/uuid.kt new file mode 100644 index 0000000..d5409fa --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/util/uuid.kt @@ -0,0 +1,10 @@ +package moe.nea.firmament.util + +import java.math.BigInteger +import java.util.UUID + +fun parseDashlessUUID(dashlessUuid: String): UUID { + val most = BigInteger(dashlessUuid.substring(0, 16), 16) + val least = BigInteger(dashlessUuid.substring(16, 32), 16) + return UUID(most.toLong(), least.toLong()) +} diff --git a/src/main/resources/assets/firmament/lang/en_us.json b/src/main/resources/assets/firmament/lang/en_us.json index 440d923..541724f 100644 --- a/src/main/resources/assets/firmament/lang/en_us.json +++ b/src/main/resources/assets/firmament/lang/en_us.json @@ -35,5 +35,6 @@ "firmament.config.fishing-warning.highlight-wake-chain": "Highlight fishing particles", "firmament.key.slotlocking": "Lock Slot / Slot Binding", "firmament.key.category": "Firmament", - "firmament.protectitem": "Firmament protected your item: " + "firmament.protectitem": "Firmament protected your item: ", + "firmament.recipe.forge.time": "Forging Time: %s" } -- cgit