Atlas variant for skin element

Fixes #99
This commit is contained in:
DBotThePony 2022-10-06 23:41:08 +07:00
parent 92ae0408e8
commit f99ed19d9c
Signed by: DBot
GPG Key ID: DCC23B5715498507
39 changed files with 719 additions and 322 deletions

View File

@ -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

View File

@ -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();

View File

@ -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<Component>? = null,
var descriptionSuppliers: MutableList<ComponentSupplier>? = null,
var itemIcon: Item? = null,
var skinIcon: SkinElement? = null,
var skinIcon: AbstractSkinElement? = null,
var iconText: Component? = null,
) {
private val items = ArrayList<Pair<Ingredient, Int>>()
@ -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() }

View File

@ -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")

View File

@ -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<AbstractSkinElement>(), JsonSerializer<AbstractSkinElement>, JsonDeserializer<AbstractSkinElement> {
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)
}
}
}

View File

@ -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<AtlasSkinElement>(), JsonSerializer<AtlasSkinElement>, JsonDeserializer<AtlasSkinElement> {
private val skinElementCache = HashMap<ResourceLocation, AtlasSkinElement>()
val keys: Stream<ResourceLocation> 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)
}
}
}

View File

@ -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++}"))
}
}

View File

@ -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)
)
}
}
}

View File

@ -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)
)
}
}
}

View File

@ -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<ResourceManager>()
private var profileManager by Delegates.notNull<ProfilerFiller>()
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<ResourceLocation> {
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
}
}
}

View File

@ -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) {

View File

@ -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<Timer>()
@ -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()
}
}

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB