From f99ed19d9ce04eb706ccb2d76d7dc91d340f4c0a Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Thu, 6 Oct 2022 23:41:08 +0700 Subject: [PATCH] Atlas variant for skin element Fixes #99 --- gradle.properties | 2 +- .../mc/otm/OverdriveThatMatters.java | 2 + .../mc/otm/android/AndroidResearchType.kt | 31 +- .../mc/otm/client/ClientTickHandler.kt | 10 + .../otm/client/render/AbstractSkinElement.kt | 255 ++++++++++++++++ .../mc/otm/client/render/AtlasSkinElement.kt | 148 ++++++++++ .../mc/otm/client/render/ResearchIcons.kt | 98 +++---- .../mc/otm/client/render/SkinElement.kt | 271 +----------------- .../render/StretchingRectangleElement.kt | 127 ++++++++ .../mc/otm/client/render/WidgetAtlasHolder.kt | 78 +++++ .../otm/client/screen/AndroidStationScreen.kt | 2 +- .../ru/dbotthepony/mc/otm/core/TimerQueue.kt | 15 + .../resources/META-INF/accesstransformer.cfg | 2 + .../textures/gui/android_stuff-0.png | Bin 0 -> 383 bytes .../textures/gui/android_stuff-1.png | Bin 0 -> 408 bytes .../textures/gui/android_stuff-10.png | Bin 0 -> 407 bytes .../textures/gui/android_stuff-11.png | Bin 0 -> 427 bytes .../textures/gui/android_stuff-12.png | Bin 0 -> 376 bytes .../textures/gui/android_stuff-13.png | Bin 0 -> 411 bytes .../textures/gui/android_stuff-14.png | Bin 0 -> 375 bytes .../textures/gui/android_stuff-15.png | Bin 0 -> 399 bytes .../textures/gui/android_stuff-16.png | Bin 0 -> 402 bytes .../textures/gui/android_stuff-17.png | Bin 0 -> 387 bytes .../textures/gui/android_stuff-18.png | Bin 0 -> 383 bytes .../textures/gui/android_stuff-19.png | Bin 0 -> 383 bytes .../textures/gui/android_stuff-2.png | Bin 0 -> 382 bytes .../textures/gui/android_stuff-20.png | Bin 0 -> 399 bytes .../textures/gui/android_stuff-21.png | Bin 0 -> 395 bytes .../textures/gui/android_stuff-22.png | Bin 0 -> 385 bytes .../textures/gui/android_stuff-23.png | Bin 0 -> 407 bytes .../textures/gui/android_stuff-24.png | Bin 0 -> 372 bytes .../textures/gui/android_stuff-3.png | Bin 0 -> 370 bytes .../textures/gui/android_stuff-4.png | Bin 0 -> 386 bytes .../textures/gui/android_stuff-5.png | Bin 0 -> 384 bytes .../textures/gui/android_stuff-6.png | Bin 0 -> 377 bytes .../textures/gui/android_stuff-7.png | Bin 0 -> 414 bytes .../textures/gui/android_stuff-8.png | Bin 0 -> 376 bytes .../textures/gui/android_stuff-9.png | Bin 0 -> 399 bytes .../textures/gui/android_upgrades.png | Bin 2551 -> 0 bytes 39 files changed, 719 insertions(+), 322 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AbstractSkinElement.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/render/StretchingRectangleElement.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-0.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-1.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-10.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-11.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-12.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-13.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-14.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-15.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-16.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-17.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-18.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-19.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-2.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-20.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-21.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-22.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-23.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-24.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-3.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-4.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-5.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-6.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-7.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-8.png create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-9.png delete mode 100644 src/main/resources/assets/overdrive_that_matters/textures/gui/android_upgrades.png diff --git a/gradle.properties b/gradle.properties index afaef7038..e80d6a0ff 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Sets default memory used for gradle commands. Can be overridden by user or command line properties. # This is required to provide enough memory for the Minecraft decompilation process. -org.gradle.jvmargs=-Xmx3G -XX:+UseParallelGC +org.gradle.jvmargs=-Xmx3G org.gradle.daemon=true kotlin.stdlib.default.dependency=false diff --git a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java index 2b4ddfdc7..83b9209de 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java +++ b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java @@ -31,6 +31,7 @@ import ru.dbotthepony.mc.otm.client.ClientTickHandlerKt; import ru.dbotthepony.mc.otm.client.MatteryGUI; import ru.dbotthepony.mc.otm.client.model.GravitationStabilizerModel; import ru.dbotthepony.mc.otm.client.model.TritaniumArmorModel; +import ru.dbotthepony.mc.otm.client.render.WidgetAtlasHolder; import ru.dbotthepony.mc.otm.compat.mekanism.QIOKt; import ru.dbotthepony.mc.otm.compat.mekanism.TooltipsKt; import ru.dbotthepony.mc.otm.core.ImpreciseFraction; @@ -102,6 +103,7 @@ public final class OverdriveThatMatters { modBus.addListener(EventPriority.NORMAL, AndroidAbilityKeyMapping.INSTANCE::register); modBus.addListener(EventPriority.NORMAL, TritaniumArmorModel::register); modBus.addListener(EventPriority.NORMAL, GravitationStabilizerModel::register); + modBus.addListener(EventPriority.NORMAL, WidgetAtlasHolder::register); }); ClientConfig.INSTANCE.register(); diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt index 10d6afeca..75a8b9342 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt @@ -7,6 +7,7 @@ import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import com.google.gson.JsonSyntaxException +import com.google.gson.internal.bind.TypeAdapters import net.minecraft.network.FriendlyByteBuf import net.minecraft.network.chat.Component import net.minecraft.network.chat.ComponentContents @@ -20,6 +21,7 @@ import net.minecraft.world.item.Items import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.level.ItemLike import net.minecraftforge.registries.ForgeRegistries +import ru.dbotthepony.mc.otm.client.render.AbstractSkinElement import ru.dbotthepony.mc.otm.client.render.SkinElement import ru.dbotthepony.mc.otm.core.ListSet import ru.dbotthepony.mc.otm.core.TranslatableComponent @@ -28,6 +30,7 @@ import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.core.set import ru.dbotthepony.mc.otm.core.toImmutableList import ru.dbotthepony.mc.otm.data.stream +import ru.dbotthepony.mc.otm.isClient import ru.dbotthepony.mc.otm.registry.MRegistry import java.util.LinkedList import java.util.stream.Stream @@ -88,16 +91,22 @@ class AndroidResearchType( val experienceLevels: Int = 0, private val customName: Component? = null, - // why pack these into research itself? - // because Minecraft itself does this - // check out advancements - val skinIcon: SkinElement? = null, + // ok + val skinIcon: JsonElement? = null, val itemIcon: Item? = null, iconText: Component? = null, ) { private val iconTextValue = iconText?.copy() val iconText get() = iconTextValue?.copy() + val resolvedSkinIcon by lazy { + check(isClient) { "Invalid realm" } + if (skinIcon != null) + AbstractSkinElement.fromJson(skinIcon) + else + null + } + data class Reference( val id: ResourceLocation, val isRigid: Boolean @@ -471,7 +480,7 @@ class AndroidResearchType( it["experience"] = JsonPrimitive(experienceLevels) if (skinIcon != null) { - it["skin_icon"] = skinIcon.toJson() + it["skin_icon"] = skinIcon } if (itemIcon != null) { @@ -511,7 +520,7 @@ class AndroidResearchType( if (iconTextValue != null) buff.writeComponent(iconTextValue) buff.writeBoolean(skinIcon != null) - skinIcon?.toNetwork(buff) + if (skinIcon != null) buff.writeUtf(skinIcon.toString()) buff.writeBoolean(itemIcon != null) if (itemIcon != null) buff.writeUtf(itemIcon.registryName!!.toString()) @@ -541,7 +550,7 @@ class AndroidResearchType( } val skinIcon = if (buff.readBoolean()) { - SkinElement.fromNetwork(buff) + TypeAdapters.JSON_ELEMENT.fromJson(buff.readUtf()) } else { null } @@ -582,7 +591,7 @@ class AndroidResearchType( val experience = value["experience"]?.asInt ?: 0 val customName = value["custom_name"]?.let(Component.Serializer::fromJson) val iconText = value["icon_text"]?.let(Component.Serializer::fromJson) - val skinIcon = value["skin_icon"]?.let(SkinElement.Companion::fromJson) + val skinIcon = value["skin_icon"] val itemIcon = value["item_icon"]?.let { ForgeRegistries.ITEMS.getValue(ResourceLocation(it.asString)).let { if (it == Items.AIR) null else it } } return AndroidResearchType( @@ -615,7 +624,7 @@ class AndroidResearchType( var description: MutableList? = null, var descriptionSuppliers: MutableList? = null, var itemIcon: Item? = null, - var skinIcon: SkinElement? = null, + var skinIcon: AbstractSkinElement? = null, var iconText: Component? = null, ) { private val items = ArrayList>() @@ -629,7 +638,7 @@ class AndroidResearchType( return this } - fun withIcon(icon: SkinElement? = null): Builder { + fun withIcon(icon: AbstractSkinElement? = null): Builder { this.skinIcon = icon this.itemIcon = null return this @@ -840,7 +849,7 @@ class AndroidResearchType( descriptionSuppliers = descriptionSuppliers ?: listOf(), experienceLevels = experience, customName = customName, - skinIcon = skinIcon, + skinIcon = skinIcon?.toJson(), itemIcon = itemIcon, iconText = iconText ).also { if (validate) it.validate() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientTickHandler.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientTickHandler.kt index f443f7539..d3eb21b76 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientTickHandler.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/ClientTickHandler.kt @@ -25,6 +25,16 @@ fun onceClientPre(ticker: ITickable) { preTickList.add(ticker, LOGGED_IN, "Not logged in") } +fun onceClient(inTicks: Int, ticker: Runnable) { + check(isClient) { "Illegal side" } + postTimerList.add(inTicks, ticker, LOGGED_IN, "Not logged in") +} + +fun onceClientPre(inTicks: Int, ticker: Runnable) { + check(isClient) { "Illegal side" } + preTimerList.add(inTicks, ticker, LOGGED_IN, "Not logged in") +} + fun tickClient(ticker: IConditionalTickable) { check(isClient) { "Illegal side" } postTickList.add(ticker, LOGGED_IN, "Not logged in") diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AbstractSkinElement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AbstractSkinElement.kt new file mode 100644 index 000000000..6b349f0d0 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AbstractSkinElement.kt @@ -0,0 +1,255 @@ +package ru.dbotthepony.mc.otm.client.render + +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import com.google.gson.JsonSerializationContext +import com.google.gson.JsonSerializer +import com.google.gson.JsonSyntaxException +import com.google.gson.TypeAdapter +import com.google.gson.internal.bind.TypeAdapters +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import com.mojang.blaze3d.systems.RenderSystem +import com.mojang.blaze3d.vertex.PoseStack +import net.minecraft.network.FriendlyByteBuf +import net.minecraft.resources.ResourceLocation +import ru.dbotthepony.mc.otm.core.linearInterpolation +import java.lang.reflect.Type + +sealed class AbstractSkinElement { + abstract val x: Float + abstract val y: Float + abstract val width: Float + abstract val height: Float + abstract val u0: Float + abstract val v0: Float + abstract val u1: Float + abstract val v1: Float + open val winding: UVWindingOrder get() = UVWindingOrder.NORMAL + + abstract val texture: ResourceLocation + + /** + * See [ru.dbotthepony.mc.otm.client.render.clearDepth] + */ + fun clearDepth( + stack: PoseStack, + x: Float = 0f, + y: Float = 0f, + width: Float = this.width, + height: Float = this.height, + ) = ru.dbotthepony.mc.otm.client.render.clearDepth(stack, x, y, width, height) + + @JvmOverloads + fun render( + stack: PoseStack, + x: Float = 0f, + y: Float = 0f, + width: Float = this.width, + height: Float = this.height, + winding: UVWindingOrder = this.winding + ) { + RenderSystem.setShaderTexture(0, texture) + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + + renderRaw(stack, x, y, width, height, winding) + } + + @JvmOverloads + fun render( + stack: PoseStack, + x: Double, + y: Double = 0.0, + width: Double = this.width.toDouble(), + height: Double = this.height.toDouble(), + winding: UVWindingOrder = this.winding + ) = render(stack, x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), winding) + + @JvmOverloads + fun renderPartial( + stack: PoseStack, + x: Float = 0f, + y: Float = 0f, + width: Float = this.width, + height: Float = this.height, + winding: UVWindingOrder = this.winding + ) { + RenderSystem.setShaderTexture(0, texture) + RenderSystem.enableBlend() + RenderSystem.defaultBlendFunc() + + renderRawPartial(stack, x, y, width, height, winding) + } + + @JvmOverloads + fun renderPartial( + stack: PoseStack, + x: Double, + y: Double = 0.0, + width: Double = this.width.toDouble(), + height: Double = this.height.toDouble(), + winding: UVWindingOrder = this.winding + ) = renderPartial(stack, x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), winding) + + @JvmOverloads + fun renderWidth( + stack: PoseStack, + x: Float = 0f, + y: Float = 0f, + width: Float = this.width, + winding: UVWindingOrder = this.winding + ) { + render(stack, x, y, width = width, winding = winding) + } + + @JvmOverloads + fun renderWidth( + stack: PoseStack, + x: Double, + y: Double = 0.0, + width: Double = this.width.toDouble(), + winding: UVWindingOrder = this.winding + ) = renderWidth(stack, x.toFloat(), y.toFloat(), width.toFloat(), winding) + + @JvmOverloads + fun renderHeight( + stack: PoseStack, + x: Float = 0f, + y: Float = 0f, + height: Float = this.height, + winding: UVWindingOrder = this.winding + ) { + render(stack, x, y, height = height, winding = winding) + } + + @JvmOverloads + fun renderHeight( + stack: PoseStack, + x: Double, + y: Double = 0.0, + height: Double = this.height.toDouble(), + winding: UVWindingOrder = this.winding + ) = renderHeight(stack, x.toFloat(), y.toFloat(), height.toFloat(), winding) + + + @JvmOverloads + fun renderRaw( + stack: PoseStack, + x: Float = 0f, + y: Float = 0f, + width: Float = this.width, + height: Float = this.height, + winding: UVWindingOrder = this.winding + ) { + val winded = winding.translate(u0, v0, u1, v1) + + drawTexturedRect( + stack, + x, + y, + width, + height, + winded, + ) + } + + @JvmOverloads + fun renderRaw( + stack: PoseStack, + x: Double, + y: Double = 0.0, + width: Double = this.width.toDouble(), + height: Double = this.height.toDouble(), + winding: UVWindingOrder = this.winding + ) = renderRaw(stack, x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), winding) + + @JvmOverloads + fun renderRawPartial( + stack: PoseStack, + x: Float = 0f, + y: Float = 0f, + width: Float = this.width, + height: Float = this.height, + winding: UVWindingOrder = this.winding + ) { + val u1 = (u0 + linearInterpolation(width / this.width, 0f, u1 - u0)).coerceAtLeast(0f).coerceAtMost(1f) + val v1 = (v0 + linearInterpolation(height / this.height, 0f, v1 - v0)).coerceAtLeast(0f).coerceAtMost(1f) + + val winded = winding.translate(u0, v0, u1, v1) + + drawTexturedRect( + stack, + x, + y, + width, + height, + winded, + ) + } + + @JvmOverloads + fun renderRawPartial( + stack: PoseStack, + x: Double, + y: Double = 0.0, + width: Double = this.width.toDouble(), + height: Double = this.height.toDouble(), + winding: UVWindingOrder = this.winding + ) = renderRawPartial(stack, x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), winding) + + abstract fun toJson(): JsonElement + abstract fun toNetwork(buff: FriendlyByteBuf) + + companion object : TypeAdapter(), JsonSerializer, JsonDeserializer { + fun toNetwork(value: AbstractSkinElement, buff: FriendlyByteBuf) { + buff.writeBoolean(value is AtlasSkinElement) + value.toNetwork(buff) + } + + fun fromNetwork(buff: FriendlyByteBuf): AbstractSkinElement { + if (buff.readBoolean()) { + return AtlasSkinElement.fromNetwork(buff) + } + + return SkinElement.fromNetwork(buff) + } + + fun fromJson(json: JsonElement): AbstractSkinElement { + if (json is JsonPrimitive) { + return AtlasSkinElement.fromJson(json) + } else if (json is JsonObject) { + return SkinElement.fromJson(json) + } else { + throw JsonSyntaxException("Unknown element type ${json::class.qualifiedName}") + } + } + + override fun write(out: JsonWriter, value: AbstractSkinElement) { + TypeAdapters.JSON_ELEMENT.write(out, value.toJson()) + } + + override fun read(`in`: JsonReader): AbstractSkinElement { + return fromJson(TypeAdapters.JSON_ELEMENT.read(`in`)) + } + + override fun serialize( + src: AbstractSkinElement, + typeOfSrc: Type, + context: JsonSerializationContext + ): JsonElement { + return src.toJson() + } + + override fun deserialize( + json: JsonElement, + typeOfT: Type, + context: JsonDeserializationContext + ): AbstractSkinElement { + return fromJson(json) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt new file mode 100644 index 000000000..671c8aa31 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt @@ -0,0 +1,148 @@ +package ru.dbotthepony.mc.otm.client.render + +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import com.google.gson.JsonPrimitive +import com.google.gson.JsonSerializationContext +import com.google.gson.JsonSerializer +import com.google.gson.TypeAdapter +import com.google.gson.internal.bind.TypeAdapters +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter +import net.minecraft.client.renderer.texture.TextureAtlasSprite +import net.minecraft.network.FriendlyByteBuf +import net.minecraft.resources.ResourceLocation +import ru.dbotthepony.mc.otm.client.onceClient +import ru.dbotthepony.mc.otm.isClient +import java.lang.reflect.Type +import java.util.stream.Stream + +fun AtlasSkinElement(location: ResourceLocation) = AtlasSkinElement.create(location) + +class AtlasSkinElement private constructor( + val location: ResourceLocation, + marker: Any? +) : AbstractSkinElement() { + private var changeset = -1 + private var _textureAtlasSprite: TextureAtlasSprite? = null + + private val textureAtlasSpriteImpl: TextureAtlasSprite get(): TextureAtlasSprite { + check(isClient) { "Invalid realm" } + val _textureAtlasSprite = _textureAtlasSprite + + if (_textureAtlasSprite == null || changeset != WidgetAtlasHolder.INSTANCE.changeset) { + val get = WidgetAtlasHolder.INSTANCE.getSprite(location) + this._textureAtlasSprite = get + changeset = WidgetAtlasHolder.INSTANCE.changeset + return get + } + + return _textureAtlasSprite + } + + val textureAtlasSprite: TextureAtlasSprite get(): TextureAtlasSprite { + check(isWidgetAtlasAvailable) { "Atlas is not available; either we are not a client, or we are running datagen" } + return textureAtlasSpriteImpl + } + + override val x get() = textureAtlasSprite.x.toFloat() + override val y get() = textureAtlasSprite.y.toFloat() + override val width get() = textureAtlasSprite.width.toFloat() + override val height get() = textureAtlasSprite.height.toFloat() + + override val u0 get() = textureAtlasSprite.u0 + override val v0 get() = textureAtlasSprite.v0 + override val u1 get() = textureAtlasSprite.u1 + override val v1 get() = textureAtlasSprite.v1 + override val texture: ResourceLocation get() = textureAtlasSprite.atlas().location() + + override fun equals(other: Any?): Boolean { + if (other is AtlasSkinElement) + return location == other.location + + return super.equals(other) + } + + override fun hashCode(): Int { + return WidgetAtlasHolder.INSTANCE.hashCode() xor location.hashCode() + } + + override fun toString(): String { + return "AtlasSkinElement[$location]" + } + + override fun toJson(): JsonElement { + return JsonPrimitive(location.toString()) + } + + override fun toNetwork(buff: FriendlyByteBuf) { + buff.writeResourceLocation(location) + } + + companion object : TypeAdapter(), JsonSerializer, JsonDeserializer { + private val skinElementCache = HashMap() + + val keys: Stream get() = skinElementCache.keys.stream() + + private fun queueRebuild() { + WidgetAtlasHolder.INSTANCE.queueRebuild() + } + + private fun createImpl(location: ResourceLocation): AtlasSkinElement { + var element = skinElementCache[location] + + if (element == null) { + element = AtlasSkinElement(location, null) + skinElementCache[location] = element + + if (isWidgetAtlasAvailable) { + queueRebuild() + } + } + + return element + } + + @JvmStatic + fun create(location: ResourceLocation): AtlasSkinElement { + if (!isWidgetAtlasAvailable) { + return AtlasSkinElement(location, null) + } + + return createImpl(location) + } + + fun fromNetwork(buff: FriendlyByteBuf): AtlasSkinElement { + return create(buff.readResourceLocation()) + } + + fun fromJson(element: JsonElement): AtlasSkinElement { + return create(ResourceLocation.tryParse(element.asString) ?: ResourceLocation("missing")) + } + + override fun write(out: JsonWriter, value: AtlasSkinElement) { + TypeAdapters.JSON_ELEMENT.write(out, value.toJson()) + } + + override fun read(`in`: JsonReader): AtlasSkinElement { + return fromJson(TypeAdapters.JSON_ELEMENT.read(`in`)) + } + + override fun serialize( + src: AtlasSkinElement, + typeOfSrc: Type, + context: JsonSerializationContext + ): JsonElement { + return src.toJson() + } + + override fun deserialize( + json: JsonElement, + typeOfT: Type, + context: JsonDeserializationContext + ): AtlasSkinElement { + return fromJson(json) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ResearchIcons.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ResearchIcons.kt index 0ead968cb..e31000426 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ResearchIcons.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ResearchIcons.kt @@ -4,64 +4,60 @@ import net.minecraft.resources.ResourceLocation import ru.dbotthepony.mc.otm.OverdriveThatMatters object ResearchIcons { - val ICONS = ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/gui/android_upgrades.png") - val ICON_TRANSFER: SkinElement - val ICON_ATTACK_BOOST: SkinElement - val ICON_PLASMA_SHIELD_BOOST: SkinElement - val ICON_CLOAK: SkinElement - val ICON_GRAVITATIONAL_STABILIZER: SkinElement - val ICON_AIR_BAGS: SkinElement - val ICON_JUMP_BOOST: SkinElement + val ICON_TRANSFER: AbstractSkinElement + val ICON_ATTACK_BOOST: AbstractSkinElement + val ICON_PLASMA_SHIELD_BOOST: AbstractSkinElement + val ICON_CLOAK: AbstractSkinElement + val ICON_GRAVITATIONAL_STABILIZER: AbstractSkinElement + val ICON_AIR_BAGS: AbstractSkinElement + val ICON_JUMP_BOOST: AbstractSkinElement - val ICON_FEATHER_FALLING: SkinElement - val ICON_ITEM_MAGNET: SkinElement - val ICON_ARROW: SkinElement - val ICON_ARMOR: SkinElement - val ICON_NANOBOTS: SkinElement - val ICON_NIGHT_VISION: SkinElement - val ICON_OXYGEN_SUPPLY: SkinElement + val ICON_FEATHER_FALLING: AbstractSkinElement + val ICON_ITEM_MAGNET: AbstractSkinElement + val ICON_ARROW: AbstractSkinElement + val ICON_ARMOR: AbstractSkinElement + val ICON_NANOBOTS: AbstractSkinElement + val ICON_NIGHT_VISION: AbstractSkinElement + val ICON_OXYGEN_SUPPLY: AbstractSkinElement - val ICON_PLASMA_SHIELD: SkinElement - val ICON_SHOCKWAVE: SkinElement - val ICON_LIMB_OVERCLOCKING: SkinElement - val ICON_STEP_ASSIST: SkinElement - val ICON_ENDER_TELEPORT: SkinElement - val ICON_WIRELESS_CHARGING: SkinElement - val ICON_UNKNOWN: SkinElement + val ICON_PLASMA_SHIELD: AbstractSkinElement + val ICON_SHOCKWAVE: AbstractSkinElement + val ICON_LIMB_OVERCLOCKING: AbstractSkinElement + val ICON_STEP_ASSIST: AbstractSkinElement + val ICON_ENDER_TELEPORT: AbstractSkinElement + val ICON_WIRELESS_CHARGING: AbstractSkinElement + val ICON_UNKNOWN: AbstractSkinElement - val ICON_EXTENDED_REACH: SkinElement - val ICON_PHANTOM_ATTRACTOR: SkinElement + val ICON_EXTENDED_REACH: AbstractSkinElement + val ICON_PHANTOM_ATTRACTOR: AbstractSkinElement val KOT = ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/block/ph_kitty.png").element(0f, 0f, 32f, 32f, 32f, 32f) init { - val grid = SkinGrid(ICONS, 18f, 18f, 7, 7) + var i = 0 - ICON_TRANSFER = grid.next() - ICON_ATTACK_BOOST = grid.next() - ICON_PLASMA_SHIELD_BOOST = grid.next() - ICON_CLOAK = grid.next() - ICON_GRAVITATIONAL_STABILIZER = grid.next() - ICON_AIR_BAGS = grid.next() - ICON_JUMP_BOOST = grid.next() - - ICON_FEATHER_FALLING = grid.next() - ICON_ITEM_MAGNET = grid.next() - ICON_ARROW = grid.next() - ICON_ARMOR = grid.next() - ICON_NANOBOTS = grid.next() - ICON_NIGHT_VISION = grid.next() - ICON_OXYGEN_SUPPLY = grid.next() - - ICON_PLASMA_SHIELD = grid.next() - ICON_SHOCKWAVE = grid.next() - ICON_LIMB_OVERCLOCKING = grid.next() - ICON_STEP_ASSIST = grid.next() - ICON_ENDER_TELEPORT = grid.next() - ICON_WIRELESS_CHARGING = grid.next() - ICON_UNKNOWN = grid.next() - - ICON_EXTENDED_REACH = grid.next() - ICON_PHANTOM_ATTRACTOR = grid.next() + ICON_TRANSFER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_ATTACK_BOOST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_PLASMA_SHIELD_BOOST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_CLOAK = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_GRAVITATIONAL_STABILIZER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_AIR_BAGS = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_JUMP_BOOST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_FEATHER_FALLING = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_ITEM_MAGNET = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_ARROW = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_ARMOR = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_NANOBOTS = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_NIGHT_VISION = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_OXYGEN_SUPPLY = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_PLASMA_SHIELD = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_SHOCKWAVE = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_LIMB_OVERCLOCKING = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_STEP_ASSIST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_ENDER_TELEPORT = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_WIRELESS_CHARGING = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_UNKNOWN = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_EXTENDED_REACH = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) + ICON_PHANTOM_ATTRACTOR = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_stuff-${i++}")) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElement.kt index 526f76608..8f9a39c81 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElement.kt @@ -12,11 +12,8 @@ import com.google.gson.TypeAdapter import com.google.gson.internal.bind.TypeAdapters import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonWriter -import com.mojang.blaze3d.systems.RenderSystem -import com.mojang.blaze3d.vertex.PoseStack import net.minecraft.network.FriendlyByteBuf import net.minecraft.resources.ResourceLocation -import ru.dbotthepony.mc.otm.client.screen.panels.DockProperty import ru.dbotthepony.mc.otm.core.set import java.lang.reflect.Type @@ -54,16 +51,16 @@ fun ResourceLocation.vLine( @Suppress("unused") data class SkinElement @JvmOverloads constructor( - val texture: ResourceLocation, - val x: Float, - val y: Float, - val width: Float, - val height: Float, + override val texture: ResourceLocation, + override val x: Float, + override val y: Float, + override val width: Float, + override val height: Float, val imageWidth: Float = 256f, val imageHeight: Float = 256f, - val winding: UVWindingOrder = UVWindingOrder.NORMAL -) { - fun toJson(): JsonObject { + override val winding: UVWindingOrder = UVWindingOrder.NORMAL, +) : AbstractSkinElement() { + override fun toJson(): JsonObject { return JsonObject().also { it["texture"] = JsonPrimitive(texture.toString()) it["x"] = JsonPrimitive(x) @@ -76,7 +73,7 @@ data class SkinElement @JvmOverloads constructor( } } - fun toNetwork(buff: FriendlyByteBuf) { + override fun toNetwork(buff: FriendlyByteBuf) { buff.writeUtf(texture.toString()) buff.writeFloat(x) buff.writeFloat(y) @@ -149,250 +146,8 @@ data class SkinElement @JvmOverloads constructor( require(imageHeight > 0f) { "Invalid image height $imageHeight" } } - /** - * See [ru.dbotthepony.mc.otm.client.render.clearDepth] - */ - fun clearDepth( - stack: PoseStack, - x: Float = 0f, - y: Float = 0f, - width: Float = this.width, - height: Float = this.height, - ) = ru.dbotthepony.mc.otm.client.render.clearDepth(stack, x, y, width, height) - - @JvmOverloads - fun render( - stack: PoseStack, - x: Float = 0f, - y: Float = 0f, - width: Float = this.width, - height: Float = this.height, - winding: UVWindingOrder = this.winding - ) { - RenderSystem.setShaderTexture(0, texture) - RenderSystem.enableBlend() - RenderSystem.defaultBlendFunc() - - renderRaw(stack, x, y, width, height, winding) - } - - @JvmOverloads - fun render( - stack: PoseStack, - x: Double, - y: Double = 0.0, - width: Double = this.width.toDouble(), - height: Double = this.height.toDouble(), - winding: UVWindingOrder = this.winding - ) = render(stack, x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), winding) - - @JvmOverloads - fun renderPartial( - stack: PoseStack, - x: Float = 0f, - y: Float = 0f, - width: Float = this.width, - height: Float = this.height, - winding: UVWindingOrder = this.winding - ) { - RenderSystem.setShaderTexture(0, texture) - RenderSystem.enableBlend() - RenderSystem.defaultBlendFunc() - - renderRawPartial(stack, x, y, width, height, winding) - } - - @JvmOverloads - fun renderPartial( - stack: PoseStack, - x: Double, - y: Double = 0.0, - width: Double = this.width.toDouble(), - height: Double = this.height.toDouble(), - winding: UVWindingOrder = this.winding - ) = renderPartial(stack, x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), winding) - - @JvmOverloads - fun renderWidth( - stack: PoseStack, - x: Float = 0f, - y: Float = 0f, - width: Float = this.width, - winding: UVWindingOrder = this.winding - ) { - render(stack, x, y, width = width, winding = winding) - } - - @JvmOverloads - fun renderWidth( - stack: PoseStack, - x: Double, - y: Double = 0.0, - width: Double = this.width.toDouble(), - winding: UVWindingOrder = this.winding, - ) = renderWidth(stack, x.toFloat(), y.toFloat(), width.toFloat(), winding) - - @JvmOverloads - fun renderHeight( - stack: PoseStack, - x: Float = 0f, - y: Float = 0f, - height: Float = this.height, - winding: UVWindingOrder = this.winding, - ) { - render(stack, x, y, height = height, winding = winding) - } - - @JvmOverloads - fun renderHeight( - stack: PoseStack, - x: Double, - y: Double = 0.0, - height: Double = this.height.toDouble(), - winding: UVWindingOrder = this.winding, - ) = renderHeight(stack, x.toFloat(), y.toFloat(), height.toFloat(), winding) - - val u0 = this.x / imageWidth - val v0 = this.y / imageHeight - val u1 = (this.x + width) / imageWidth - val v1 = (this.y + height) / imageHeight - - @JvmOverloads - fun renderRaw( - stack: PoseStack, - x: Float = 0f, - y: Float = 0f, - width: Float = this.width, - height: Float = this.height, - winding: UVWindingOrder = UVWindingOrder.NORMAL - ) { - val winded = winding.translate(u0, v0, u1, v1) - - drawTexturedRect( - stack, - x, - y, - width, - height, - winded, - ) - } - - @JvmOverloads - fun renderRaw( - stack: PoseStack, - x: Double, - y: Double = 0.0, - width: Double = this.width.toDouble(), - height: Double = this.height.toDouble(), - winding: UVWindingOrder = UVWindingOrder.NORMAL - ) = renderRaw(stack, x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), winding) - - @JvmOverloads - fun renderRawPartial( - stack: PoseStack, - x: Float = 0f, - y: Float = 0f, - width: Float = this.width, - height: Float = this.height, - winding: UVWindingOrder = UVWindingOrder.NORMAL - ) { - val u1 = (this.x + width) / imageWidth - val v1 = (this.y + height) / imageHeight - - val winded = winding.translate(u0, v0, u1, v1) - - drawTexturedRect( - stack, - x, - y, - width, - height, - winded, - ) - } - - @JvmOverloads - fun renderRawPartial( - stack: PoseStack, - x: Double, - y: Double = 0.0, - width: Double = this.width.toDouble(), - height: Double = this.height.toDouble(), - winding: UVWindingOrder = UVWindingOrder.NORMAL - ) = renderRawPartial(stack, x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), winding) + override val u0 = this.x / imageWidth + override val v0 = this.y / imageHeight + override val u1 = (this.x + width) / imageWidth + override val v1 = (this.y + height) / imageHeight } - -data class StretchingRectangleElement( - val topLeft: SkinElement, - val topRight: SkinElement, - val bottomLeft: SkinElement, - val bottomRight: SkinElement, - val left: SkinElement, - val right: SkinElement, - val top: SkinElement, - val bottom: SkinElement, - val middle: SkinElement, - val padding: DockProperty = DockProperty.EMPTY -) { - fun render( - stack: PoseStack, - x: Float = 0f, - y: Float = 0f, - width: Float, - height: Float, - ) { - val bgWidth = width - topLeft.width.coerceAtMost(bottomLeft.width) - topRight.width.coerceAtMost(bottomRight.width) - padding.left - padding.right - val bgHeight = height - topLeft.height.coerceAtMost(bottomLeft.height) - topRight.height.coerceAtMost(bottomRight.height) - padding.top - padding.bottom - - if (bgWidth > 0 && bgHeight > 0) { - middle.render(stack, x + topLeft.width + padding.left, y + topLeft.height + padding.top, bgWidth, bgHeight) - } - - top.render(stack, x + topLeft.width, y, width = width - topLeft.width - topRight.width) - bottom.render(stack, x + bottomLeft.width, y + height - bottom.height, width = width - bottomLeft.width - bottomRight.width) - - left.render(stack, x, y + topLeft.height, height = height - topLeft.height - bottomLeft.height) - right.render(stack, x + width - right.width, y + topRight.height, height = height - topRight.height - bottomRight.height) - - topLeft.render(stack, x, y) - topRight.render(stack, x + width - topRight.width, y) - - bottomLeft.render(stack, x, y + height - bottomLeft.height) - bottomRight.render(stack, x + width - bottomRight.width, y + height - bottomLeft.height) - } - - companion object { - fun square( - texture: ResourceLocation, - x: Float, - y: Float, - cornerWidth: Float, - cornerHeight: Float, - width: Float, - height: Float, - sideThickness: Float, - imageWidth: Float = 256f, - imageHeight: Float = 256f, - ): StretchingRectangleElement { - val innerThickness = sideThickness.coerceAtLeast(cornerWidth).coerceAtLeast(cornerHeight) - val padding = sideThickness - innerThickness - - return StretchingRectangleElement( - topLeft = texture.element(x, y, cornerWidth, cornerHeight, imageWidth, imageHeight), - topRight = texture.element(x + width - cornerWidth, y, cornerWidth, cornerHeight, imageWidth, imageHeight), - bottomLeft = texture.element(x, y + height - cornerHeight, cornerWidth, cornerHeight, imageWidth, imageHeight), - bottomRight = texture.element(x + width - cornerWidth, y + height - cornerHeight, cornerWidth, cornerHeight, imageWidth, imageHeight), - - top = texture.element(x + cornerWidth, y, width - cornerWidth * 2f, sideThickness, imageWidth, imageHeight), - bottom = texture.element(x + cornerWidth, y + height - sideThickness, width - cornerWidth * 2f, sideThickness, imageWidth, imageHeight), - left = texture.element(x, y, sideThickness, height - cornerHeight * 2f, imageWidth, imageHeight), - right = texture.element(x, y + width - sideThickness, sideThickness, height - cornerHeight * 2f, imageWidth, imageHeight), - - middle = texture.element(x + innerThickness, y + innerThickness, width - innerThickness * 2f, height - innerThickness * 2f, imageWidth, imageHeight), - padding = DockProperty(padding, padding, padding, padding) - ) - } - } -} - diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/StretchingRectangleElement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/StretchingRectangleElement.kt new file mode 100644 index 000000000..8fe0010fb --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/StretchingRectangleElement.kt @@ -0,0 +1,127 @@ +package ru.dbotthepony.mc.otm.client.render + +import com.mojang.blaze3d.vertex.PoseStack +import net.minecraft.resources.ResourceLocation +import ru.dbotthepony.mc.otm.client.screen.panels.DockProperty + +data class StretchingRectangleElement( + val topLeft: AbstractSkinElement, + val topRight: AbstractSkinElement, + val bottomLeft: AbstractSkinElement, + val bottomRight: AbstractSkinElement, + val left: AbstractSkinElement, + val right: AbstractSkinElement, + val top: AbstractSkinElement, + val bottom: AbstractSkinElement, + val middle: AbstractSkinElement, + val padding: DockProperty = DockProperty.EMPTY +) { + fun render( + stack: PoseStack, + x: Float = 0f, + y: Float = 0f, + width: Float, + height: Float, + ) { + val bgWidth = width - topLeft.width.coerceAtMost(bottomLeft.width) - topRight.width.coerceAtMost(bottomRight.width) - padding.left - padding.right + val bgHeight = height - topLeft.height.coerceAtMost(bottomLeft.height) - topRight.height.coerceAtMost(bottomRight.height) - padding.top - padding.bottom + + if (bgWidth > 0 && bgHeight > 0) { + middle.render(stack, x + topLeft.width + padding.left, y + topLeft.height + padding.top, bgWidth, bgHeight) + } + + top.render(stack, x + topLeft.width, y, width = width - topLeft.width - topRight.width) + bottom.render(stack, x + bottomLeft.width, y + height - bottom.height, width = width - bottomLeft.width - bottomRight.width) + + left.render(stack, x, y + topLeft.height, height = height - topLeft.height - bottomLeft.height) + right.render(stack, x + width - right.width, y + topRight.height, height = height - topRight.height - bottomRight.height) + + topLeft.render(stack, x, y) + topRight.render(stack, x + width - topRight.width, y) + + bottomLeft.render(stack, x, y + height - bottomLeft.height) + bottomRight.render(stack, x + width - bottomRight.width, y + height - bottomLeft.height) + } + + companion object { + fun square( + texture: ResourceLocation, + x: Float, + y: Float, + cornerWidth: Float, + cornerHeight: Float, + width: Float, + height: Float, + sideThickness: Float, + imageWidth: Float = 256f, + imageHeight: Float = 256f, + ): StretchingRectangleElement { + val innerThickness = sideThickness.coerceAtLeast(cornerWidth).coerceAtLeast(cornerHeight) + val padding = sideThickness - innerThickness + + return StretchingRectangleElement( + topLeft = texture.element(x, y, cornerWidth, cornerHeight, imageWidth, imageHeight), + topRight = texture.element( + x + width - cornerWidth, + y, + cornerWidth, + cornerHeight, + imageWidth, + imageHeight + ), + bottomLeft = texture.element( + x, + y + height - cornerHeight, + cornerWidth, + cornerHeight, + imageWidth, + imageHeight + ), + bottomRight = texture.element( + x + width - cornerWidth, + y + height - cornerHeight, + cornerWidth, + cornerHeight, + imageWidth, + imageHeight + ), + + top = texture.element( + x + cornerWidth, + y, + width - cornerWidth * 2f, + sideThickness, + imageWidth, + imageHeight + ), + bottom = texture.element( + x + cornerWidth, + y + height - sideThickness, + width - cornerWidth * 2f, + sideThickness, + imageWidth, + imageHeight + ), + left = texture.element(x, y, sideThickness, height - cornerHeight * 2f, imageWidth, imageHeight), + right = texture.element( + x, + y + width - sideThickness, + sideThickness, + height - cornerHeight * 2f, + imageWidth, + imageHeight + ), + + middle = texture.element( + x + innerThickness, + y + innerThickness, + width - innerThickness * 2f, + height - innerThickness * 2f, + imageWidth, + imageHeight + ), + padding = DockProperty(padding, padding, padding, padding) + ) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt new file mode 100644 index 000000000..0b858629a --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt @@ -0,0 +1,78 @@ +package ru.dbotthepony.mc.otm.client.render + +import net.minecraft.client.renderer.texture.TextureAtlas +import net.minecraft.client.renderer.texture.TextureAtlasSprite +import net.minecraft.client.renderer.texture.TextureManager +import net.minecraft.client.resources.TextureAtlasHolder +import net.minecraft.resources.ResourceLocation +import net.minecraft.server.packs.resources.ReloadableResourceManager +import net.minecraft.server.packs.resources.ResourceManager +import net.minecraft.util.profiling.ProfilerFiller +import net.minecraftforge.client.event.RegisterClientReloadListenersEvent +import ru.dbotthepony.mc.otm.OverdriveThatMatters +import ru.dbotthepony.mc.otm.client.minecraft +import ru.dbotthepony.mc.otm.client.onceClient +import ru.dbotthepony.mc.otm.registry.WriteOnce +import java.util.stream.Stream +import kotlin.properties.Delegates + +var isWidgetAtlasAvailable: Boolean = false + private set + +class WidgetAtlasHolder private constructor(manager: TextureManager) : TextureAtlasHolder(manager, LOCATION, "gui") { + var changeset = 0 + private set + + private var queued = false + private var once = false + + private var resourceManager by Delegates.notNull() + private var profileManager by Delegates.notNull() + + override fun prepare(p_118891_: ResourceManager, p_118892_: ProfilerFiller): TextureAtlas.Preparations { + once = true + resourceManager = p_118891_ + profileManager = p_118892_ + return super.prepare(p_118891_, p_118892_) + } + + public override fun getSprite(p_118902_: ResourceLocation): TextureAtlasSprite { + if (!once) { + throw IllegalStateException("Trying to get sprite too early") + } + + return super.getSprite(p_118902_) + } + + fun queueRebuild() { + if (!queued && once) { + queued = true + + onceClient(20) { + queued = false + apply(prepare(resourceManager, profileManager), resourceManager, profileManager) + } + } + } + + override fun getResourcesToLoad(): Stream { + changeset++ + return AtlasSkinElement.keys + } + + companion object { + @JvmStatic + val LOCATION = ResourceLocation(OverdriveThatMatters.MOD_ID, "textures/atlas/gui.png") + + @JvmStatic + var INSTANCE: WidgetAtlasHolder by WriteOnce() + private set + + @JvmStatic + fun register(event: RegisterClientReloadListenersEvent) { + INSTANCE = WidgetAtlasHolder(minecraft.textureManager) + event.registerReloadListener(INSTANCE) + isWidgetAtlasAvailable = true + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt index f51d30da0..db5eb82c4 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/AndroidStationScreen.kt @@ -303,7 +303,7 @@ private class AndroidResearchButton( AndroidStationScreen.CAN_NOT_BE_RESEARCHED.setSystemColor() } - val icon = node.type.skinIcon + val icon = node.type.resolvedSkinIcon val itemIcon = node.type.itemIcon if (icon != null) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/TimerQueue.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/TimerQueue.kt index 40e5ec0e7..35604a00a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/TimerQueue.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/TimerQueue.kt @@ -1,5 +1,7 @@ package ru.dbotthepony.mc.otm.core +import org.apache.logging.log4j.LogManager + class TimerQueue { private var ticks = 0 private val list = ArrayDeque() @@ -48,6 +50,15 @@ class TimerQueue { } } + fun add(timerTicks: Int, ticker: Runnable, condition: Boolean, reason: String): Timer? { + if (!condition) { + LOGGER.error("Refusing to add timer $ticker in $timerTicks ticks because we $reason", IllegalStateException(reason)) + return null + } + + return Timer(timerTicks, ticker) + } + fun tick() { ticks++ @@ -67,4 +78,8 @@ class TimerQueue { ticks = 0 list.clear() } + + companion object { + private val LOGGER = LogManager.getLogger() + } } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 61f82a3ce..6d2453c24 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -45,3 +45,5 @@ protected net.minecraft.client.gui.screens.inventory.AbstractContainerScreen m_9 protected net.minecraft.client.gui.screens.inventory.AbstractContainerScreen m_97818_()V # recalculateQuickCraftRemaining protected net.minecraft.client.gui.screens.inventory.AbstractContainerScreen m_97744_(DD)Lnet/minecraft/world/inventory/Slot; # findSlot protected net.minecraft.client.gui.screens.inventory.AbstractContainerScreen m_97782_(Lnet/minecraft/world/item/ItemStack;IILjava/lang/String;)V # renderFloatingItem + +protected net.minecraft.client.resources.TextureAtlasHolder f_118884_ # textureAtlas diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-0.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-0.png new file mode 100644 index 0000000000000000000000000000000000000000..d94769ae23d60a3153a508184c4064cb0e3ca680 GIT binary patch literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@krQ$6De z)T>(J8c~vxSdwa$T$Bo=7>o>zjC2hRbPbF`3=OPI46TgJwG9lc3=CT57UrR7$jwj5 zOsmAL;md)Omw*~H;5L+G=B5^xB<2<%^q7Shnpzo|S{XtuVU?nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kHJr!C8<`)MX5lF!N|bKNY~In z*T5*m(7?*X(8|bM+rYrez@T+*VIGQx-29Zxv`X9>z8pAt38+B>ZbM0CZfbE!Vr~IK fk6DPJsg;qbl_As;R;emypdJQKS3j3^P6nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kXH2==Zx_6|8KuD|Ms%lk*~k(51&`>e*JksGgV7mBT7;dOH!?pi&B9UgOP!ek*=YE zu7OdAp@Ef&p_P%jwt<0_fkErs!aNiWx%nxXX_dG&d^vFP5>SH%+=i0O+|=Td#M}ae f9Fdh=jEjw5QgG(K+f#u;S)MMAAsXjOLj37rS6Rl^=8X`YuKH>`NmSAGwIQ)o0i*b_+S#axqeB% z(UTtOEl0A_H!H|&RTfy+m^-E5m}KRln%1{d?(OJ%;cP!`_WH?u_Uqd7cv~xGWUpMR z3$#_W#5JNMC9x#cD!C{XNHG{07#Zmr8t57rg%}!GnHX9bnQI#uSQ!|!&MnMC(U6;; zl9^VCTf>(FCocgtXuxeK$;?eHE=kNSK>Y@gKq!; literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-12.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-12.png new file mode 100644 index 0000000000000000000000000000000000000000..97ef5abe569272c8d7deb00ea7fac5df07fa25c0 GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kE*4Y~O#nQ4`{HGDa6 w@)A&k2Hb{{%-q!ClEmBsgdVdHLsKgwQ!7KLC9G0a&Okj3p00i_>zopr0M?;lD*ylh literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-13.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-13.png new file mode 100644 index 0000000000000000000000000000000000000000..ca7c89649e45c826be1b06bd2fe517e8f27f30d1 GIT binary patch literal 411 zcmeAS@N?(olHy`uVBq!ia0vp^LLkfmBp8a9UhD=^Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8W<`)^mS!_#>K`jDLC`r?WsT^A5Ry@ z5Q)oE`wWE`6gUoZ{jV>6&BW2(%~R!;%lwDWvTqW@hUSQ_mEYP-)-fDCpCET5Z-f4& zM^hu`9o@8v@y4D{s;B=|6?oj%ihBCqa;B@+^-aH1`+?@FmbgZgq$HN4S|t~y0x1R~ z10y3{Ljzp{qYy&_D-%O2BXey711kfA*13gwC>nC}Q!>*kaclT;;N&Hs1`W6kC7HRY m#U+Wk1qeN6A%><_My6JVP)k^)s+@s(7(8A5T-G@yGywog0eRQ} literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-14.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-14.png new file mode 100644 index 0000000000000000000000000000000000000000..4b56feda20407567c9ea96a0297a479601dd3c49 GIT binary patch literal 375 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kro4Hqe&#?uswJ)wB`Jv| zsaDBFsX&Us$iT=**U&)Mz$nDfz{<_My6JVP)k^)s+@s(7(8A5T-G@yGywo)5?q@A literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-15.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-15.png new file mode 100644 index 0000000000000000000000000000000000000000..69cdf547de7c085a0a9b96b615606384397788da GIT binary patch literal 399 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@k<=WU^G{^%G`0h( z6G{O3=9xeW)0NGEK(ka!Tq8E*4Y~O#nQ4`{HGDa6@)A&k2Hb{{%-q!ClEmBsgdVdHLsKgwQ!7KLC9G0a Q&Okj3p00i_>zopr0C$~g;Q#;t literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-16.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-16.png new file mode 100644 index 0000000000000000000000000000000000000000..446c17f7d4ba0cdce2c6ffea5271a540a92d366a GIT binary patch literal 402 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kp#Ds(ksR;oq@)I;Rq$lk7_>;kzQ9zsFCXa*R{H;7Z`R5yE@+c@S5IOu@*}+Uc zAjd4=oeXocKQ}YO6ba!3MdzkRK;u+PTq8E*4Y~O#nQ4`{HGDa6@)A&k2Hb{{%-q!ClEmBsgdVdHLsKgw YQ!7KLC9G0a&Okj3p00i_>zopr02az?*Z=?k literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-17.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-17.png new file mode 100644 index 0000000000000000000000000000000000000000..38728ee3f261ae41b25e33c701d859fcc2e1fd2a GIT binary patch literal 387 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@k%sRNJ(eaZ5bF&b$z-MCvjgKY<5&sz<-7i?nFl7~w zvV4eTBv8LRdP`(kYX@0Ff!6LG|)9L3NbXWGBLC=GS@aRure@com-fP zq9HdwB{QuOw}vkVPF@0P(16=el9`)YT#}eufY4(WVrXh*WNKvywS-lw${DDK!PC{x JWt~$(695sLX5jz; literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-18.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-18.png new file mode 100644 index 0000000000000000000000000000000000000000..f4560d6cd98b8b236b04a113c7fb0ec24c2246ce GIT binary patch literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kvzF`IX%H(?tM+BZS#O>i!xG_yX z3aD4L#5JNMC9x#cD!C{XNHG{07#Zmr8t57rg%}!GnHX9bnQI#uSQ!|!&MnMC(U6;; zl9^VCTf>(FCocgtXuxeK$;?eHE=kNSK|SIW5ECb literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-19.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-19.png new file mode 100644 index 0000000000000000000000000000000000000000..421e5fc94d1c7b86962dad7bd4b3f56eab017157 GIT binary patch literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kzm0Xkxq!^40jEr;*4Rj5RLJSS8Obo4z%(V>+tPBiV=N9InXvob^ z$xN%nt>Mdolb3)RG~hOrWag$8mn7yEAoQ4p7@ArcnOYe_En$_aat7*Q@O1TaS?83{ F1OUwYWi0>z literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-2.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-2.png new file mode 100644 index 0000000000000000000000000000000000000000..fa4be628356a3faff383216dbf4f2c3265660a45 GIT binary patch literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@k%r0Oli10cQ>{Fa%F#(&M}Q4rGOD ziEBhjN@7W>RdP`(kYX@0Ff!6LG|)9L3NbXWGBLC=GS@aRure@com-fPq9HdwB{QuO zw}vkVPF@0P(16=el9`)YT#}eufY4(WVrXh*WNKvywS-lw${DDK!PC{xWt~$(69B?A BVKD#z literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-20.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-20.png new file mode 100644 index 0000000000000000000000000000000000000000..78067b180b68acf6a728aa8cf0fa77a5faff9681 GIT binary patch literal 399 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@k(FCocgtXuxeK$;?eHE=kNSK< VTEZ$-^>@Xq^B6 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-21.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-21.png new file mode 100644 index 0000000000000000000000000000000000000000..cc9d32fe312dbb79deec3232475752b429bc0a4c GIT binary patch literal 395 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kBQ;SOya|;l9%t8!Jt&B{q455~=N>w=n P^)Pt4`njxgN@xNAaqMUd literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-22.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-22.png new file mode 100644 index 0000000000000000000000000000000000000000..ea6ae2a55761f5dee6e5fe2cc1cedf4ae9e8e9b7 GIT binary patch literal 385 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kFdh=jEjw5QgG(K+f#u;VV*9IAsXlJPB7#SH%+=i0O+|=Td j#M}ae9Fdh=jEjw5QgG(K+f#u;#-1*YAsXl3p54gHV8FqA zu>RZqo0qI)aU0TV|_-^BZ8!j8q*M7O_uJ-wp%mYTt{hSddxx$O|6Vftqh@-uu4@q1NAU?y85}Sb4q9e0K+S4 Awg3PC literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-3.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-3.png new file mode 100644 index 0000000000000000000000000000000000000000..10646c9ed8844e6ea2096a5a5c92b4f1908c8cb1 GIT binary patch literal 370 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kddxx$O|6Vftqh@-uu4@q1NAU?y85}Sb4q9e0OTQIWB>pF literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-4.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-4.png new file mode 100644 index 0000000000000000000000000000000000000000..ecfeb4a92e7b2eb376381a7018083e5d5eab4bdc GIT binary patch literal 386 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@knTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@k275JLki6GJN_b8Q0yD+7bpxrKQs8glbf zGSez?Yxr{DnTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kcptHiD0 z%Yl=ZfEqO5Hk4%MrWThZ<`y9In1vXcS{a#I8A2^#m8xS6G7^>bP0l+XkKfstc2 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-7.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-7.png new file mode 100644 index 0000000000000000000000000000000000000000..5b6b03cf6e7dc1bdb604980fb202af27a4c7133c GIT binary patch literal 414 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|mSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YDR+ueoXe|!I#{XiaP zfk$L90|OtB&9|S?f;CtLD9B#o>Fdh=jEjw5QgG(K+f#u;v7RoDAsXkmUU1|)pupjB zvH#`Y{xc;D-Mn4HHyIw{W_eI`;%R!*4Wpf}W^eXmSYlkLT6@KvCrIqv6NTjw{znpZ z7dtX#9Z5Q<7EmuX>wn+$oIg9=)82OC7#SED=^7g78W@Eb8d#YaS{a#Z8yHv_7_`nU%tO(To1c=IR*74~mjfp+0X1mA rZ79jiO)V}-%q>9ZF$*y?wK6ibGK5;fDplnS)WhKE>gTe~DWM4f3+sG^ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-8.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-8.png new file mode 100644 index 0000000000000000000000000000000000000000..e2dcc9d85efc2b4b9a7f7190fe530e2340ec9a01 GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@k_gW*Ju^f%&v5`daiOI#yL zQW8s2t&)pUffR$0fsv7}p@FV}QHY^|m5HI1k-4^kft7(l>)gUT6b-rgDVb@NxHWt^ xaPksRg9hA&lFZ!H;*!MN0)!s35JOWdBU39ws3ojYRn9;?44$rjF6*2UngCpzV_N_K literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-9.png b/src/main/resources/assets/overdrive_that_matters/textures/gui/android_stuff-9.png new file mode 100644 index 0000000000000000000000000000000000000000..a18a56c74f965006c7ee77909c2fb1c7730a7330 GIT binary patch literal 399 zcmeAS@N?(olHy`uVBq!ia0vp^LLkftBpB+pZvF+NSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#KiG8eT9klo~KFyh>nTu$sZZAYL$MSD+10f~h3PFPMR$Z1PzP5HHEw z-GzbiZkO;9Adj=aBeIx*fp0$uGg`0)s{jSrOFVsD*`IN-@kF|pc=S3Q?wmOAK<&hV1?!F+SWv^UqK$P$ z7}JX9j6UYf4DVRkRyQ+z?g5&mTH+c}l9E`GYL#4+3Zxi}42+C)4GnY+j6w_ztV|58 zjLfwS46FPsvQH#I51Wfs>bj8Z_WGlw{_n7MCRE79jMPg&3Mz8JSuc WLM>sHs&WSEVeoYIb6Mw<&;$To>}wDQ9-Keu0+?Z$1uyHSVvinHowY<0z5Hf<-Q zj?$_~iKDF$M>VRh${Z2Iku)MreC>buKF{a&+tvd^FtN3l9J&)Bcl; z*%7zz4sJOPw${JJpx4W@L)m7EmvG|!x#=4aLy9%*J^lBGZEoP|W02y4kTX|wFiXsO z5rNKF(Ammsa5H|LXyT2LvE-v?vg$lhLVUE5Wr(f~OglNXZUyk|UVdMfxjs&MlofR^ zzf(Trwo%#>fqhkAGg@V$A;9+tZbZ6efe?m9szWahID%F{3m_Gcd@a&HzIjsrrX`IK z#&oZct03hTmgd0XJJNB2)Ly#;S#8*Oa;7VUUu!`Qc+GuweZ;c?R}pwE$|knSdpQ$s zOJUJ*o!UMhEo#8tE%fv4HXgKZqJFxFQqOh)P1b_XaM9O1KCyGYD5iG@t0t~!4jQW% zC+Tu6YQZY2%#~{yl1RIT)4ye_$Avs-^+u6{P$G0Fq*DU}1j3Co5+0Q4IoI8wI*jG= z|80g>Yvf64s#J)d-Q>K68HWOfrY|%k)0eH9%)R#T!!38^jZga2rxG&CM2q3-Z-zxb zOfnfJ^NNmFd4;Vj)kjPjce;3IzjQkuZs>V7QYqAFs$6u^omv}FF{XA=cc1WrjNA|uNsH0~8R zZFnjKNGrHgmG)B3;5qhlK}j)$t+K6EH0x^6$a?@!P1XLmS)3@Ij)ks(Bt1b?OVj<$ z_Zek-gratM?~p}OTXD(+dhC2UFKSen58FxNzhh(dl}CBW!;JpRxhO%`kNuj`fq{Wa z-zf|?LbId#wVZ<6)20Xv-T4Me3 z1S>6!OIMv&kg6f>r{KK(Frl%@^IYmI`@%7aRzPZym*I@d^z`j!v=%NFBu86coa1Oq z4Usej(e)KkkU2+Nvf8We9#7y5)Y=$is7mGz*0OBTTG(n#aOH^}yKl zlo;_3tKAB-Uyd;~7cKPRo|haox3L!3hfa{4k!+zAuW7SjH@Cdd)F852<7{^xSfDQP zZo?Zh;FzZy*4V~OQ-<@7KUh!Mp9`IvpsA)od;wZpWKiDmMfUi7{oBxO`su;s+2zF+ z@0HJ^D-y*(PeWr1I$_~#rRccShN`rS*+3$YrE2R}=P4DP5SK3sRf|Lyg57@omZ?_F zz9Rn{eD);|T64St6}fTJfS9Z30SwkQB7*(FSLs7RASPePBuV@~XC#vY|o=>~sj4PDC;zF9>(N6#9ZDN6mr ziHn^yH0$OPO@bLB_j`StL|bQy{|VC_t2*6LE}JCy4aC_bn$*X=5HNqy#+C8&)3n3H z6Kwn#vDx#x@evS%+DnRUy(kx)7D?86_bAr{qnd4^|CgzC5BRjwFCL4FC_glUAM}9L zcucq{AyP+f{o>2z2;Oc|sY=j+)t&sl%-bDLZhjvv)-aoT6V|gz3asI2E&e>2qMd;- z9z9A9p}+`2PT113um$ko^jqd%`8ML(FjSr(w zZS2BPC7sDMZw=R(iM2*2A&A(+}+E+p$8Pb6?X;ComP> zm%LOY?p_QFPJ|p1y{qaLmV-ZJU&qKykpC2H2r|4WWr9;YiK4*jtU0!*%9?tRPjSm- z{0E*%mx503P;5*^h?(2jxld%9h|Lbgs)5jMdfH}ej32Hu2T;ZleY%UpCZt9+Plw_4X)sX^6N7$RSw4TD!1z<+XpAa@KrmihU zHNn0)UeT{y%`eB@mKLM?nD%l!-1C(94zz!3$YL^})3-CvEhP_m2%in<-=1fW4EgkM znIF`#CD{W#O4Iwb=Id4W=wtIwf5Hcb2gEspjP)g1k4NB$>1EEm1b{+*TO24+fc7iA zkEO*JisxT$^@t5$HvN3UxT}A>^iByeY4`H}0%B@tShmJ``of9z2o%Ln>DA(TB1B*! zo6NuA85dUaN&aD!gJrRG1msoL+@XyX(WgvdB;aZYl`SlFqfOuF19hZHO{P$MyRxme z)!BA2bAO=~Ij^>>!PGVSYvCSvc1ux~5noDXRYF1jow|Vji;Q6VG4Rr9t8y2)p*@vTFW6 eBMp^)6@juuBsJa!MRZWs01lU~*f!c=?*9*e@F=+e