From c03bbf829fe7b9f6303c203bc4d2abbda6d9385a Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 16 Apr 2024 19:41:19 +0200 Subject: [PATCH 01/14] Upgraded kotlin, javafx plugin and various deps to latest version --- build.gradle.kts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d2fc486..f78f8c1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - id("org.openjfx.javafxplugin") version "0.0.11" - kotlin("jvm") version "1.7.10" + id("org.openjfx.javafxplugin") version "0.1.0" + kotlin("jvm") version "1.9.10" kotlin("plugin.serialization") version "1.6.0" application } @@ -9,7 +9,7 @@ repositories { jcenter() } -version = "0.1.8" +version = "0.1.9" allprojects { group = "stan.qodat" @@ -52,18 +52,18 @@ val javaFXOptions = the() dependencies { implementation(project("qodat-api")) - implementation("org.jsoup:jsoup:1.15.2") + implementation("org.jsoup:jsoup:1.17.2") implementation("us.ihmc:ihmc-javafx-toolkit:17-0.21.2") implementation("org.jcodec:jcodec:0.2.5") implementation("org.jcodec:jcodec-javase:0.2.5") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.6.4") - implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.8.0") + implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0") implementation("org.orbisgis:poly2tri-core:0.1.2") - implementation("com.displee:rs-cache-library:6.8.1") + implementation("com.displee:rs-cache-library:7.1.3") implementation("org.joml:joml-primitives:1.10.0") - implementation("org.joml:joml:1.10.2") + implementation("org.joml:joml:1.10.5") implementation("no.tornado:tornadofx:1.7.20") testImplementation(kotlin("test-junit")) } From 860909c8cb6d615d515c2a21adc416ca6cedfb06 Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 16 Apr 2024 19:43:00 +0200 Subject: [PATCH 02/14] Added support for label mapping --- src/main/kotlin/stan/qodat/Properties.kt | 6 ++ .../stan/qodat/scene/control/LabeledHBox.kt | 74 +++++++++++++++++-- .../scene/runescape/animation/Animation.kt | 22 ++++-- .../scene/runescape/entity/AnimatedEntity.kt | 7 +- .../qodat/scene/runescape/entity/Entity.kt | 5 +- .../scene/runescape/entity/SpotAnimation.kt | 4 +- .../stan/qodat/scene/runescape/model/Model.kt | 2 +- .../scene/runescape/ui/InterfaceGroup.kt | 4 +- .../stan/qodat/scene/runescape/ui/Sprite.kt | 2 +- src/main/kotlin/stan/qodat/util/Labels.kt | 39 +++++++++- 10 files changed, 141 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/stan/qodat/Properties.kt b/src/main/kotlin/stan/qodat/Properties.kt index 2366c04..2787d7d 100644 --- a/src/main/kotlin/stan/qodat/Properties.kt +++ b/src/main/kotlin/stan/qodat/Properties.kt @@ -93,6 +93,7 @@ object Properties { val osrsCachePath = SimpleObjectProperty(rootPath.get().resolve("caches/OS/rev203")) val qodatCachePath = SimpleObjectProperty(rootPath.get().resolve("caches/qodat")) val legacyCachePath = SimpleObjectProperty(rootPath.get().resolve("caches/667")) + val labelMappingPath = SimpleObjectProperty(rootPath.get().resolve("labels.json")) val downloadsPath = SimpleObjectProperty(rootPath.get().resolve("downloads")) @@ -139,6 +140,8 @@ object Properties { val copyModelsFromNpc = SimpleBooleanProperty(true) val copyAnimationsFromNpc = SimpleBooleanProperty(true) + val showIdAfterLabel = SimpleBooleanProperty(true) + fun bind(sessionManager: PropertiesManager) { sessionManager.bind("anti-aliasing", antialiasing) { @@ -204,6 +207,7 @@ object Properties { sessionManager.bindPath("osrs-cache-path", osrsCachePath) sessionManager.bindPath("qodat-cache-path", qodatCachePath) sessionManager.bindPath("legacy-cache-path", legacyCachePath) + sessionManager.bindPath("label-mapping-path", labelMappingPath) sessionManager.bindPath("main-data-path", projectFilesPath) sessionManager.bindPath("exports-path", defaultExportsPath) @@ -211,5 +215,7 @@ object Properties { sessionManager.bindPath("last-wavefront-sequence-export-path", lastWaveFrontSequenceExportPath) sessionManager.bindPath("last-gif-export-path", lastGIFExportPath) sessionManager.bindPath("last-sprite-export-path", lastSpriteExportPath) + + sessionManager.bindBoolean("show-id-after-label", showIdAfterLabel) } } diff --git a/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt b/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt index 0cce404..581ad51 100644 --- a/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt +++ b/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt @@ -4,6 +4,13 @@ import javafx.beans.property.SimpleStringProperty import javafx.geometry.Pos import javafx.scene.control.Label import javafx.scene.layout.HBox +import javafx.scene.paint.Color +import javafx.scene.text.Font +import javafx.scene.text.FontWeight +import stan.qodat.Properties +import stan.qodat.util.LabelMapping +import tornadofx.* +import kotlin.error /** * Represents a [HBox] containing a [Label] bound to the text property. @@ -12,15 +19,70 @@ import javafx.scene.layout.HBox * @since 31/01/2021 */ class LabeledHBox( - textProperty: SimpleStringProperty, + private val originalValueProperty: SimpleStringProperty, spacing: Double = 10.0, - alignment: Pos = Pos.CENTER_LEFT + alignment: Pos = Pos.CENTER_LEFT, + private val labelPrefix: String? = null ) : HBox(spacing) { - val label = Label() + private val actualTextProperty = stringProperty(mappingOrOriginal()) + + val editableValueProperty = stringProperty(mappingOrNull()?:"").apply { + onChange { + if (it != null) { + LabelMapping[key()] = it + actualTextProperty.value = it.ifBlank { originalValueProperty.value } + } + } + } + + private fun mappingOrOriginal() = + mappingOrNull()?: originalValueProperty.value + private fun mappingOrNull() = + labelPrefix?.let { LabelMapping[key()] } + private fun key() = + "${labelPrefix ?: error("Label prefix is required for editable labeled hbox!")}.${originalValueProperty.value}" + + val editableProperty = booleanProperty(false) + init { this.alignment = alignment - label.textProperty().bind(textProperty) - children.add(label) + editableProperty.onChange { updateChildren() } + focusedProperty().onChange { if (!it) editableProperty.set(false) } + updateChildren() + } + + private fun LabeledHBox.updateChildren() { + children.clear() + if (editableProperty.get()) { + textfield(editableValueProperty) { + focusedProperty().onChange { + if (!it) + editableProperty.set(false) + } + setOnKeyPressed { + if (it.code.name == "ENTER") + editableProperty.set(false) + } + } + } else { + if (actualTextProperty.value.isNullOrBlank()) { + actualTextProperty.value = originalValueProperty.value + } + text(actualTextProperty) { + font = Font.font("Menlo", FontWeight.BOLD, 13.0) + fill = Color(214 / 255.0, 214 / 255.0, 214 / 255.0, 1.0) + onDoubleClick { + editableProperty.set(true) + } + } + if (actualTextProperty.value != originalValueProperty.value && Properties.showIdAfterLabel.value) { + text(originalValueProperty) { + font = Font.font("Menlo", FontWeight.EXTRA_LIGHT, 13.0) + fill = Color.web("#A4B8C8") + } + } + } } -} \ No newline at end of file +} + diff --git a/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt b/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt index 8538831..574e213 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt @@ -27,6 +27,9 @@ import stan.qodat.scene.transform.Transformable import stan.qodat.scene.transform.Transformer import stan.qodat.task.BackgroundTasks import stan.qodat.util.Searchable +import tornadofx.contextmenu +import tornadofx.item +import tornadofx.stringBinding /** * Represents a [Transformer] for [Model] objects. @@ -35,7 +38,7 @@ import stan.qodat.util.Searchable * @since 28/01/2021 */ class Animation( - label: String, + private val label: String, private val definition: AnimationDefinition? = null, private val cache: Cache? = null ) : Transformer, Searchable, ViewNodeProvider, Encoder { @@ -49,6 +52,7 @@ class Animation( val treeItemProperty = SimpleObjectProperty() val exportFrameArchiveId = SimpleIntegerProperty() val labelProperty = SimpleStringProperty(label) + val nameProperty = SimpleStringProperty(label) val idProperty = SimpleIntegerProperty() val loopOffsetProperty = SimpleIntegerProperty(definition?.loopOffset ?: -1) val leftHandItemProperty = SimpleIntegerProperty(definition?.leftHandItem ?: -1) @@ -114,12 +118,20 @@ class Animation( } } - override fun getName() = labelProperty.get() + override fun getName() = nameProperty.get() override fun getViewNode(): Node { if (!this::viewBox.isInitialized) { - viewBox = LabeledHBox(labelProperty).apply { - label.contextMenu = ContextMenu().apply { + viewBox = LabeledHBox(labelProperty, labelPrefix = "animation").apply { + nameProperty.bind(editableValueProperty.stringBinding(labelProperty) { + "${editableValueProperty.get()} (${labelProperty.get()})" + }) + contextmenu { + item("Rename") { + setOnAction { + editableProperty.set(true) + } + } menu("export") { menuItem("GIF") { BackgroundTasks.submit( @@ -163,4 +175,4 @@ class Animation( } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt index 57cdf65..2400412 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt @@ -17,8 +17,9 @@ import stan.qodat.scene.transform.Transformable abstract class AnimatedEntity( cache: Cache, definition: D, - private val animationProvider: D.() -> Array -) : Entity(cache, definition), Transformable, GroupableTransformable { + private val animationProvider: D.() -> Array, + labelPrefix: String? = null, +) : Entity(cache, definition, labelPrefix), Transformable, GroupableTransformable { private lateinit var animations: Array // private lateinit var skeletons: Map @@ -58,4 +59,4 @@ abstract class AnimatedEntity( for (model in getModels()) model.animate(frame) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt index 123141a..8b87ba2 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt @@ -37,7 +37,8 @@ import stan.qodat.util.getMaterial */ abstract class Entity( protected val cache: Cache, - val definition: D + val definition: D, + val labelPrefix: String? = null ) : Exportable, SceneNodeProvider, ViewNodeProvider, TreeItemProvider { private var modelGroup: Group? = null @@ -142,7 +143,7 @@ abstract class Entity( override fun getViewNode(): Node { if (!this::viewBox.isInitialized) { val optionalInt = definition.getOptionalId() - val box = LabeledHBox(labelProperty) + val box = LabeledHBox(labelProperty, labelPrefix = labelPrefix) viewBox = if (optionalInt.isPresent) HBox().apply { val id = optionalInt.asInt.toString() diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt index 15f7d24..dc790c1 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt @@ -11,9 +11,9 @@ class SpotAnimation( cache: Cache = OldschoolCacheRuneLite, definition: SpotAnimationDefinition, animationProvider: SpotAnimationDefinition.() -> Array -) : AnimatedEntity(cache, definition, animationProvider), Encoder { +) : AnimatedEntity(cache, definition, animationProvider, labelPrefix = "spot_anim"), Encoder { override fun toString(): String = getName() override fun property() = Properties.selectedSpotAnimName -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt b/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt index b2bb3d5..9ddc2bc 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt @@ -186,7 +186,7 @@ class Model(label: String, override fun getViewNode(): Node { if (!this::viewBox.isInitialized) { - viewBox = LabeledHBox(labelProperty) + viewBox = LabeledHBox(labelProperty, labelPrefix = "model") } return viewBox } diff --git a/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt b/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt index a263dcf..2589ec4 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt @@ -24,7 +24,7 @@ class InterfaceGroup(val cache: Cache, private val groupId: Int, val definitions val nameProperty = SimpleStringProperty(idProperty.get().toString()) private val viewBox: HBox by lazy { - LabeledHBox(nameProperty) + LabeledHBox(nameProperty, labelPrefix = "widget") } private val sceneGroup: Group by lazy { @@ -48,4 +48,4 @@ class InterfaceGroup(val cache: Cache, private val groupId: Int, val definitions override fun treeItemExpandedProperty(): BooleanProperty = Properties.treeItemInterfaceExpanded -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt b/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt index 8c195a5..8f92928 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt @@ -38,7 +38,7 @@ class Sprite(val definition: SpriteDefinition) : SceneNodeProvider, ViewNodeProv } val viewNode: HBox by lazy { - LabeledHBox(nameProperty) .apply { + LabeledHBox(nameProperty, labelPrefix = "sprite").apply { contextmenu { styleClass += "wave-front-format-export-menu" for (exportFormat in SpriteExportFormat.all) diff --git a/src/main/kotlin/stan/qodat/util/Labels.kt b/src/main/kotlin/stan/qodat/util/Labels.kt index 282e692..b0e8c8a 100644 --- a/src/main/kotlin/stan/qodat/util/Labels.kt +++ b/src/main/kotlin/stan/qodat/util/Labels.kt @@ -1,6 +1,41 @@ package stan.qodat.util -object Labels { +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import stan.qodat.Properties +import kotlin.io.path.createFile +import kotlin.io.path.exists +import kotlin.io.path.readText +import kotlin.io.path.writeText +object LabelMapping { -} \ No newline at end of file + private val labelData by lazy { load() } + + private fun load(): MutableMap { + val labelData = mutableMapOf() + Properties.labelMappingPath.value?.let { path -> + if (path.exists()) + labelData.putAll(Json.decodeFromString(path.readText())) + } + return labelData + } + + private fun save() { + Properties.labelMappingPath.value?.let { path -> + if (!path.exists()) + path.createFile() + path.writeText(Json.encodeToString(labelData)) + } + } + + operator fun get(key: String): String? { + return labelData[key] + } + + operator fun set(key: String, value: String) { + labelData[key] = value + save() + } +} From 8807e2d5de0df69e6f59aee8ba2e429d183fc10c Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 16 Apr 2024 19:43:00 +0200 Subject: [PATCH 03/14] Added support for label mapping --- src/main/kotlin/stan/qodat/Properties.kt | 6 ++ .../stan/qodat/scene/control/LabeledHBox.kt | 76 +++++++++++++++++-- .../scene/runescape/animation/Animation.kt | 22 ++++-- .../scene/runescape/entity/AnimatedEntity.kt | 7 +- .../qodat/scene/runescape/entity/Entity.kt | 5 +- .../stan/qodat/scene/runescape/entity/Item.kt | 6 +- .../stan/qodat/scene/runescape/entity/NPC.kt | 6 +- .../qodat/scene/runescape/entity/Object.kt | 6 +- .../scene/runescape/entity/SpotAnimation.kt | 4 +- .../stan/qodat/scene/runescape/model/Model.kt | 2 +- .../scene/runescape/ui/InterfaceGroup.kt | 4 +- .../stan/qodat/scene/runescape/ui/Sprite.kt | 2 +- src/main/kotlin/stan/qodat/util/Labels.kt | 39 +++++++++- 13 files changed, 152 insertions(+), 33 deletions(-) diff --git a/src/main/kotlin/stan/qodat/Properties.kt b/src/main/kotlin/stan/qodat/Properties.kt index 2366c04..2787d7d 100644 --- a/src/main/kotlin/stan/qodat/Properties.kt +++ b/src/main/kotlin/stan/qodat/Properties.kt @@ -93,6 +93,7 @@ object Properties { val osrsCachePath = SimpleObjectProperty(rootPath.get().resolve("caches/OS/rev203")) val qodatCachePath = SimpleObjectProperty(rootPath.get().resolve("caches/qodat")) val legacyCachePath = SimpleObjectProperty(rootPath.get().resolve("caches/667")) + val labelMappingPath = SimpleObjectProperty(rootPath.get().resolve("labels.json")) val downloadsPath = SimpleObjectProperty(rootPath.get().resolve("downloads")) @@ -139,6 +140,8 @@ object Properties { val copyModelsFromNpc = SimpleBooleanProperty(true) val copyAnimationsFromNpc = SimpleBooleanProperty(true) + val showIdAfterLabel = SimpleBooleanProperty(true) + fun bind(sessionManager: PropertiesManager) { sessionManager.bind("anti-aliasing", antialiasing) { @@ -204,6 +207,7 @@ object Properties { sessionManager.bindPath("osrs-cache-path", osrsCachePath) sessionManager.bindPath("qodat-cache-path", qodatCachePath) sessionManager.bindPath("legacy-cache-path", legacyCachePath) + sessionManager.bindPath("label-mapping-path", labelMappingPath) sessionManager.bindPath("main-data-path", projectFilesPath) sessionManager.bindPath("exports-path", defaultExportsPath) @@ -211,5 +215,7 @@ object Properties { sessionManager.bindPath("last-wavefront-sequence-export-path", lastWaveFrontSequenceExportPath) sessionManager.bindPath("last-gif-export-path", lastGIFExportPath) sessionManager.bindPath("last-sprite-export-path", lastSpriteExportPath) + + sessionManager.bindBoolean("show-id-after-label", showIdAfterLabel) } } diff --git a/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt b/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt index 0cce404..c70316c 100644 --- a/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt +++ b/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt @@ -4,6 +4,13 @@ import javafx.beans.property.SimpleStringProperty import javafx.geometry.Pos import javafx.scene.control.Label import javafx.scene.layout.HBox +import javafx.scene.paint.Color +import javafx.scene.text.Font +import javafx.scene.text.FontWeight +import stan.qodat.Properties +import stan.qodat.util.LabelMapping +import tornadofx.* +import kotlin.error /** * Represents a [HBox] containing a [Label] bound to the text property. @@ -12,15 +19,72 @@ import javafx.scene.layout.HBox * @since 31/01/2021 */ class LabeledHBox( - textProperty: SimpleStringProperty, + private val originalValueProperty: SimpleStringProperty, spacing: Double = 10.0, - alignment: Pos = Pos.CENTER_LEFT + alignment: Pos = Pos.CENTER_LEFT, + private val labelPrefix: String? = null ) : HBox(spacing) { - val label = Label() + private val actualTextProperty = stringProperty(mappingOrOriginal()) + + val editableValueProperty = stringProperty(mappingOrNull()?:"").apply { + onChange { + if (it != null) { + LabelMapping[key()] = it + actualTextProperty.value = it.ifBlank { originalValueProperty.value } + } + } + } + + private fun mappingOrOriginal() = + mappingOrNull()?: originalValueProperty.value + private fun mappingOrNull() = + labelPrefix?.let { LabelMapping[key()] } + private fun key() = + "${labelPrefix ?: error("Label prefix is required for editable labeled hbox!")}.${originalValueProperty.value}" + + val editableProperty = booleanProperty(false) + init { this.alignment = alignment - label.textProperty().bind(textProperty) - children.add(label) + editableProperty.onChange { updateChildren() } + focusedProperty().onChange { if (!it) editableProperty.set(false) } + updateChildren() + } + + private fun LabeledHBox.updateChildren() { + children.clear() + if (editableProperty.get()) { + textfield(editableValueProperty) { + focusedProperty().onChange { + if (!it) + editableProperty.set(false) + } + setOnKeyPressed { + if (it.code.name == "ENTER") + editableProperty.set(false) + } + } + } else { + if (actualTextProperty.value.isNullOrBlank()) { + actualTextProperty.value = originalValueProperty.value + } + text(actualTextProperty) { + font = Font.font("Menlo", FontWeight.BOLD, 13.0) + fill = Color(214 / 255.0, 214 / 255.0, 214 / 255.0, 1.0) + if (labelPrefix != null) { + onDoubleClick { + editableProperty.set(true) + } + } + } + if (actualTextProperty.value != originalValueProperty.value && Properties.showIdAfterLabel.value) { + text(originalValueProperty) { + font = Font.font("Menlo", FontWeight.EXTRA_LIGHT, 13.0) + fill = Color.web("#A4B8C8") + } + } + } } -} \ No newline at end of file +} + diff --git a/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt b/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt index 8538831..574e213 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt @@ -27,6 +27,9 @@ import stan.qodat.scene.transform.Transformable import stan.qodat.scene.transform.Transformer import stan.qodat.task.BackgroundTasks import stan.qodat.util.Searchable +import tornadofx.contextmenu +import tornadofx.item +import tornadofx.stringBinding /** * Represents a [Transformer] for [Model] objects. @@ -35,7 +38,7 @@ import stan.qodat.util.Searchable * @since 28/01/2021 */ class Animation( - label: String, + private val label: String, private val definition: AnimationDefinition? = null, private val cache: Cache? = null ) : Transformer, Searchable, ViewNodeProvider, Encoder { @@ -49,6 +52,7 @@ class Animation( val treeItemProperty = SimpleObjectProperty() val exportFrameArchiveId = SimpleIntegerProperty() val labelProperty = SimpleStringProperty(label) + val nameProperty = SimpleStringProperty(label) val idProperty = SimpleIntegerProperty() val loopOffsetProperty = SimpleIntegerProperty(definition?.loopOffset ?: -1) val leftHandItemProperty = SimpleIntegerProperty(definition?.leftHandItem ?: -1) @@ -114,12 +118,20 @@ class Animation( } } - override fun getName() = labelProperty.get() + override fun getName() = nameProperty.get() override fun getViewNode(): Node { if (!this::viewBox.isInitialized) { - viewBox = LabeledHBox(labelProperty).apply { - label.contextMenu = ContextMenu().apply { + viewBox = LabeledHBox(labelProperty, labelPrefix = "animation").apply { + nameProperty.bind(editableValueProperty.stringBinding(labelProperty) { + "${editableValueProperty.get()} (${labelProperty.get()})" + }) + contextmenu { + item("Rename") { + setOnAction { + editableProperty.set(true) + } + } menu("export") { menuItem("GIF") { BackgroundTasks.submit( @@ -163,4 +175,4 @@ class Animation( } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt index 57cdf65..2400412 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt @@ -17,8 +17,9 @@ import stan.qodat.scene.transform.Transformable abstract class AnimatedEntity( cache: Cache, definition: D, - private val animationProvider: D.() -> Array -) : Entity(cache, definition), Transformable, GroupableTransformable { + private val animationProvider: D.() -> Array, + labelPrefix: String? = null, +) : Entity(cache, definition, labelPrefix), Transformable, GroupableTransformable { private lateinit var animations: Array // private lateinit var skeletons: Map @@ -58,4 +59,4 @@ abstract class AnimatedEntity( for (model in getModels()) model.animate(frame) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt index 123141a..8b87ba2 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt @@ -37,7 +37,8 @@ import stan.qodat.util.getMaterial */ abstract class Entity( protected val cache: Cache, - val definition: D + val definition: D, + val labelPrefix: String? = null ) : Exportable, SceneNodeProvider, ViewNodeProvider, TreeItemProvider { private var modelGroup: Group? = null @@ -142,7 +143,7 @@ abstract class Entity( override fun getViewNode(): Node { if (!this::viewBox.isInitialized) { val optionalInt = definition.getOptionalId() - val box = LabeledHBox(labelProperty) + val box = LabeledHBox(labelProperty, labelPrefix = labelPrefix) viewBox = if (optionalInt.isPresent) HBox().apply { val id = optionalInt.asInt.toString() diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/Item.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/Item.kt index 3d693b0..dbfd7de 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/Item.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/Item.kt @@ -1,9 +1,9 @@ package stan.qodat.scene.runescape.entity -import stan.qodat.Properties import qodat.cache.Cache import qodat.cache.Encoder import qodat.cache.definition.ItemDefinition +import stan.qodat.Properties /** * TODO: add documentation @@ -12,7 +12,7 @@ import qodat.cache.definition.ItemDefinition * @since 28/01/2021 */ class Item(cache: Cache, definition: ItemDefinition) - : Entity(cache, definition), Encoder { + : Entity(cache, definition, "item"), Encoder { override fun property() = Properties.selectedItemName -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/NPC.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/NPC.kt index 72117b7..e95138f 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/NPC.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/NPC.kt @@ -1,10 +1,10 @@ package stan.qodat.scene.runescape.entity -import stan.qodat.Properties import qodat.cache.Cache import qodat.cache.EncodeResult import qodat.cache.Encoder import qodat.cache.definition.NPCDefinition +import stan.qodat.Properties import stan.qodat.cache.impl.oldschool.OldschoolCacheRuneLite import stan.qodat.scene.runescape.animation.Animation import java.io.UnsupportedEncodingException @@ -19,7 +19,7 @@ class NPC( cache: Cache = OldschoolCacheRuneLite, definition: NPCDefinition, animationProvider: NPCDefinition.() -> Array -) : AnimatedEntity(cache, definition, animationProvider), Encoder { +) : AnimatedEntity(cache, definition, animationProvider, "npc"), Encoder { override fun encode(format: Cache) : EncodeResult { throw UnsupportedEncodingException() @@ -28,4 +28,4 @@ class NPC( override fun toString(): String = getName() override fun property() = Properties.selectedNpcName -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/Object.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/Object.kt index 5c07061..477ca0b 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/Object.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/Object.kt @@ -1,10 +1,10 @@ package stan.qodat.scene.runescape.entity -import stan.qodat.Properties import qodat.cache.Cache import qodat.cache.EncodeResult import qodat.cache.Encoder import qodat.cache.definition.ObjectDefinition +import stan.qodat.Properties import stan.qodat.cache.impl.oldschool.OldschoolCacheRuneLite import stan.qodat.scene.runescape.animation.Animation import java.io.UnsupportedEncodingException @@ -19,11 +19,11 @@ class Object( cache: Cache = OldschoolCacheRuneLite, definition: ObjectDefinition, animationProvider: ObjectDefinition.() -> Array -) : AnimatedEntity(cache, definition, animationProvider), Encoder { +) : AnimatedEntity(cache, definition, animationProvider, "loc"), Encoder { override fun encode(format: Cache) : EncodeResult { throw UnsupportedEncodingException() } override fun property() = Properties.selectedObjectName -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt index 15f7d24..dc790c1 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt @@ -11,9 +11,9 @@ class SpotAnimation( cache: Cache = OldschoolCacheRuneLite, definition: SpotAnimationDefinition, animationProvider: SpotAnimationDefinition.() -> Array -) : AnimatedEntity(cache, definition, animationProvider), Encoder { +) : AnimatedEntity(cache, definition, animationProvider, labelPrefix = "spot_anim"), Encoder { override fun toString(): String = getName() override fun property() = Properties.selectedSpotAnimName -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt b/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt index b2bb3d5..9ddc2bc 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt @@ -186,7 +186,7 @@ class Model(label: String, override fun getViewNode(): Node { if (!this::viewBox.isInitialized) { - viewBox = LabeledHBox(labelProperty) + viewBox = LabeledHBox(labelProperty, labelPrefix = "model") } return viewBox } diff --git a/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt b/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt index a263dcf..2589ec4 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt @@ -24,7 +24,7 @@ class InterfaceGroup(val cache: Cache, private val groupId: Int, val definitions val nameProperty = SimpleStringProperty(idProperty.get().toString()) private val viewBox: HBox by lazy { - LabeledHBox(nameProperty) + LabeledHBox(nameProperty, labelPrefix = "widget") } private val sceneGroup: Group by lazy { @@ -48,4 +48,4 @@ class InterfaceGroup(val cache: Cache, private val groupId: Int, val definitions override fun treeItemExpandedProperty(): BooleanProperty = Properties.treeItemInterfaceExpanded -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt b/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt index 8c195a5..8f92928 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt @@ -38,7 +38,7 @@ class Sprite(val definition: SpriteDefinition) : SceneNodeProvider, ViewNodeProv } val viewNode: HBox by lazy { - LabeledHBox(nameProperty) .apply { + LabeledHBox(nameProperty, labelPrefix = "sprite").apply { contextmenu { styleClass += "wave-front-format-export-menu" for (exportFormat in SpriteExportFormat.all) diff --git a/src/main/kotlin/stan/qodat/util/Labels.kt b/src/main/kotlin/stan/qodat/util/Labels.kt index 282e692..b0e8c8a 100644 --- a/src/main/kotlin/stan/qodat/util/Labels.kt +++ b/src/main/kotlin/stan/qodat/util/Labels.kt @@ -1,6 +1,41 @@ package stan.qodat.util -object Labels { +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import stan.qodat.Properties +import kotlin.io.path.createFile +import kotlin.io.path.exists +import kotlin.io.path.readText +import kotlin.io.path.writeText +object LabelMapping { -} \ No newline at end of file + private val labelData by lazy { load() } + + private fun load(): MutableMap { + val labelData = mutableMapOf() + Properties.labelMappingPath.value?.let { path -> + if (path.exists()) + labelData.putAll(Json.decodeFromString(path.readText())) + } + return labelData + } + + private fun save() { + Properties.labelMappingPath.value?.let { path -> + if (!path.exists()) + path.createFile() + path.writeText(Json.encodeToString(labelData)) + } + } + + operator fun get(key: String): String? { + return labelData[key] + } + + operator fun set(key: String, value: String) { + labelData[key] = value + save() + } +} From 0a8fa8d67858dd22e541bfd55259971483aeef11 Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 16 Apr 2024 23:34:33 +0200 Subject: [PATCH 04/14] Fall back on old anim decoder in case new one fails, for older caches --- .../impl/oldschool/OldschoolCacheRuneLite.kt | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt b/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt index 81de1af..d7a3ceb 100644 --- a/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt +++ b/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt @@ -209,14 +209,26 @@ object OldschoolCacheRuneLite : Cache("LIVE") { val seqArchiveFiles = seqArchive.getFiles(seqArchiveData) animations = seqArchiveFiles.files.map { - val sequence = SequenceLoader().load(it.fileId, it.contents) - return@map object : AnimationDefinition { - override val id: String = it.fileId.toString() - override val frameHashes: IntArray = sequence.frameIDs?: IntArray(0) - override val frameLengths: IntArray = sequence.frameLenghts?: IntArray(0) - override val loopOffset: Int = sequence.frameStep - override val leftHandItem: Int = sequence.leftHandItem - override val rightHandItem: Int = sequence.rightHandItem + try { + val sequence = SequenceLoader().load(it.fileId, it.contents) + object : AnimationDefinition { + override val id: String = it.fileId.toString() + override val frameHashes: IntArray = sequence.frameIDs?: IntArray(0) + override val frameLengths: IntArray = sequence.frameLenghts?: IntArray(0) + override val loopOffset: Int = sequence.frameStep + override val leftHandItem: Int = sequence.leftHandItem + override val rightHandItem: Int = sequence.rightHandItem + } + } catch (e: Exception) { + val sequence = SequenceLoader206().load(it.fileId, it.contents) + object : AnimationDefinition { + override val id: String = it.fileId.toString() + override val frameHashes: IntArray = sequence.frameIDs?: IntArray(0) + override val frameLengths: IntArray = sequence.frameLenghts?: IntArray(0) + override val loopOffset: Int = sequence.frameStep + override val leftHandItem: Int = sequence.leftHandItem + override val rightHandItem: Int = sequence.rightHandItem + } } }.toTypedArray() } From f6e467626526344f6b3da812f4f38dd836dd390d Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 16 Apr 2024 23:38:44 +0200 Subject: [PATCH 05/14] Fix loading of interfaces --- .../qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt b/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt index d7a3ceb..004dcd3 100644 --- a/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt +++ b/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt @@ -283,10 +283,9 @@ object OldschoolCacheRuneLite : Cache("LIVE") { override fun getRootInterfaces(): Map> = interfaceManager.interfaces - ?.flatten() - ?.map { RuneliteIntefaceDefinition(it) } - ?.groupBy { it.id.shr(16) } - ?: emptyMap() + .filterNotNull() + .flatMap { components -> components.mapNotNull { RuneliteIntefaceDefinition(it) } } + .groupBy { it.id.shr(16) } override fun getSprites(): Array = spriteManager.sprites.map { RuneliteSpriteDefinition(it) }.toTypedArray() From 351c3ce99c71eb1e72254d644e22b16b2065164e Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 16 Apr 2024 23:39:50 +0200 Subject: [PATCH 06/14] Fix sequence definition label for 206 rev version --- .../definition/SequenceDefinition206.kt | 38 ++++++------------- .../oldschool/loader/SequenceLoader206.kt | 5 ++- 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/main/kotlin/stan/qodat/cache/impl/oldschool/definition/SequenceDefinition206.kt b/src/main/kotlin/stan/qodat/cache/impl/oldschool/definition/SequenceDefinition206.kt index 4f2011a..86f21d2 100644 --- a/src/main/kotlin/stan/qodat/cache/impl/oldschool/definition/SequenceDefinition206.kt +++ b/src/main/kotlin/stan/qodat/cache/impl/oldschool/definition/SequenceDefinition206.kt @@ -1,30 +1,9 @@ -/* - * Copyright (c) 2016-2017, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ package stan.qodat.cache.impl.oldschool.definition -class SequenceDefinition206(val id: Int) { +import qodat.cache.definition.AnimationDefinition + +class SequenceDefinition206(override val id: String) : AnimationDefinition { + var frameIDs : IntArray? = null var chatFrameIds: IntArray? = null var frameLenghts: IntArray? = null @@ -33,11 +12,16 @@ class SequenceDefinition206(val id: Int) { var interleaveLeave: IntArray? = null var stretches = false var forcedPriority = 5 - var leftHandItem = -1 - var rightHandItem = -1 var maxLoops = 99 var precedenceAnimating = -1 var priority = -1 var replyMode = 2 var cachedModelId = -1 + + override val frameHashes: IntArray get() = frameIDs!! + override val frameLengths: IntArray get() = frameLenghts!! + override val loopOffset: Int get() = frameStep + override var leftHandItem = -1 + override var rightHandItem = -1 + } diff --git a/src/main/kotlin/stan/qodat/cache/impl/oldschool/loader/SequenceLoader206.kt b/src/main/kotlin/stan/qodat/cache/impl/oldschool/loader/SequenceLoader206.kt index ea07852..dbf8818 100644 --- a/src/main/kotlin/stan/qodat/cache/impl/oldschool/loader/SequenceLoader206.kt +++ b/src/main/kotlin/stan/qodat/cache/impl/oldschool/loader/SequenceLoader206.kt @@ -11,7 +11,7 @@ import stan.qodat.cache.impl.oldschool.definition.SequenceDefinition206 class SequenceLoader206 { fun load(id: Int, b: ByteArray): SequenceDefinition206 { - val def = SequenceDefinition206(id) + val def = SequenceDefinition206(id.toString()) val `is` = InputStream(b) while (true) { val opcode = `is`.readUnsignedByte() @@ -26,6 +26,9 @@ class SequenceLoader206 { private fun SequenceDefinition206.decodeValues(opcode: Int, stream: InputStream) { val length: Int var i: Int + if (id == "7981") { + println() + } when (opcode) { 1 -> { length = stream.readUnsignedShort() From 1e6a33bb385bf67a4c54358baa16166077628f17 Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 16 Apr 2024 23:40:02 +0200 Subject: [PATCH 07/14] Assert models path is not null --- .../qodat/cache/impl/legacy/storage/LegacyModelStorage.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/stan/qodat/cache/impl/legacy/storage/LegacyModelStorage.kt b/src/main/kotlin/stan/qodat/cache/impl/legacy/storage/LegacyModelStorage.kt index 2a5a6fc..d670fbc 100644 --- a/src/main/kotlin/stan/qodat/cache/impl/legacy/storage/LegacyModelStorage.kt +++ b/src/main/kotlin/stan/qodat/cache/impl/legacy/storage/LegacyModelStorage.kt @@ -18,7 +18,7 @@ object LegacyModelStorage { private val modelMap = HashMap() fun getModel(cachePath: Path, modelId: String) = modelMap.getOrPut(modelId) { - val compressedData = Files.readAllBytes(cachePath.resolve("all_models").resolve("$modelId.gz"))!! + val compressedData = Files.readAllBytes(cachePath.resolve("all_models").resolve("$modelId.gz")) RSModelLoader().load(modelId, CompressionUtil.uncrompressGzip(compressedData)) } -} \ No newline at end of file +} From fa867472f8d307c297125adb0dce1def28640deb Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 16 Apr 2024 23:40:41 +0200 Subject: [PATCH 08/14] Init AWT toolkit in main method to avoid mac os exception https://bugs.java.com/bugdatabase/view_bug?bug_id=8318129 --- src/main/kotlin/stan/qodat/Launcher.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/stan/qodat/Launcher.kt b/src/main/kotlin/stan/qodat/Launcher.kt index 70b741f..bd56ac0 100644 --- a/src/main/kotlin/stan/qodat/Launcher.kt +++ b/src/main/kotlin/stan/qodat/Launcher.kt @@ -1,11 +1,13 @@ package stan.qodat +import java.awt.Toolkit import kotlin.time.ExperimentalTime object Launcher { @ExperimentalTime @JvmStatic fun main(args: Array) { - Qodat.main(args) + Toolkit.getDefaultToolkit() + Qodat.main(args) } -} \ No newline at end of file +} From 768995e9a99886525434f25326cf0be8e21647c6 Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 16 Apr 2024 23:40:58 +0200 Subject: [PATCH 09/14] Overwrite model files when drag and dropping --- .../kotlin/stan/qodat/scene/controller/ModelController.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/stan/qodat/scene/controller/ModelController.kt b/src/main/kotlin/stan/qodat/scene/controller/ModelController.kt index af7710c..b47e566 100644 --- a/src/main/kotlin/stan/qodat/scene/controller/ModelController.kt +++ b/src/main/kotlin/stan/qodat/scene/controller/ModelController.kt @@ -79,7 +79,7 @@ class ModelController : Initializable { if (syncPath != null) { for ((file, _) in it) { val savePath = syncPath.resolve(file.name) - file.copyTo(savePath.toFile()) + file.copyTo(savePath.toFile(), overwrite = true) } } else { for ((_, model) in it) @@ -183,4 +183,4 @@ class ModelController : Initializable { companion object { var watchThread: Thread? = null } -} \ No newline at end of file +} From 528e491cebcfbba5d59237f174a4d5fc4f35da94 Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Wed, 17 Apr 2024 00:55:11 +0200 Subject: [PATCH 10/14] Start of adding new widget rendering system --- .../scene/control/tree/InterfaceTreeItem.kt | 62 +- .../qodat/scene/runescape/widget/Widget.kt | 17 + .../scene/runescape/widget/WidgetRendering.kt | 453 ++ .../runescape/widget/component/ButtonType.kt | 11 + .../runescape/widget/component/Colour.kt | 13 + .../runescape/widget/component/Component.kt | 31 + .../widget/component/ComponentReferences.kt | 38 + .../scene/runescape/widget/component/Font.kt | 36 + .../runescape/widget/component/Models.kt | 4 + .../scene/runescape/widget/component/Pos.kt | 37 + .../scene/runescape/widget/component/Size.kt | 20 + .../runescape/widget/component/Sprites.kt | 5076 +++++++++++++++++ .../runescape/widget/component/TextAlign.kt | 10 + .../widget/component/impl/Graphic.kt | 36 + .../widget/component/impl/Inventory.kt | 22 + .../runescape/widget/component/impl/Layer.kt | 40 + .../runescape/widget/component/impl/Line.kt | 22 + .../runescape/widget/component/impl/Model.kt | 21 + .../widget/component/impl/ModelList.kt | 21 + .../widget/component/impl/Rectangle.kt | 21 + .../runescape/widget/component/impl/Text.kt | 28 + 21 files changed, 6014 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/Widget.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/WidgetRendering.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/ButtonType.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/Colour.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/Component.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/ComponentReferences.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/Font.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/Models.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/Pos.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/Size.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/Sprites.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/TextAlign.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Graphic.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Inventory.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Layer.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Line.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Model.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/ModelList.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Rectangle.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Text.kt diff --git a/src/main/kotlin/stan/qodat/scene/control/tree/InterfaceTreeItem.kt b/src/main/kotlin/stan/qodat/scene/control/tree/InterfaceTreeItem.kt index 54c6478..2b30674 100644 --- a/src/main/kotlin/stan/qodat/scene/control/tree/InterfaceTreeItem.kt +++ b/src/main/kotlin/stan/qodat/scene/control/tree/InterfaceTreeItem.kt @@ -15,10 +15,21 @@ import javafx.scene.shape.Line import javafx.scene.shape.Rectangle import javafx.scene.text.Text import javafx.util.Duration +import qodat.cache.definition.InterfaceDefinition +import stan.qodat.cache.impl.oldschool.OldschoolCacheRuneLite import stan.qodat.javafx.label import stan.qodat.javafx.text import stan.qodat.javafx.treeItem import stan.qodat.scene.runescape.ui.InterfaceGroup +import stan.qodat.scene.runescape.ui.Sprite +import stan.qodat.scene.runescape.widget.Widget +import stan.qodat.scene.runescape.widget.component.Component +import stan.qodat.scene.runescape.widget.component.Pos +import stan.qodat.scene.runescape.widget.component.Size +import stan.qodat.scene.runescape.widget.component.impl.Graphic +import stan.qodat.scene.runescape.widget.component.impl.Inventory +import stan.qodat.scene.runescape.widget.component.impl.Layer +import stan.qodat.scene.runescape.widget.component.impl.Model import stan.qodat.util.ModelUtil import stan.qodat.util.onInvalidation @@ -30,6 +41,13 @@ class InterfaceTreeItem(val group: InterfaceGroup, val selectionModel: MultipleS label(group.nameProperty) text(group.javaClass.simpleName, Color.web("#FFC66D")) + val widget = Widget() + + val componentsById = group.definitions.associateBy { it.id.and(0xffff) } + componentsById.forEach { (id, comp) -> + val component = convert(comp, componentsById, id) + widget.children.add(component) + } do3d.selectedProperty().onInvalidation { if (get()) { @@ -62,6 +80,42 @@ class InterfaceTreeItem(val group: InterfaceGroup, val selectionModel: MultipleS expandedProperty().set(group.treeItemExpandedProperty().get()) } + private fun convert( + base: InterfaceDefinition, + componentsById: Map, + id: Int, + ): Component<*> { + val component = when (base.type) { + 0 -> Layer().apply { + scrollX = 0 + scrollY = 0 + scrollHeight = base.scrollHeight + children.addAll(componentsById.values.filter { it.parentId.and(0xffff) == id }.map { convert(it, componentsById, it.id.and(0xffff))}) + } + 2 -> Inventory().apply { + + } + 3 -> stan.qodat.scene.runescape.widget.component.impl.Rectangle().apply { + } + 4 -> stan.qodat.scene.runescape.widget.component.impl.Text() + 5 -> Graphic() + 6 -> Model() + 9 -> stan.qodat.scene.runescape.widget.component.impl.Line() + else -> error("Unsupported component type: $base") + }.apply { + name = base.id.toString() + x = base.originalX + y = base.originalY + width = base.originalWidth + height = base.originalHeight + hSize = Size.fromId(base.widthMode) + vSize = Size.fromId(base.heightMode) + hPos = Pos.fromIdHor(base.xPositionMode) + vPos = Pos.fromIdVer(base.yPositionMode) + } + return component + } + private fun TreeItem.add(sceneGroup: Parent, depth: Int) { for (child in children) { if (child is InterfaceComponentTreeItem) { @@ -114,10 +168,8 @@ class InterfaceTreeItem(val group: InterfaceGroup, val selectionModel: MultipleS fill = ModelUtil.hsbToColor(def.textColor, def.opacity.toByte()) } 5 -> { - val view = ImageView((child.children.find { it.graphic is ImageView }?.graphic as ImageView).image) -// view.x = bounds.x -// view.y = bounds.y - view + val sprite = def.spriteId.takeIf { it > 0 }?.let { OldschoolCacheRuneLite.getSprite(it, 0) }?.let { Sprite(it) } + ImageView(sprite?.image) } 6 -> Group() 9 -> Line() @@ -147,4 +199,4 @@ class InterfaceTreeItem(val group: InterfaceGroup, val selectionModel: MultipleS } return null } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/Widget.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/Widget.kt new file mode 100644 index 0000000..782cecc --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/Widget.kt @@ -0,0 +1,17 @@ +package stan.qodat.scene.runescape.widget + +import stan.qodat.scene.runescape.widget.component.Component +import stan.qodat.scene.runescape.widget.component.Pos +import stan.qodat.scene.runescape.widget.component.Size +import stan.qodat.scene.runescape.widget.component.WidgetRef +import stan.qodat.scene.runescape.widget.component.impl.Layer +import kotlin.reflect.KProperty + +class Widget { + + val children = mutableListOf>() + + operator fun getValue(any: Any?, property: KProperty<*>) = + WidgetRef(property.name, this) + +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/WidgetRendering.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/WidgetRendering.kt new file mode 100644 index 0000000..9db1cca --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/WidgetRendering.kt @@ -0,0 +1,453 @@ +package stan.qodat.scene.runescape.widget + +import javafx.scene.Group +import javafx.scene.image.ImageView +import javafx.scene.layout.Pane +import qodat.cache.Cache +import stan.qodat.scene.runescape.ui.Sprite +import stan.qodat.scene.runescape.widget.component.Component +import stan.qodat.scene.runescape.widget.component.Pos +import stan.qodat.scene.runescape.widget.component.Size +import stan.qodat.scene.runescape.widget.component.Sprites +import stan.qodat.scene.runescape.widget.component.impl.* +import stan.qodat.util.ModelUtil +import tornadofx.imageview +import tornadofx.line +import tornadofx.rectangle +import kotlin.math.max +import kotlin.math.min + +data class DrawDimensions( + val width: Int, + val height: Int, + val x: Int, + val y: Int, +) + +class WidgetRenderer( + val cache: Cache, +) { + + class RenderContext( + val nestedLevel: Int, + val maxWidth: Int, + val maxHeight: Int, + val clipLeft: Int, + val clipTop: Int, + val clipRight: Int, + val clipBottom: Int, + val absXOffset: Int, + val absYOffset: Int, + ) { + private val drawDimensionsMap = mutableMapOf, DrawDimensions>() + private val Component<*>.drawDimensions + get() = drawDimensionsMap.getOrPut(this) { + DrawDimensions( + width = when (hSize) { + Size.abs -> width + Size.minus -> maxWidth - width + Size.proportion -> maxWidth * width shr 14 + else -> error("Unhandled hSize $hSize") + }, + height = when (vSize) { + Size.abs -> height + Size.minus -> maxHeight - height + Size.proportion -> maxHeight * height shr 14 + else -> error("Unhandled vSize $vSize") + }, + x = when (hPos) { + Pos.abs_left -> x + Pos.abs_centre -> x + (maxWidth - width) / 2 + Pos.abs_right -> maxWidth - width - x + Pos.proportion_left -> (maxWidth * x) shr 14 + Pos.proportion_centre -> ((maxWidth * x) shr 14) + ((maxWidth - width) / 2) + Pos.proportion_right -> (maxWidth - width - ((x * maxWidth) shr 14)) + else -> error("Unhandled hPos $hPos") + }, + y = when (vPos) { + Pos.abs_top -> y + Pos.abs_centre -> ((maxHeight - height) / 2) + y + Pos.abs_bottom -> maxHeight - height - y + Pos.proportion_top -> (maxHeight * y) shr 14 + Pos.proportion_centre -> ((maxHeight * y) shr 14) + ((maxHeight - height) / 2) + Pos.proportion_bottom -> (maxHeight - height - ((maxHeight * y) shr 14)) + else -> error("Unhandled hPos $hPos") + } + ) + } + val Component<*>.drawWidth get() = drawDimensions.width + val Component<*>.drawHeight get() = drawDimensions.height + val Component<*>.drawX get() = drawDimensions.x + val Component<*>.drawY get() = drawDimensions.y + + fun debugLn(message: String) { + repeat(nestedLevel) { print('\t') } + println(message) + } + } + + fun render(widget: Widget, width: Int, height: Int): Group { + val context = RenderContext(0, width, height, 0, 0, width, height, 0, 0) + return context.renderComponents(widget.children) + } + + private fun RenderContext.renderComponents(components: List>) = Group().apply { + for (component in components) { +// setClip(clipLeft, clipTop, clipRight, clipBottom) + if (component.hidden) { + debugLn("Skipping rendering of $component because it is hidden") + continue + } + val absX = component.drawX + absXOffset + val absY = component.drawY + absYOffset + val (startX, startY, endX, endY) = if (component is Inventory) { + listOf(clipLeft, clipTop, clipRight, clipBottom) + } else { + var absWidth: Int = component.drawWidth + absX + var absHeight: Int = component.drawHeight + absY + if (component is Line) { + absWidth++ + absHeight++ + } + listOf(max(clipLeft, absX), max(clipTop, absY), min(clipRight, absWidth), min(clipBottom, absHeight)) + } + if (startX < endX && startY < endY) { + + debugLn("Rendering at ($absX, $absY) in bounds ($startX..$endX, $startY..$endY) - $component") + children.add(when (component) { + is Layer -> renderLayer(component, absX, absY, startX, startY, endX, endY) + is Rectangle -> renderRectangle(component, absX, absY) + is Text -> renderText(component, absX, absY) + is Graphic -> renderGraphic(component, absX, absY) + else -> continue + }) + } else { + debugLn("Not rendering at ($absX, $absY) in bounds ($startX..$endX, $startY..$endY) - $component") + } + } + } + + + val field643: Int = 16777215.inv() or 2301979 + val field584: Int = 16777215.inv() or 5063219 + val field646: Int = 16777215.inv() or 7759444 + val field836: Int = 16777215.inv() or 3353893 + private fun RenderContext.renderLayer( + component: Layer, + absX: Int, + absY: Int, + startX: Int, + startY: Int, + endX: Int, + endY: Int, + ): Pane = with(component) { + val context = RenderContext( + nestedLevel + 1, + endX - startX, + endY - startY, + startX, + startY, + endX, + endY, + absX - component.scrollX, + absY - component.scrollY + ) + val pane = Pane() + val subChildren: Group = context.renderComponents(children) + pane.children.addAll(subChildren) + + val scrollHeight = scrollHeight + if (scrollHeight > drawHeight) { + val scrollBarX = absX + drawWidth + pane.imageview(Sprite(cache.getSprite(316, 0)).image) { + x = scrollBarX.toDouble() + y = absY.toDouble() + } + pane.imageview(Sprite(cache.getSprite(316, 1)).image) { + x = scrollBarX.toDouble() + y = absY + drawHeight - 16.toDouble() + } + pane.rectangle( + x = scrollBarX, + y = absY + 16, + width = 16, + height = drawHeight - 32, + ) { + fill = ModelUtil.hsbToColor(field643, 100) + } + + val scrollBarHeight = ((drawHeight * (drawHeight - 32)) / scrollHeight).coerceAtLeast(8) + val int_23: Int = scrollY * (drawHeight - 32 - scrollBarHeight) / (scrollHeight - drawHeight) + pane.rectangle( + x = scrollBarX, + y = absY + int_23 + 16, + width = 16, + height = scrollBarHeight, + ) { + fill = ModelUtil.hsbToColor(field584, 100) + } + pane.line( + startX = scrollBarX.toDouble(), + startY = absY + int_23 + 16.toDouble(), + endX = scrollBarX.toDouble(), + endY = int_23 + absY + 16 + scrollBarHeight.toDouble(), + ) { + stroke = ModelUtil.hsbToColor(field646, 100) + } + pane.line( + startX = scrollBarX + 1.toDouble(), + startY = absY + int_23 + 16.toDouble(), + endX = scrollBarX + 1.toDouble(), + endY = int_23 + absY + 16 + scrollBarHeight.toDouble(), + ) { + stroke = ModelUtil.hsbToColor(field646, 100) + } + pane.line( + startX = scrollBarX.toDouble(), + startY = absY + int_23 + 16.toDouble(), + endX = scrollBarX + 16.toDouble(), + endY = int_23 + absY + 16.toDouble(), + ) { + stroke = ModelUtil.hsbToColor(field646, 100) + } + pane.line( + startX = scrollBarX.toDouble(), + startY = int_23 + absY + 17.toDouble(), + endX = scrollBarX + 16.toDouble(), + endY = int_23 + absY + 17.toDouble(), + ) { + stroke = ModelUtil.hsbToColor(field646, 100) + } + pane.line( + startX = scrollBarX + 15.toDouble(), + startY = absY + int_23 + 16.toDouble(), + endX = scrollBarX + 15.toDouble(), + endY = int_23 + absY + 16 + scrollBarHeight.toDouble(), + ) { + stroke = ModelUtil.hsbToColor(field836, 100) + } + pane.line( + startX = scrollBarX + 14.toDouble(), + startY = int_23 + absY + 17.toDouble(), + endX = scrollBarX + 14.toDouble(), + endY = int_23 + absY + 16 + scrollBarHeight - 1.toDouble(), + ) { + stroke = ModelUtil.hsbToColor(field836, 100) + } + pane.line( + startX = scrollBarX.toDouble(), + startY = absY + int_23 + scrollHeight + 15.toDouble(), + endX = scrollBarX + 16.toDouble(), + endY = int_23 + absY + scrollHeight + 15.toDouble(), + ) { + stroke = ModelUtil.hsbToColor(field836, 100) + } + pane.line( + startX = scrollBarX + 1.toDouble(), + startY = int_23 + absY + scrollHeight + 14.toDouble(), + endX = scrollBarX + 15.toDouble(), + endY = int_23 + absY + scrollHeight + 14.toDouble(), + ) { + stroke = ModelUtil.hsbToColor(field836, 100) + } + +// toolkit.fillRectangle(scrollBarX, absY + int_23 + 16, 16, scrollBarHeight, field584, 1) +// toolkit.drawVerticalLine(scrollBarX, int_23 + absY + 16, scrollBarHeight, field646, 1) +// toolkit.drawVerticalLine(scrollBarX + 1, absY + int_23 + 16, scrollBarHeight, field646, 1) +// toolkit.drawHorizontalLine(scrollBarX, absY + int_23 + 16, 16, field646, 1) +// toolkit.drawHorizontalLine(scrollBarX, int_23 + absY + 17, 16, field646, 1) +// toolkit.drawVerticalLine(scrollBarX + 15, absY + int_23 + 16, scrollBarHeight, field836, 1) +// toolkit.drawVerticalLine(scrollBarX + 14, int_23 + absY + 17, scrollBarHeight - 1, field836, 1) +// toolkit.drawHorizontalLine(scrollBarX, absY + int_23 + scrollHeight + 15, 16, field836, 1) +// toolkit.drawHorizontalLine(scrollBarX + 1, int_23 + absY + scrollHeight + 14, 15, field836, 1) + } + pane + } + + private fun RenderContext.renderRectangle(component: Rectangle, x: Int, y: Int): javafx.scene.shape.Rectangle = with(component) { + val color = if (trans == 0) + 0xffffff.inv() or (colour?.value ?: 0) + else + 255 - (trans and 0xff) shl 24 or ((colour?.value ?: 0) and 0xffffff) + javafx.scene.shape.Rectangle(x.toDouble(), y.toDouble()).apply { + width = drawWidth.toDouble() + height = drawHeight.toDouble() + if (filled) + fill = ModelUtil.hsbToColor(color, 100) + else + stroke = ModelUtil.hsbToColor(color, 100) + } + } + + private fun RenderContext.renderText( + component: Text, + absX: Int, + absY: Int, + ) = with(component) { + Group() +// var shadowColor: Int = (255 - (trans and 0xff)) +// if (shadowColor == 0) +// return@with +// val font = font?.run(this@WidgetRenderer::createFont) +// ?: error("Did not find font $font for text $component") +// val text = itemId +// ?.run { +// val itemDef = cache.getItemDefinition(this) +// val text = itemDef?.name ?: "null" +// itemAmount +// ?.takeIf { itemDef.stackable == 1 || it > 1 } +// ?.run { getColTags(16748608) + text + aString94 + " x" + method7251(this) } +// ?: text +// } +// ?: text +// if (clipWidgetComponents) +// toolkit.ensureClips(absX, absY, drawWidth + absX, drawHeight + absY) +// shadowColor = shadowColor shl 24 +// font.drawString( +// text, +// absX, +// absY, +// drawWidth, +// drawHeight, +// shadowColor or (colour?.value ?: 0), +// if (shadowed) shadowColor else -1, +// hAlign.id, +// vAlign.id, +// lineHeight, +// 0, +// cache.crownSprites, +// null, +// null, +// 0, +// 0, +// cache +// ) +// if (clipWidgetComponents) +// toolkit.setClip(clipLeft, clipTop, clipRight, clipBottom) + } + +// private fun createFont(font: Font): JagexFont { +// val group = cache.getSpriteGroupDefinition(font.id) +// if (group?.list.isNullOrEmpty()) +// error("Sprite group is null or empty for font $font (group = $group)") +// val fontMetrics = try { +// cache.getFontMetrics(font.id) +// ?: error("Did not find font metrics for font $font") +// } catch (e: Exception) { +// throw Exception("Failed to load font metrics for font $font", e) +// } +// val sprites = group.toArray().filterIsInstance().toTypedArray() +// return toolkit.createFont(fontMetrics, sprites, false) +// } + + private fun RenderContext.renderGraphic( + component: Graphic, + absX: Int, + absY: Int, + ): ImageView = with(component) { + val sprite: ImageView = + sprite?.run { createSprite(this, flipH, flipV, borderThickness, shadowColour?.value ?: 0) } + ?:return@with ImageView() + + val spriteWidth: Int = sprite.fitWidth.toInt() + val spriteHeight: Int = sprite.fitHeight.toInt() + val spriteColour: Int = 255 - (trans and 0xff) + if (spriteColour == 0) { + debugLn("Not rendering $component because spriteColour == 0 ") + return@with ImageView() + } + val colour = (colour?.value + ?.takeUnless { it == -1 } + ?.run { this and 0xffffff } + ?.coerceAtLeast(16777215) + ?: 16777215) or (spriteColour shl 24) + val blend = -1 != colour + val is317 = false + if (repeatSprite) { +// toolkit.ensureClips(absX, absY, drawWidth + absX, drawHeight + absY) + if (spriteRotation != 0) { + val chunkX: Int = (drawWidth + (spriteWidth - 1)) / spriteWidth + val chunkY: Int = (spriteHeight - 1 + drawHeight) / spriteHeight + for (dx in 0 until chunkX) { + for (dy in 0 until chunkY) { + val drawX = spriteWidth.toFloat() / 2.0f + (dx * spriteWidth + absX).toFloat() + val drawY = (dy * spriteHeight + absY).toFloat() + spriteHeight.toFloat() / 2.0f +// if (blend) +// sprite.drawRotated(drawX, drawY, 4096, spriteRotation, 0, colour, 1) +// else +// sprite.drawRotated(drawX, drawY, 4096, spriteRotation) + } + } + } +// else if (blend) { +// +// sprite.drawRepeated(absX, absY, drawWidth, drawHeight, 0, colour, 1) +// }else +// sprite.drawRepeated(absX, absY, drawWidth, drawHeight) +// toolkit.setClip(clipLeft, clipTop, clipRight, clipBottom); + } else if (blend) { +// if (spriteRotation != 0) +// sprite.drawRotated( +// drawWidth.toFloat() / 2.0f + absX.toFloat(), +// absY.toFloat() + drawHeight.toFloat() / 2.0f, +// 4096 * drawWidth / spriteWidth, +// spriteRotation, +// 0, +// colour, +// 1 +// ) +// else if (drawWidth != spriteWidth || spriteHeight != drawHeight) +// sprite.drawScaled(absX, absY, drawWidth, drawHeight, 0, colour, 1) +// else +// sprite.draw(absX, absY, 0, colour, 1) + } +// else if (spriteRotation != 0) +// sprite.drawRotated( +// drawWidth.toFloat() / 2.0f + absX.toFloat(), +// drawHeight.toFloat() / 2.0f + absY.toFloat(), +// 4096 * drawWidth / spriteWidth, +// spriteRotation +// ) +// else if (!is317 && (drawWidth != spriteWidth || drawHeight != spriteHeight)) +// sprite.drawScaled(absX, absY, drawWidth, drawHeight) +// else +// sprite.draw(absX, absY) + sprite?:ImageView() + } + + private fun createSprite( + sprites: Sprites, + flipH: Boolean, + flipV: Boolean, + borderThickness: Int, + shadowColour: Int, + ): ImageView { + val sprite = ImageView(Sprite(cache.getSprite(sprites.groupId, sprites.index)).image) + if (flipV) sprite.scaleY = -1.0 + if (flipH) sprite.scaleX = -1.0 +// if (borderThickness > 0) indexedSprite.outlineToSprite(borderThickness) +// else if (shadowColour != 0) indexedSprite.outlineToSprite(1) +// if (borderThickness >= 1) indexedSprite.addOutline(1) +// if (borderThickness >= 2) indexedSprite.addOutline(16777215) +// if (shadowColour != 0) indexedSprite.fillTransparentAreas(0xffffff.inv() or shadowColour) +// return toolkit.createSprite(indexedSprite, false) + return sprite + } + + private fun RenderContext.renderModel( + component: Model, + absX: Int, + absY: Int, + ) = with(component) { +// toolkit.method7493() +// +// val currentBrightness = 0 +// +// toolkit.setAmbientIntensity((0.7f + currentBrightness.toFloat() * 0.1f) * 1.1523438f) +// toolkit.setSun(anInt4824, 0.69921875f, 1.2f, -200.0f, -240.0f, -200.0f) +// toolkit.setFog(DEFAULT_SKY_COLOR, -1, 0) +// +// // TOOD: finish + Group() + } +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/ButtonType.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/ButtonType.kt new file mode 100644 index 0000000..13bd7c9 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/ButtonType.kt @@ -0,0 +1,11 @@ +package stan.qodat.scene.runescape.widget.component + +sealed class ButtonType(val id: Int) { + data object None : ButtonType(0) + data object Ok : ButtonType(1) + data object Target : ButtonType(2) + data object Close : ButtonType(3) + data object Check : ButtonType(4) + data object Toggle : ButtonType(5) + data object Pause : ButtonType(6) +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Colour.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Colour.kt new file mode 100644 index 0000000..d827c1c --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Colour.kt @@ -0,0 +1,13 @@ +package stan.qodat.scene.runescape.widget.component + +sealed class Colour(val value: Int) { + object red : Colour(16711680) + object green : Colour(65280) + object blue : Colour(255) + object yellow : Colour(16776960) + object magenta : Colour(16711935) + object cyan : Colour(65535) + object white : Colour(16777215) + object black : Colour(0) + class Hex(value: Int) : Colour(value) +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Component.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Component.kt new file mode 100644 index 0000000..dc6ddb6 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Component.kt @@ -0,0 +1,31 @@ +package stan.qodat.scene.runescape.widget.component + +import kotlin.reflect.KProperty + +abstract class Component> { + + abstract var name: String? + abstract var x: Int + abstract var y: Int + abstract var width: Int + abstract var height: Int + abstract var hSize: Size + abstract var vSize: Size + abstract var hPos: Pos + abstract var vPos: Pos + + var hidden: Boolean = false + + var buttonType: ButtonType? = null + var parent: Component<*>? = null + + operator fun getValue(any: Any?, property: KProperty<*>) : R { + name = property.name + return toReference(property.name) + } + + abstract fun toReference(name: String): R + + override fun toString(): String = + name?.let { "${javaClass.simpleName}:$it" }?:super.toString() +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/ComponentReferences.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/ComponentReferences.kt new file mode 100644 index 0000000..fe3d1a6 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/ComponentReferences.kt @@ -0,0 +1,38 @@ +package stan.qodat.scene.runescape.widget.component + +import stan.qodat.scene.runescape.widget.Widget +import stan.qodat.scene.runescape.widget.component.impl.* +import kotlin.reflect.KProperty + + +abstract class Ref { + abstract val name: String + abstract val value: T +} + +class WidgetRef(override val name: String, override val value: Widget) : Ref() + +class LayerRef(override val name: String, override val value: Layer) : Ref() +class GraphicRef(override val name: String, override val value: Graphic) : Ref() +class RectangleRef(override val name: String, override val value: Rectangle) : Ref() +class TextRef(override val name: String, override val value: Text) : Ref() +class ModelListRef(override val name: String, override val value: ModelList) : Ref() +class InventoryRef(override val name: String, override val value: Inventory) : Ref() +class ModelRef(override val name: String, override val value: Model) : Ref() +class LineRef(override val name: String, override val value: Line) : Ref() + +class ProtoReference> { + lateinit var reference: Ref + operator fun setValue(any: Any?, property: KProperty<*>, value: Ref) { + reference = value.value.toReference(property.name) as Ref + } + operator fun getValue(t: Any?, property: KProperty<*>): Ref = + reference + +} +class UnnamedRef>(override val value: T) : Ref() { + override val name: String get() = "" +} +fun> lazyComponent() = ProtoReference() +fun> T.toUnnamedRef() = UnnamedRef(this) +fun> lazilyNamed(block: () -> T) = block().toUnnamedRef() diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Font.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Font.kt new file mode 100644 index 0000000..4668c28 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Font.kt @@ -0,0 +1,36 @@ +package stan.qodat.scene.runescape.widget.component + +sealed class Font(val id: Int){ + object verdana_11pt_regular : Font(1442) + object verdana_11pt_bold : Font(1443) + object quill_oblique_large : Font(645) + object verdana_13pt_regular : Font(1445) + object quill_caps_large : Font(646) + object verdana_13pt_bold : Font(1446) + object lunar_alphabet : Font(647) + object verdana_15pt_regular : Font(1447) + object lunar_alphabet_lrg : Font(648) + object p11_full : Font(494) + object p12_full : Font(495) + object b12_full : Font(496) + object q8_full : Font(497) + object surok_font : Font(819) + object barbassault_font : Font(764) +} +/* +1442 verdana_11pt_regular +1443 verdana_11pt_bold +645 quill_oblique_large +1445 verdana_13pt_regular +646 quill_caps_large +1446 verdana_13pt_bold +647 lunar_alphabet +1447 verdana_15pt_regular +648 lunar_alphabet_lrg +494 p11_full +495 p12_full +496 b12_full +497 q8_full +819 surok_font +764 barbassault_font + */ diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Models.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Models.kt new file mode 100644 index 0000000..b60c230 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Models.kt @@ -0,0 +1,4 @@ +package stan.qodat.scene.runescape.widget.component + +sealed class Models(val id: Int) { +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Pos.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Pos.kt new file mode 100644 index 0000000..c87d179 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Pos.kt @@ -0,0 +1,37 @@ +package stan.qodat.scene.runescape.widget.component + +sealed class Pos(val id: Int) { + + data object abs_left : Pos(0) + data object abs_centre : Pos(1) + data object abs_right : Pos(2) + data object proportion_left : Pos(3) + data object proportion_centre : Pos(4) + data object proportion_right : Pos(5) + + data object abs_top : Pos(0) + data object abs_bottom : Pos(2) + data object proportion_top : Pos(3) + data object proportion_bottom : Pos(5) + + companion object { + fun fromIdHor(id: Int) = when (id) { + 0 -> abs_left + 1 -> abs_centre + 2 -> abs_right + 3 -> proportion_left + 4 -> proportion_centre + 5 -> proportion_right + else -> throw IllegalArgumentException("Invalid Pos id: $id") + } + fun fromIdVer(id: Int) = when (id) { + 0 -> abs_top + 1 -> abs_centre + 2 -> abs_bottom + 3 -> proportion_top + 4 -> proportion_centre + 5 -> proportion_bottom + else -> throw IllegalArgumentException("Invalid Pos id: $id") + } + } +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Size.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Size.kt new file mode 100644 index 0000000..a63ad0c --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Size.kt @@ -0,0 +1,20 @@ +package stan.qodat.scene.runescape.widget.component + +sealed class Size(val id: Int) { + object abs : Size(0) + object minus : Size(1) + object proportion : Size(2) + object unknown : Size(3) + object aspect : Size(4) + + companion object { + fun fromId(id: Int) = when (id) { + 0 -> abs + 1 -> minus + 2 -> proportion + 3 -> unknown + 4 -> aspect + else -> throw IllegalArgumentException("Invalid Size id: $id") + } + } +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Sprites.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Sprites.kt new file mode 100644 index 0000000..d45a5de --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/Sprites.kt @@ -0,0 +1,5076 @@ +package stan.qodat.scene.runescape.widget.component + +sealed class Sprites(open val groupId: Int, open val index: Int = 0) { + + data object RedCircleCrossedDiagonally : Sprites(940) + data object OpenHandWithCoinPile : Sprites(1112) + data object SearchIcon : Sprites(1113) + + sealed class RadioButton(spriteId: Int) : Sprites(spriteId) { + data object DarkEmpty : RadioButton(697) + data object DarkRedX : RadioButton( 698) + data object DarkGreenCheck : RadioButton(699) + data object DarkRedCheck : RadioButton( 1192) + data object LightEmpty : RadioButton(1211) + data object LightRedX : RadioButton(1212) + data object LightGreenCheck : RadioButton(1213) + data object LightRedCheck : RadioButton(1214) + } + data class ById(override val groupId: Int) : Sprites(groupId) +} + +/* +1 2x_standard_spells_on,1 +2 2x_standard_spells_on,2 +3 2x_standard_spells_on,3 +4 backleft1 +5 2x_standard_spells_on,4 +6 2x_standard_spells_on,5 +7 2x_standard_spells_on,6 +8 2x_standard_spells_on,7 +9 2x_standard_spells_on,8 +10 2x_standard_spells_on,9 +11 2x_standard_spells_on,10 +12 mapback +13 2x_standard_spells_on,11 +14 2x_standard_spells_on,12 +15 magicon,0 +16 magicon,1 +17 magicon,2 +18 magicon,3 +19 magicon,4 +20 magicon,5 +21 magicon,6 +22 magicon,7 +23 magicon,8 +24 magicon,9 +25 magicon,10 +26 magicon,11 +27 magicon,12 +28 magicon,13 +29 magicon,14 +30 magicon,15 +31 magicon,16 +32 magicon,17 +33 magicon,18 +34 magicon,19 +35 magicon,20 +36 magicon,21 +37 magicon,22 +38 magicon,23 +39 magicon,24 +40 magicon,25 +41 magicon,26 +42 magicon,27 +43 magicon,28 +44 magicon,29 +45 magicon,30 +46 magicon,31 +47 magicon,32 +48 magicon,33 +49 magicon,34 +50 magicon,35 +51 magicon,36 +52 magicon,37 +53 magicon,38 +54 magicon,39 +55 magicon,40 +56 magicon,41 +57 magicon,42 +58 magicon,43 +59 magicon,44 +60 magicon,45 +61 magicon,46 +62 magicon,47 +63 magicon,48 +64 magicon,49 +65 magicoff,0 +66 magicoff,1 +67 magicoff,2 +68 magicoff,3 +69 magicoff,4 +70 magicoff,5 +71 magicoff,6 +72 magicoff,7 +73 magicoff,8 +74 magicoff,9 +75 magicoff,10 +76 magicoff,11 +77 magicoff,12 +78 magicoff,13 +79 magicoff,14 +80 magicoff,15 +81 magicoff,16 +82 magicoff,17 +83 magicoff,18 +84 magicoff,19 +85 magicoff,20 +86 magicoff,21 +87 magicoff,22 +88 magicoff,23 +89 magicoff,24 +90 magicoff,25 +91 magicoff,26 +92 magicoff,27 +93 magicoff,28 +94 magicoff,29 +95 magicoff,30 +96 magicoff,31 +97 magicoff,32 +98 magicoff,33 +99 magicoff,34 +100 magicoff,35 +101 magicoff,36 +102 magicoff,37 +103 magicoff,38 +104 magicoff,39 +105 magicoff,40 +106 magicoff,41 +107 magicoff,42 +108 magicoff,43 +109 magicoff,44 +110 magicoff,45 +111 magicoff,46 +112 magicoff,47 +113 magicoff,48 +114 magicoff,49 +115 prayeron,0 +116 prayeron,1 +117 prayeron,2 +118 prayeron,3 +119 prayeron,4 +120 prayeron,5 +121 prayeron,6 +122 prayeron,7 +123 prayeron,8 +124 prayeron,9 +125 prayeron,10 +126 prayeron,11 +127 prayeron,12 +128 prayeron,13 +129 prayeron,14 +130 prayeron,15 +131 prayeron,16 +132 prayeron,17 +133 prayeron,18 +134 prayeron,19 +135 prayeroff,0 +136 prayeroff,1 +137 prayeroff,2 +138 prayeroff,3 +139 prayeroff,4 +140 prayeroff,5 +141 prayeroff,6 +142 prayeroff,7 +143 prayeroff,8 +144 prayeroff,9 +145 prayeroff,10 +146 prayeroff,11 +147 prayeroff,12 +148 prayeroff,13 +149 prayeroff,14 +150 prayeroff,15 +151 prayeroff,16 +152 prayeroff,17 +153 prayeroff,18 +154 prayeroff,19 +155 graphic_155 +156 wornicons,0 +157 wornicons,1 +158 wornicons,2 +159 wornicons,3 +160 wornicons,4 +161 wornicons,5 +162 wornicons,6 +163 wornicons,7 +164 wornicons,8 +165 wornicons,9 +166 wornicons,10 +167 wornicons,11 +168 side_icons,0 +169 compass +170 miscgraphics,0 +171 miscgraphics,1 +172 miscgraphics,2 +173 miscgraphics,3 +174 miscgraphics,4 +175 miscgraphics,5 +176 miscgraphics,6 +177 miscgraphics,7 +178 miscgraphics,8 +179 miscgraphics,9 +180 miscgraphics,10 +181 miscgraphics,11 +182 miscgraphics,12 +183 miscgraphics,13 +184 miscgraphics,14 +185 miscgraphics2,0 +186 miscgraphics2,1 +187 miscgraphics2,2 +188 miscgraphics2,3 +189 miscgraphics2,4 +190 miscgraphics2,5 +191 miscgraphics2,6 +192 miscgraphics2,7 +193 miscgraphics2,8 +194 miscgraphics2,9 +195 miscgraphics3,0 +196 miscgraphics3,1 +197 staticons,0 +198 staticons,1 +199 staticons,2 +200 staticons,3 +201 staticons,4 +202 staticons,5 +203 staticons,6 +204 staticons,7 +205 staticons,8 +206 staticons,9 +207 staticons,10 +208 staticons,11 +209 staticons,12 +210 staticons,13 +211 staticons,14 +212 staticons,15 +213 staticons,16 +214 staticons,17 +215 staticons2,0 +216 staticons2,1 +217 staticons2,2 +218 staticons2,3 +219 staticons2,4 +220 staticons2,5 +221 staticons2,6 +222 staticons2,7 +223 staticons2,8 +224 staticons2,9 +225 staticons2,10 +226 staticons2,11 +227 staticons2,12 +228 staticons2,13 +229 staticons2,14 +230 staticons2,15 +231 staticons2,16 +232 staticons2,17 +233 combaticons,0 +234 combaticons,1 +235 combaticons,2 +236 combaticons,3 +237 combaticons,4 +238 combaticons,5 +239 combaticons,6 +240 combaticons,7 +241 combaticons,8 +242 combaticons,9 +243 combaticons,10 +244 combaticons,11 +245 combaticons,12 +246 combaticons,13 +247 combaticons,14 +248 combaticons,15 +249 combaticons,16 +250 combaticons,17 +251 combaticons,18 +252 combaticons,19 +253 combaticons2,0 +254 combaticons2,1 +255 combaticons2,2 +256 combaticons2,3 +257 combaticons2,4 +258 combaticons2,5 +259 combaticons2,6 +260 combaticons2,7 +261 combaticons2,8 +262 combaticons2,9 +263 combaticons2,10 +264 combaticons2,11 +265 combaticons2,12 +266 combaticons2,13 +267 combaticons2,14 +268 combaticons2,15 +269 combaticons2,16 +270 combaticons2,17 +271 combaticons2,18 +272 combaticons2,19 +273 combaticons3,0 +274 combaticons3,1 +275 combaticons3,2 +276 combaticons3,3 +277 combaticons3,4 +278 combaticons3,5 +279 combaticons3,6 +280 combaticons3,7 +281 combaticons3,8 +282 combaticons3,9 +283 combaticons3,10 +284 combaticons3,11 +285 combaticons3,12 +286 combaticons3,13 +287 combaticons3,14 +288 combaticons3,15 +289 combaticons3,16 +290 combaticons3,17 +291 combaticons3,18 +292 combaticons3,19 +293 combatboxes,0 +294 combatboxes,1 +295 combatboxes,2 +296 combatboxes,3 +297 tradebacking +298 headicons_pk_interface,14 +299 cross +300 mapdots +301 sworddecor,0 +302 sworddecor,1 +303 sworddecor,2 +304 sworddecor,3 +305 2x_standard_spells_on,13 +306 2x_standard_spells_on,14 +307 2x_standard_spells_on,15 +308 leftarrow +309 rightarrow +310 steelborder,0 +311 steelborder,1 +312 steelborder,2 +313 steelborder,3 +314 steelborder2,0 +315 steelborder2,1 +316 scrollbar +317 mapscene +318 infernal_cape +319 magicon2,0 +320 magicon2,1 +321 magicon2,2 +322 magicon2,3 +323 magicon2,4 +324 magicon2,5 +325 magicon2,6 +326 magicon2,7 +327 magicon2,8 +328 magicon2,9 +329 magicon2,10 +330 magicon2,11 +331 magicon2,12 +332 magicon2,13 +333 magicon2,14 +334 magicon2,15 +335 magicon2,16 +336 magicon2,17 +337 magicon2,18 +338 magicon2,19 +339 magicon2,20 +340 magicon2,21 +341 magicon2,22 +342 magicon2,23 +343 magicon2,24 +344 magicon2,25 +345 magicon2,26 +346 magicon2,27 +347 magicon2,28 +348 magicon2,29 +349 magicon2,30 +350 magicon2,31 +351 magicon2,32 +352 magicon2,33 +353 magicon2,34 +354 magicon2,35 +355 magicon2,36 +356 magicon2,37 +357 magicon2,38 +358 magicon2,39 +359 magicon2,40 +360 magicon2,41 +361 magicon2,42 +362 magicon2,43 +363 magicon2,44 +364 magicon2,45 +365 magicon2,46 +366 magicon2,47 +367 magicon2,48 +368 magicon2,49 +369 magicoff2,0 +370 magicoff2,1 +371 magicoff2,2 +372 magicoff2,3 +373 magicoff2,4 +374 magicoff2,5 +375 magicoff2,6 +376 magicoff2,7 +377 magicoff2,8 +378 magicoff2,9 +379 magicoff2,10 +380 magicoff2,11 +381 magicoff2,12 +382 magicoff2,13 +383 magicoff2,14 +384 magicoff2,15 +385 magicoff2,16 +386 magicoff2,17 +387 magicoff2,18 +388 magicoff2,19 +389 magicoff2,20 +390 magicoff2,21 +391 magicoff2,22 +392 magicoff2,23 +393 magicoff2,24 +394 magicoff2,25 +395 magicoff2,26 +396 magicoff2,27 +397 magicoff2,28 +398 magicoff2,29 +399 magicoff2,30 +400 magicoff2,31 +401 magicoff2,32 +402 magicoff2,33 +403 magicoff2,34 +404 magicoff2,35 +405 magicoff2,36 +406 magicoff2,37 +407 magicoff2,38 +408 magicoff2,39 +409 magicoff2,40 +410 magicoff2,41 +411 magicoff2,42 +412 magicoff2,43 +413 magicoff2,44 +414 magicoff2,45 +415 magicoff2,46 +416 magicoff2,47 +417 magicoff2,48 +418 magicoff2,49 +419 gnomeball_buttons,0 +420 gnomeball_buttons,1 +421 gnomeball_buttons,2 +422 mapmarker +423 mod_icons +424 mapedge +425 leftarrow_small +426 rightarrow_small +427 blackmark +428 button_brown +429 button_red +430 chest +431 coins +432 key +433 keys +434 pen +435 startgame +436 titlescroll +437 letter +438 button_brown_big +439 headicons_pk +440 headicons_prayer +441 headicons_hint +442 overlay_multiway +443 overlay_duel +444 tex_brown +445 tex_red +446 number_button +447 door +448 water +449 wall +450 planks +451 elfdoor +452 darkwood +453 roof +454 damage +455 leafytree +456 treestump +457 mossy +458 railings +459 painting1 +460 painting2 +461 marble +462 wood2 +463 fountain +464 thatched +465 cargonet +466 books +467 elfroof2 +468 elfwood +469 mossybricks +470 gungywater +471 web +472 elfroof +473 mossydamage +474 bamboo +475 willowtex3 +476 lava +477 bark +478 mapletree +479 stars +480 elfbrick +481 elfwall +482 chainmail +483 mummy +484 elfpainting +485 lava_animated +486 leafytree_diseased +487 stone_tiles +488 roof2 +489 red_green_arrows,0 +490 pebblefloor +491 wall_white +492 glyphs +493 canvas +494 p11_full +495 p12_full +496 b12_full +497 q8_full +498 logo +499 titlebox +500 titlebutton +501 runes +502 prayeron,20 +503 prayeron,21 +504 prayeron,22 +505 prayeron,23 +506 prayeroff,20 +507 prayeroff,21 +508 prayeroff,22 +509 prayeroff,23 +510 mapdots_interface,0 +511 mapdots_interface,1 +512 mapdots_interface,2 +513 mapdots_interface,3 +514 mapdots_interface,4 +515 cross_interface,0 +516 cross_interface,1 +517 cross_interface,2 +518 cross_interface,3 +519 cross_interface,4 +520 cross_interface,5 +521 cross_interface,6 +522 cross_interface,7 +523 headicons_pk_interface,0 +524 headicons_pk_interface,1 +525 headicons_pk_interface,2 +526 headicons_pk_interface,3 +527 headicons_pk_interface,4 +528 headicons_pk_interface,5 +529 hloading2 +530 crag +531 water_animated +532 marble_poh +533 old_tiles +534 red_green_arrows,1 +535 close_buttons,0 +536 close_buttons,1 +537 close_buttons,2 +538 close_buttons,3 +539 close_buttons,4 +540 close_buttons,5 +541 close_buttons,6 +542 close_buttons,7 +543 lunar_magic_on,0 +544 lunar_magic_on,1 +545 lunar_magic_on,2 +546 lunar_magic_on,3 +547 lunar_magic_on,4 +548 lunar_magic_on,5 +549 lunar_magic_on,6 +550 lunar_magic_on,7 +551 lunar_magic_on,8 +552 lunar_magic_on,9 +553 lunar_magic_on,10 +554 lunar_magic_on,11 +555 lunar_magic_on,12 +556 lunar_magic_on,13 +557 lunar_magic_on,14 +558 lunar_magic_on,15 +559 lunar_magic_on,16 +560 lunar_magic_on,17 +561 lunar_magic_on,18 +562 lunar_magic_on,19 +563 lunar_magic_on,20 +564 lunar_magic_on,21 +565 lunar_magic_on,22 +566 lunar_magic_on,23 +567 lunar_magic_on,24 +568 lunar_magic_on,25 +569 lunar_magic_on,26 +570 lunar_magic_on,27 +571 lunar_magic_on,28 +572 lunar_magic_on,29 +573 lunar_magic_on,30 +574 lunar_magic_on,31 +575 lunar_magic_on,32 +576 lunar_magic_on,33 +577 lunar_magic_on,34 +578 lunar_magic_on,35 +579 lunar_magic_on,36 +580 lunar_magic_on,37 +581 lunar_magic_on,38 +582 lunar_magic_on,39 +583 lunar_magic_on,40 +584 lunar_magic_on,41 +585 lunar_magic_on,42 +586 lunar_magic_on,43 +587 lunar_magic_on,44 +588 lunar_magic_on,45 +589 lunar_magic_on,46 +590 lunar_magic_on,47 +591 lunar_magic_on,48 +592 lunar_magic_on,49 +593 lunar_magic_off,0 +594 lunar_magic_off,1 +595 lunar_magic_off,2 +596 lunar_magic_off,3 +597 lunar_magic_off,4 +598 lunar_magic_off,5 +599 lunar_magic_off,6 +600 lunar_magic_off,7 +601 lunar_magic_off,8 +602 lunar_magic_off,9 +603 lunar_magic_off,10 +604 lunar_magic_off,11 +605 lunar_magic_off,12 +606 lunar_magic_off,13 +607 lunar_magic_off,14 +608 lunar_magic_off,15 +609 lunar_magic_off,16 +610 lunar_magic_off,17 +611 lunar_magic_off,18 +612 lunar_magic_off,19 +613 lunar_magic_off,20 +614 lunar_magic_off,21 +615 lunar_magic_off,22 +616 lunar_magic_off,23 +617 lunar_magic_off,24 +618 lunar_magic_off,25 +619 lunar_magic_off,26 +620 lunar_magic_off,27 +621 lunar_magic_off,28 +622 lunar_magic_off,29 +623 lunar_magic_off,30 +624 lunar_magic_off,31 +625 lunar_magic_off,32 +626 lunar_magic_off,33 +627 lunar_magic_off,34 +628 lunar_magic_off,35 +629 lunar_magic_off,36 +630 lunar_magic_off,37 +631 lunar_magic_off,38 +632 lunar_magic_off,39 +633 lunar_magic_off,40 +634 lunar_magic_off,41 +635 lunar_magic_off,42 +636 lunar_magic_off,43 +637 lunar_magic_off,44 +638 lunar_magic_off,45 +639 lunar_magic_off,46 +640 lunar_magic_off,47 +641 lunar_magic_off,48 +642 lunar_magic_off,49 +643 old_tiles_lighter +644 woodenfloor_new +645 quill_oblique_large +646 quill_caps_large +647 lunar_alphabet +648 lunar_alphabet_lrg +649 options_icons_small,0 +650 options_icons_small,1 +651 options_icons_small,2 +652 graphic_652 +653 combatboxes_large,0 +654 combatboxes_large,1 +655 combatboxes_very_large,0 +656 combatboxes_very_large,1 +657 combatboxes_special_attack +658 equip_screen_button +659 options_icons,0 +660 options_icons,1 +661 options_icons,2 +662 options_icons,3 +663 options_icons,4 +664 options_icons,5 +665 options_icons,6 +666 options_icons,7 +667 options_icons,8 +668 options_icons,9 +669 options_icons,10 +670 options_icons,11 +671 options_icons,12 +672 options_icons,13 +673 options_icons,14 +674 options_icons,15 +675 options_icons,16 +676 options_icons,17 +677 options_icons_small,3 +678 options_poh_icon +679 options_slider,0 +680 options_slider,1 +681 options_slider,2 +682 options_slider,3 +683 options_slider,4 +684 options_slider,5 +685 options_slider,6 +686 options_slider,7 +687 options_slider_five,0 +688 options_slider_five,1 +689 options_slider_five,2 +690 options_slider_five,3 +691 options_slider_five,4 +692 options_slider_five,5 +693 options_slider_five,6 +694 options_slider_five,7 +695 options_slider_five,8 +696 options_slider_five,9 +697 options_radio_buttons,0 +698 options_radio_buttons,1 +699 options_radio_buttons,2 +700 emotes,0 +701 emotes,1 +702 emotes,2 +703 emotes,3 +704 emotes,4 +705 emotes,5 +706 emotes,6 +707 emotes,7 +708 emotes,8 +709 emotes,9 +710 emotes,10 +711 emotes,11 +712 emotes,12 +713 emotes,13 +714 emotes,14 +715 emotes,15 +716 emotes,16 +717 emotes,17 +718 emotes,18 +719 emotes,19 +720 emotes,20 +721 emotes,21 +722 emotes,22 +723 emotes,23 +724 emotes,24 +725 emotes,25 +726 emotes,26 +727 emotes,27 +728 emotes,28 +729 emotes,29 +730 emotes,30 +731 emotes,31 +732 emotes,32 +733 emotes,33 +734 emotes,34 +735 emotes,35 +736 emotes,36 +737 emotes,37 +738 emotes,38 +739 emotes,39 +740 emotes_locked,0 +741 emotes_locked,1 +742 emotes_locked,2 +743 emotes_locked,3 +744 emotes_locked,4 +745 emotes_locked,5 +746 emotes_locked,6 +747 emotes_locked,7 +748 emotes_locked,8 +749 emotes_locked,9 +750 emotes_locked,10 +751 emotes_locked,11 +752 emotes_locked,12 +753 emotes_locked,13 +754 emotes_locked,14 +755 emotes_locked,15 +756 emotes_locked,16 +757 emotes_locked,17 +758 emotes_locked,18 +759 emotes_locked,19 +760 combat_shield +761 options_boxes,0 +762 options_boxes,1 +763 xmas_snowflake +764 barbassault_font +765 barbassault_icons,0 +766 barbassault_icons,1 +767 barbassault_icons,2 +768 barbassault_icons,3 +769 barbassault_icons,4 +770 barbassault_icons,5 +771 barbassault_icons,6 +772 barbassault_icons,7 +773 scrollbar_v2,0 +774 sideicons_interface,0 +775 sideicons_interface,1 +776 sideicons_interface,2 +777 sideicons_interface,3 +778 sideicons_interface,4 +779 sideicons_interface,5 +780 sideicons_interface,6 +781 sideicons_interface,7 +782 sideicons_interface,8 +783 sideicons_interface,9 +784 sideicons_interface,10 +785 sideicons_interface,11 +786 sideicons_interface,12 +787 sideicons_interface,13 +788 scrollbar_v2,1 +789 scrollbar_dragger_v2,0 +790 scrollbar_dragger_v2,1 +791 scrollbar_dragger_v2,2 +792 scrollbar_dragger_v2,3 +793 scrollbar_parchment_v2,0 +794 scrollbar_parchment_v2,1 +795 scrollbar_parchment_dragger_v2,0 +796 scrollbar_parchment_dragger_v2,1 +797 scrollbar_parchment_dragger_v2,2 +798 scrollbar_parchment_dragger_v2,3 +799 close_buttons_v2,0 +800 close_buttons_v2,1 +801 scrollbar_sep,0 +802 scrollbar_sep,1 +803 peng_emotes,0 +804 peng_emotes,1 +805 peng_emotes,2 +806 peng_emotes,3 +807 peng_emotes,4 +808 peng_emotes,5 +809 peng_emotes,6 +810 peng_emotes,7 +811 title_mute +812 combatboxesmed,0 +813 combatboxesmed,1 +814 sl_back +815 sl_flags +816 sl_arrows +817 sl_stars +818 sl_button +819 surok_font +820 v2_stone_borders,0 +821 v2_stone_borders,1 +822 v2_stone_borders,2 +823 v2_stone_borders,3 +824 v2_stone_borders,4 +825 v2_stone_borders,5 +826 v2_stone_borders,6 +827 v2_stone_borders,7 +828 v2_stone_borders,8 +829 v2_stone_borders,9 +830 v2_stone_borders,10 +831 v2_stone_close_button,0 +832 v2_stone_close_button,1 +833 graphic_833 +834 graphic_834 +835 achievement_diary_icons,0 +836 achievement_diary_icons,1 +837 graphic_837 +838 dream_monster_icon +839 v2_stone_borders,11 +840 v2_stone_borders,12 +841 v2_stone_borders,13 +842 v2_stone_borders,14 +843 v2_stone_borders,15 +844 v2_stone_borders,16 +845 v2_stone_borders,17 +846 v2_stone_borders,18 +847 v2_stone_borders,19 +848 v2_stone_borders,20 +849 v2_stone_borders,21 +850 v2_stone_borders,22 +851 v2_stone_borders,23 +852 v2_stone_borders,24 +853 v2_stone_borders,25 +854 v2_stone_borders,26 +855 v2_stone_borders,27 +856 v2_stone_borders,28 +857 v2_stone_borders,29 +858 v2_stone_borders,30 +859 v2_stone_borders,31 +860 v2_stone_borders,32 +861 mirror +862 graphic_862 +863 graphic_863 +864 graphic_864 +865 graphic_865 +866 old_tiles_green +867 ii_impling_icons,0 +868 ii_impling_icons,1 +869 ii_impling_icons,2 +870 ii_impling_icons,3 +871 ii_impling_icons,4 +872 ii_impling_icons,5 +873 ii_impling_icons,6 +874 ii_impling_icons,7 +875 ii_impling_icons,8 +876 ii_impling_icons,9 +877 ii_impling_icons,10 +878 ii_impling_icons,11 +879 ame_confirm_button,0 +880 ame_confirm_button,1 +881 sideicons_new,0 +882 sideicons_new,1 +883 sideicons_new,2 +884 sideicons_new,3 +885 sideicons_new,4 +886 sideicons_new,5 +887 sideicons_new,6 +888 sideicons_new,7 +889 sideicons_new,8 +890 sideicons_new,9 +891 sideicons_new,10 +892 sideicons_new,11 +893 sideicons_new,12 +894 sideicons_new,13 +895 sideicons_new,14 +896 sideicons_new,15 +897 tradebacking_dark +898 side_icons,1 +899 side_icons,2 +900 side_icons,3 +901 side_icons,4 +902 side_icons,5 +903 side_icons,6 +904 side_icons,7 +905 side_icons,8 +906 side_icons,9 +907 side_icons,10 +908 side_icons,11 +909 side_icons,12 +910 side_icons,13 +911 options_icons_small,6 +912 options_icons,18 +913 v2_stone_button,0 +914 v2_stone_button,1 +915 v2_stone_button,2 +916 v2_stone_button,3 +917 v2_stone_button,4 +918 v2_stone_button,5 +919 v2_stone_button,6 +920 v2_stone_button,7 +921 v2_stone_button_in,0 +922 v2_stone_button_in,1 +923 v2_stone_button_in,2 +924 v2_stone_button_in,3 +925 v2_stone_button_in,4 +926 v2_stone_button_in,5 +927 v2_stone_button_in,6 +928 v2_stone_button_in,7 +929 v2_stone_button_out,0 +930 v2_stone_button_out,1 +931 v2_stone_button_out,2 +932 v2_stone_button_out,3 +933 v2_stone_button_out,4 +934 v2_stone_button_out,5 +935 v2_stone_button_out,6 +936 v2_stone_button_out,7 +937 exclamation_mark +938 graphic_938 +939 graphic_939 +940 warning_icons,0 +941 warning_icons,1 +942 warning_icons,2 +943 graphic_943 +944 prayeron,24 +945 prayeron,25 +946 prayeron,26 +947 prayeron,27 +948 prayeroff,24 +949 prayeroff,25 +950 prayeroff,26 +951 prayeroff,27 +952 tabs,0 +953 tabs,1 +954 v2_stone_borders_alt,0 +955 v2_stone_borders_alt,1 +956 v2_stone_borders_alt,2 +957 v2_stone_borders_alt,3 +958 v2_stone_borders_alt,4 +959 v2_stone_borders_alt,5 +960 v2_stone_borders_alt,6 +961 v2_stone_borders_alt,7 +962 v2_stone_borders_alt,8 +963 v2_stone_borders_alt,9 +964 v2_stone_borders_alt,10 +965 v2_stone_borders_alt,11 +966 v2_stone_borders_alt,12 +967 v2_stone_borders_alt,13 +968 v2_stone_borders_alt,14 +969 v2_stone_borders_alt,15 +970 v2_stone_borders_alt,16 +971 v2_stone_borders_alt,17 +972 v2_stone_borders_alt,18 +973 v2_stone_borders_alt,19 +974 v2_stone_borders_alt,20 +975 v2_stone_borders_alt,21 +976 v2_stone_borders_alt,22 +977 v2_stone_borders_alt,23 +978 v2_stone_borders_alt,24 +979 v2_stone_borders_alt,25 +980 v2_stone_borders_alt,26 +981 v2_stone_borders_alt,27 +982 v2_stone_borders_alt,28 +983 v2_stone_borders_alt,29 +984 v2_stone_borders_alt,30 +985 v2_stone_borders_alt,31 +986 v2_stone_borders_alt,32 +987 v2_borders_slim,0 +988 v2_borders_slim,1 +989 v2_borders_slim,2 +990 v2_borders_slim,3 +991 v2_borders_slim,4 +992 v2_borders_slim,5 +993 v2_borders_slim,6 +994 v2_borders_slim,7 +995 v2_borders_slim,8 +996 v2_borders_slim,9 +997 v2_borders_slim,10 +998 tabs_split,0 +999 tabs_split,1 +1000 tabs_split,2 +1001 tabs_split,3 +1002 tabs_split,4 +1003 tabs_split,5 +1004 settings_tabs,0 +1005 settings_tabs,1 +1006 settings_tabs,2 +1007 settings_tabs,3 +1008 settings_tabs,4 +1009 settings_tabs,5 +1010 settings_tabs,6 +1011 settings_tabs,7 +1012 settings_tabs,8 +1013 settings_tabs,9 +1014 settings_tabs,10 +1015 tabs_large,0 +1016 tabs_large,1 +1017 chat_background +1018 main_stones_bottom +1019 volume_options_bobble_slider +1020 pvpw_icons,4 +1021 menu_buttons,4 +1022 menu_buttons,5 +1023 magic_necro_on,50 +1024 magic_necro_on,51 +1025 magic_necro_on,52 +1026 side_stone_highlights,0 +1027 side_stone_highlights,1 +1028 side_stone_highlights,2 +1029 side_stone_highlights,3 +1030 side_stone_highlights,4 +1031 side_background +1032 side_background_bottom +1033 side_background_left1 +1034 side_background_left2 +1035 side_background_right +1036 side_background_top +1037 mini_left +1038 mini_right +1039 main_top +1040 tradebacking_light +1041 bankbuttons,0 +1042 bankbuttons,1 +1043 bankbuttons,2 +1044 mapdots_interface,5 +1045 options_icons,19 +1046 pvpw_icons,0 +1047 pvpw_icons,1 +1048 graphic_1048 +1049 graphic_1049 +1050 sortarrows,0 +1051 sortarrows,1 +1052 sideicons_interface,14 +1053 sideicons_interface,15 +1054 achievement_diary_icons,2 +1055 side_icons,14 +1056 side_icons,15 +1057 options_icons,20 +1058 orb_icon,4 +1059 orb_filler,0 +1060 orb_filler,1 +1061 orb_filler,2 +1062 orb_filler,3 +1063 orb_filler,4 +1064 orb_filler,5 +1065 orb_filler,6 +1066 orb_filler,7 +1067 orb_icon,0 +1068 orb_icon,1 +1069 orb_icon,2 +1070 orb_icon,3 +1071 orb_frame,0 +1072 orb_frame,1 +1073 options_icons,21 +1074 bankbuttons,3 +1075 lava_animated_red +1076 lava_animated_white +1077 banktabs,0 +1078 banktabs,1 +1079 banktabs,2 +1080 banktabs,3 +1081 banktab_icons,0 +1082 banktab_icons,1 +1083 banktab_icons,2 +1084 options_icons,22 +1085 options_icons,23 +1086 options_icons,24 +1087 options_icons,25 +1088 options_icons,26 +1089 options_icons,27 +1090 options_icons,28 +1091 options_icons,29 +1092 orb_icon,5 +1093 friends_changed_name +1094 mapfunction_infoicon +1095 headicons_pk_interface,6 +1096 headicons_pk_interface,7 +1097 headicons_pk_interface,8 +1098 2x_standard_spells_on,16 +1099 2x_standard_spells_on,17 +1100 2x_standard_spells_on,18 +1101 magic_necro_on,53 +1102 orb_filler,8 +1103 speedrun_trophies,4 +1104 graphic_1104 +1105 hitmark,14 +1106 ge_icons,0 +1107 ge_icons,1 +1108 ge_icons,2 +1109 ge_icons,3 +1110 ge_icons,4 +1111 ge_icons,5 +1112 ge_smallicons,0 +1113 ge_smallicons,1 +1114 ge_smallicons,2 +1115 ge_smallicons,3 +1116 ge_smallicons,4 +1117 ge_smallicons,5 +1118 ge_smallicons,6 +1119 ge_smallicons,7 +1120 graphic_1120 +1121 graphic_1121 +1122 ge_backbutton +1123 graphic_1123 +1124 graphic_1124 +1125 graphic_1125 +1126 ge_cancel,0 +1127 ge_cancel,1 +1128 membership_icon_large +1129 membership_icon_medium +1130 worldswitcher_stars,0 +1131 worldswitcher_stars,1 +1132 worldswitcher_flags,0 +1133 worldswitcher_flags,1 +1134 worldswitcher_flags,2 +1135 worldswitcher_flags,3 +1136 worldswitcher_flags,4 +1137 worldswitcher_flags,5 +1138 worldswitcher_flags,6 +1139 worldswitcher_flags,7 +1140 worldswitcher_flags,8 +1141 graphic_1141 +1142 graphic_1142 +1143 graphic_1143 +1144 graphic_1144 +1145 graphic_1145 +1146 graphic_1146 +1147 graphic_1147 +1148 graphic_1148 +1149 graphic_1149 +1150 graphic_1150 +1151 graphic_1151 +1152 graphic_1152 +1153 graphic_1153 +1154 graphic_1154 +1155 graphic_1155 +1156 graphic_1156 +1157 graphic_1157 +1158 graphic_1158 +1159 options_icons,30 +1160 options_icons,31 +1161 options_icons,32 +1162 options_icons,33 +1163 options_icons,34 +1164 options_icons,35 +1165 options_icons,36 +1166 options_icons,37 +1167 options_icons,38 +1168 options_icons,39 +1169 windowmode_icons,0 +1170 windowmode_icons,1 +1171 windowmode_icons,2 +1172 windowmode_icons,3 +1173 graphic_1173 +1174 graphic_1174 +1175 graphic_1175 +1176 graphic_1176 +1177 graphic_1177 +1178 resize_map_mask +1179 resize_compass_mask +1180 pre_eoc_stones,0 +1181 pre_eoc_stones,1 +1182 fixed_minimap_cover +1183 fixed_map_mask +1184 fixed_compass_mask +1185 scrollbar_trans_arrows,0 +1186 scrollbar_trans_arrows,1 +1187 scrollbar_trans_dragger,0 +1188 scrollbar_trans_dragger,1 +1189 scrollbar_trans_dragger,2 +1190 scrollbar_trans_dragger,3 +1191 sideicons_logout +1192 options_radio_buttons,3 +1193 stop +1194 save +1195 load +1196 orb_xp,0 +1197 orb_xp,1 +1198 orb_xp,2 +1199 orb_xp,3 +1200 fixed_map_clickmask +1201 slider_bobble +1202 emotes,40 +1203 emotes,41 +1204 emotes,42 +1205 emotes,43 +1206 emotes_locked,20 +1207 emotes_locked,21 +1208 emotes_locked,22 +1209 emotes_locked,23 +1210 tick +1211 options_radio_buttons,4 +1212 options_radio_buttons,5 +1213 options_radio_buttons,6 +1214 options_radio_buttons,7 +1215 checkbox,0 +1216 checkbox,1 +1217 checkbox,2 +1218 checkbox,3 +1219 checkbox,4 +1220 checkbox,5 +1221 headicons_pk_interface,9 +1222 headicons_pk_interface,10 +1223 headicons_pk_interface,11 +1224 headicons_pk_interface,12 +1225 headicons_pk_interface,13 +1226 bankbuttons,4 +1227 bankbuttons,5 +1228 logo_deadman_mode +1229 combatboxes_split,0 +1230 combatboxes_split,1 +1231 combatboxes_split,2 +1232 combatboxes_split,3 +1233 combatboxes_split,4 +1234 combatboxes_split,5 +1235 bankbuttons,6 +1236 info +1237 worldswitcher_stars,2 +1238 worldswitcher_stars,3 +1239 pvpw_icons,2 +1240 zeah_book,0 +1241 zeah_book,1 +1242 arceuus_sigil +1243 hosidius_sigil +1244 lovakengj_sigil +1245 piscarilius_sigil +1246 shayzien_sigil +1247 magic_necro_on,0 +1248 magic_necro_on,1 +1249 magic_necro_on,2 +1250 magic_necro_on,3 +1251 magic_necro_on,4 +1252 magic_necro_on,5 +1253 magic_necro_on,6 +1254 magic_necro_on,7 +1255 magic_necro_on,8 +1256 magic_necro_on,9 +1257 magic_necro_on,10 +1258 magic_necro_on,11 +1259 magic_necro_on,12 +1260 magic_necro_on,13 +1261 magic_necro_on,14 +1262 magic_necro_on,15 +1263 magic_necro_on,16 +1264 magic_necro_on,17 +1265 magic_necro_on,18 +1266 magic_necro_on,19 +1267 magic_necro_on,20 +1268 magic_necro_on,21 +1269 magic_necro_on,22 +1270 magic_necro_on,23 +1271 magic_necro_on,24 +1272 magic_necro_off,0 +1273 magic_necro_off,1 +1274 magic_necro_off,2 +1275 magic_necro_off,3 +1276 magic_necro_off,4 +1277 magic_necro_off,5 +1278 magic_necro_off,6 +1279 magic_necro_off,7 +1280 magic_necro_off,8 +1281 magic_necro_off,9 +1282 magic_necro_off,10 +1283 magic_necro_off,11 +1284 magic_necro_off,12 +1285 magic_necro_off,13 +1286 magic_necro_off,14 +1287 magic_necro_off,15 +1288 magic_necro_off,16 +1289 magic_necro_off,17 +1290 magic_necro_off,18 +1291 magic_necro_off,19 +1292 magic_necro_off,20 +1293 magic_necro_off,21 +1294 magic_necro_off,22 +1295 magic_necro_off,23 +1296 magic_necro_off,24 +1297 achievement_diary_icons,3 +1298 side_icons,16 +1299 sideicons_interface,16 +1300 magic_necro_on,25 +1301 magic_necro_on,26 +1302 magic_necro_on,27 +1303 magic_necro_on,28 +1304 magic_necro_on,29 +1305 magic_necro_on,30 +1306 magic_necro_on,31 +1307 magic_necro_on,32 +1308 magic_necro_on,33 +1309 magic_necro_on,34 +1310 magic_necro_on,35 +1311 magic_necro_on,36 +1312 magic_necro_on,37 +1313 magic_necro_on,38 +1314 magic_necro_on,39 +1315 magic_necro_on,40 +1316 magic_necro_on,41 +1317 magic_necro_on,42 +1318 magic_necro_on,43 +1319 magic_necro_off,25 +1320 magic_necro_off,26 +1321 magic_necro_off,27 +1322 magic_necro_off,28 +1323 magic_necro_off,29 +1324 magic_necro_off,30 +1325 magic_necro_off,31 +1326 magic_necro_off,32 +1327 magic_necro_off,33 +1328 magic_necro_off,34 +1329 magic_necro_off,35 +1330 magic_necro_off,36 +1331 magic_necro_off,37 +1332 magic_necro_off,38 +1333 magic_necro_off,39 +1334 magic_necro_off,40 +1335 magic_necro_off,41 +1336 magic_necro_off,42 +1337 magic_necro_off,43 +1338 worldswitcher_stars,4 +1339 hitmark,15 +1340 favourite_icons,0 +1341 favourite_icons,1 +1342 bankbuttons,7 +1343 whistle +1344 banktabs_small,0 +1345 banktabs_small,1 +1346 deadman_deathtype,0 +1347 deadman_deathtype,1 +1348 deadman_deathtype,2 +1349 deadman_deathtype,3 +1350 emotes,44 +1351 emotes,45 +1352 emotes,46 +1353 emotes,47 +1354 emotes_locked,24 +1355 emotes_locked,25 +1356 emotes_locked,26 +1357 emotes_locked,27 +1358 hitmark,0 +1359 hitmark,1 +1360 hitmark,2 +1361 hitmark,3 +1362 hitmark,4 +1363 hitmark,5 +1364 fast_forward,0 +1365 fast_forward,1 +1366 miscgraphics3,2 +1367 miscgraphics3,3 +1368 poh_room_layouts,0 +1369 poh_room_layouts,1 +1370 poh_room_layouts,2 +1371 poh_room_layouts,3 +1372 poh_room_layouts,4 +1373 poh_room_layouts,5 +1374 poh_room_layouts,6 +1375 poh_room_layouts,7 +1376 poh_room_layouts,8 +1377 poh_room_layouts,9 +1378 poh_room_layouts,10 +1379 poh_room_layouts,11 +1380 poh_room_layouts,12 +1381 poh_room_layouts,13 +1382 poh_room_layouts,14 +1383 poh_room_layouts,15 +1384 poh_room_layouts,16 +1385 poh_room_layouts,17 +1386 poh_room_layouts,18 +1387 poh_room_layouts,19 +1388 poh_room_layouts,20 +1389 poh_room_layouts,21 +1390 poh_room_layouts,22 +1391 poh_room_layouts,23 +1392 poh_room_layouts,24 +1393 poh_room_layouts,25 +1394 poh_room_layouts,26 +1395 poh_room_layouts,27 +1396 cherryblossom +1397 wint_icons,0 +1398 wint_icons,1 +1399 wint_icons,2 +1400 wint_icons,3 +1401 tiny_combat_staticons,0 +1402 tiny_combat_staticons,1 +1403 tiny_combat_staticons,2 +1404 tiny_combat_staticons,3 +1405 tiny_combat_staticons,4 +1406 tiny_combat_staticons,5 +1407 tiny_combat_staticons,6 +1408 iron_icons,5 +1409 pvpa_rankicons,0 +1410 pvpa_rankicons,1 +1411 pvpa_rankicons,2 +1412 refresh +1413 side_icons,17 +1414 sideicons_interface,17 +1415 graphic_1415 +1416 graphic_1416 +1417 graphic_1417 +1418 graphic_1418 +1419 hitmark,6 +1420 prayeron,28 +1421 prayeron,29 +1422 prayeron,30 +1423 prayeron,31 +1424 prayeroff,28 +1425 prayeroff,29 +1426 prayeroff,30 +1427 prayeroff,31 +1428 reset,0 +1429 reset,1 +1430 clickcross,0 +1431 clickcross,1 +1432 clickcross,2 +1433 clickcross,3 +1434 clickcross,4 +1435 clickcross,5 +1436 clickcross,6 +1437 clickcross,7 +1438 ring_30 +1439 worldmap_icon,0 +1440 worldmap_icon,1 +1441 mini_topright +1442 verdana_11pt_regular +1443 verdana_11pt_bold +1444 graphic_1444 +1445 verdana_13pt_regular +1446 verdana_13pt_bold +1447 verdana_15pt_regular +1448 mapfunction,0 +1449 mapfunction,1 +1450 mapfunction,2 +1451 mapfunction,3 +1452 mapfunction,4 +1453 mapfunction,5 +1454 mapfunction,6 +1455 mapfunction,7 +1456 mapfunction,8 +1457 mapfunction,9 +1458 mapfunction,10 +1459 mapfunction,11 +1460 mapfunction,12 +1461 mapfunction,13 +1462 mapfunction,14 +1463 mapfunction,15 +1464 mapfunction,16 +1465 mapfunction,17 +1466 mapfunction,18 +1467 mapfunction,19 +1468 mapfunction,20 +1469 mapfunction,21 +1470 mapfunction,22 +1471 mapfunction,23 +1472 mapfunction,24 +1473 mapfunction,25 +1474 mapfunction,26 +1475 mapfunction,27 +1476 mapfunction,28 +1477 mapfunction,29 +1478 mapfunction,30 +1479 mapfunction,31 +1480 mapfunction,32 +1481 mapfunction,33 +1482 mapfunction,34 +1483 mapfunction,35 +1484 mapfunction,36 +1485 mapfunction,37 +1486 mapfunction,38 +1487 mapfunction,39 +1488 mapfunction,40 +1489 mapfunction,41 +1490 mapfunction,42 +1491 mapfunction,43 +1492 mapfunction,44 +1493 mapfunction,45 +1494 mapfunction,46 +1495 mapfunction,47 +1496 mapfunction,48 +1497 mapfunction,49 +1498 mapfunction,50 +1499 mapfunction,51 +1500 mapfunction,52 +1501 mapfunction,53 +1502 mapfunction,54 +1503 mapfunction,55 +1504 mapfunction,56 +1505 mapfunction,57 +1506 mapfunction,58 +1507 mapfunction,59 +1508 mapfunction,60 +1509 mapfunction,61 +1510 mapfunction,62 +1511 mapfunction,63 +1512 mapfunction,64 +1513 mapfunction,65 +1514 mapfunction,66 +1515 mapfunction,67 +1516 mapfunction,68 +1517 mapfunction,69 +1518 mapfunction,70 +1519 mapfunction,71 +1520 mapfunction,72 +1521 mapfunction,73 +1522 mapfunction,74 +1523 mapfunction,75 +1524 mapfunction,76 +1525 mapfunction,77 +1526 mapfunction,78 +1527 mapfunction,79 +1528 mapfunction,80 +1529 mapfunction,81 +1530 mapfunction,82 +1531 mapfunction,83 +1532 mapfunction,84 +1533 mapfunction,85 +1534 mapfunction,86 +1535 mapfunction,87 +1536 mapfunction,88 +1537 mapfunction,89 +1538 mapfunction,90 +1539 worldmap_marker,0 +1540 worldmap_marker,1 +1541 worldmap_marker,2 +1542 arrow,0 +1543 arrow,1 +1544 hitmark,16 +1545 tradebacking_beige +1546 v2_stone_button_in,8 +1547 v2_stone_button_in,9 +1548 v2_stone_button_in,10 +1549 v2_stone_button_in,11 +1550 v2_stone_button_in,12 +1551 v2_stone_button_in,13 +1552 v2_stone_button_in,14 +1553 v2_stone_button_in,15 +1554 v2_stone_button_out,8 +1555 v2_stone_button_out,9 +1556 v2_stone_button_out,10 +1557 v2_stone_button_out,11 +1558 v2_stone_button_out,12 +1559 v2_stone_button_out,13 +1560 v2_stone_button_out,14 +1561 v2_stone_button_out,15 +1562 options_icons,40 +1563 options_icons,41 +1564 options_icons,42 +1565 options_icons,43 +1566 options_icons,44 +1567 options_icons,45 +1568 options_icons,46 +1569 options_icons,47 +1570 options_icons,48 +1571 options_icons,49 +1572 windowmode_icons,4 +1573 windowmode_icons,5 +1574 windowmode_icons,6 +1575 windowmode_icons,7 +1576 poh_doors,0 +1577 poh_doors,1 +1578 poh_doors,2 +1579 side_icons,18 +1580 side_icons,19 +1581 side_icons,20 +1582 sideicons_interface,18 +1583 sideicons_interface,19 +1584 sideicons_interface,20 +1585 miscgraphics4,0 +1586 miscgraphics4,1 +1587 miscgraphics4,2 +1588 miscgraphics4,3 +1589 miscgraphics4,4 +1590 miscgraphics4,5 +1591 miscgraphics4,6 +1592 miscgraphics4,7 +1593 miscgraphics4,8 +1594 miscgraphics4,9 +1595 miscgraphics4,10 +1596 miscgraphics4,11 +1597 miscgraphics4,12 +1598 miscgraphics4,13 +1599 miscgraphics4,14 +1600 thumbs,0 +1601 thumbs,1 +1602 thumbs,2 +1603 thumbs,3 +1604 spectator,0 +1605 spectator,1 +1606 spectator,2 +1607 orb_filler,9 +1608 orb_filler,10 +1609 orb_filler,11 +1610 orb_icon,6 +1611 mini_bottom +1612 keyboard_key_left +1613 keyboard_key_middle +1614 keyboard_key_right +1615 graphic_1615 +1616 osm_drag_camera +1617 options_slider,8 +1618 graphic_1618 +1619 graphic_1619 +1620 graphic_1620 +1621 graphic_1621 +1622 options_slider,9 +1623 graphic_1623 +1624 graphic_1624 +1625 clickcircle_filled +1626 clickcircle_outline +1627 raids_challenge_icon +1628 hitmark,7 +1629 hitmark,8 +1630 hitmark,9 +1631 hitmark,10 +1632 hitmark,11 +1633 hitmark,12 +1634 hitmark,13 +1635 graphic_1635 +1636 orb_filler,12 +1637 orb_filler,13 +1638 options_slider,10 +1639 options_slider,11 +1640 osm_status_icons,0 +1641 osm_status_icons,1 +1642 osm_status_icons,2 +1643 osm_status_icons,3 +1644 osm_status_icons,4 +1645 osm_status_icons,5 +1646 osm_status_icons,6 +1647 osm_status_icons,7 +1648 leafytree_tiled +1649 titlebutton_large +1650 graphic_1650 +1651 graphic_1651 +1652 graphic_1652 +1653 options_icons,50 +1654 options_icons,51 +1655 options_icons,52 +1656 options_icons,53 +1657 options_icons,54 +1658 options_icons,55 +1659 options_icons,56 +1660 options_icons,57 +1661 options_icons,58 +1662 options_icons,59 +1663 graphic_1663 +1664 graphic_1664 +1665 graphic_1665 +1666 graphic_1666 +1667 graphic_1667 +1668 orb_icon,7 +1669 mapfunction,91 +1670 mapfunction,92 +1671 mapfunction,93 +1672 mapfunction,94 +1673 mapfunction,95 +1674 mapfunction,96 +1675 mapfunction,97 +1676 mapfunction,98 +1677 mapfunction,99 +1678 mapfunction,100 +1679 mapfunction,101 +1680 mapfunction,102 +1681 mapfunction,103 +1682 mapfunction,104 +1683 mapfunction,105 +1684 mapfunction,106 +1685 mapfunction,107 +1686 mapfunction,108 +1687 mapfunction,109 +1688 mapfunction,110 +1689 mapfunction,111 +1690 mapfunction,112 +1691 mapfunction,113 +1692 mapfunction,114 +1693 mapfunction,115 +1694 mapfunction,116 +1695 mapfunction,117 +1696 mapfunction,118 +1697 ring_40 +1698 worldmap_icon_large,0 +1699 worldmap_icon_large,1 +1700 options_buttons +1701 options_buttons_large +1702 options_icons_small,4 +1703 options_icons_small,5 +1704 graphic_1704 +1705 graphic_1705 +1706 graphic_1706 +1707 graphic_1707 +1708 side_icons,21 +1709 side_icons,22 +1710 side_icons,23 +1711 sideicons_interface,21 +1712 sideicons_interface,22 +1713 sideicons_interface,23 +1714 graphic_1714 +1715 graphic_1715 +1716 graphic_1716 +1717 graphic_1717 +1718 graphic_1718 +1719 magic_necro_on,54 +1720 magic_necro_on,55 +1721 magic_necro_on,56 +1722 magic_necro_on,57 +1723 magic_necro_on,58 +1724 magic_necro_on,59 +1725 magic_necro_off,50 +1726 mobile_stones,0 +1727 mobile_stones,1 +1728 pvp_warning_icon +1729 stoneborder_close_button,0 +1730 stoneborder_close_button,1 +1731 steelborder_close_button,0 +1732 steelborder_close_button,1 +1733 oge_title,0 +1734 oge_title,1 +1735 oge_title,2 +1736 oge_title,3 +1737 oge_title,4 +1738 open_buttons,0 +1739 open_buttons,1 +1740 open_buttons,2 +1741 open_buttons,3 +1742 oge_images,0 +1743 oge_images,1 +1744 oge_images,2 +1745 oge_images,3 +1746 oge_images,4 +1747 arrowkeys +1748 combat_autoretaliate,0 +1749 combat_autoretaliate,1 +1750 barbassault_icons,8 +1751 2x_standard_spells_on,19 +1752 2x_standard_spells_on,20 +1753 2x_standard_spells_on,21 +1754 2x_standard_spells_on,22 +1755 2x_standard_spells_on,23 +1756 2x_standard_spells_on,24 +1757 2x_standard_spells_on,25 +1758 2x_standard_spells_on,26 +1759 2x_standard_spells_on,27 +1760 2x_standard_spells_on,28 +1761 2x_standard_spells_on,29 +1762 2x_standard_spells_on,30 +1763 2x_standard_spells_on,31 +1764 2x_standard_spells_on,32 +1765 2x_standard_spells_on,33 +1766 2x_standard_spells_on,34 +1767 2x_standard_spells_on,35 +1768 2x_standard_spells_on,36 +1769 2x_standard_spells_on,37 +1770 2x_standard_spells_on,38 +1771 2x_standard_spells_on,39 +1772 2x_standard_spells_on,40 +1773 2x_standard_spells_on,41 +1774 2x_standard_spells_on,42 +1775 2x_standard_spells_on,43 +1776 2x_standard_spells_on,44 +1777 2x_standard_spells_on,45 +1778 2x_standard_spells_on,46 +1779 2x_standard_spells_on,47 +1780 2x_standard_spells_on,48 +1781 2x_standard_spells_on,49 +1782 2x_standard_spells_on,50 +1783 2x_standard_spells_on,51 +1784 2x_standard_spells_on,52 +1785 2x_standard_spells_on,53 +1786 2x_standard_spells_on,54 +1787 2x_standard_spells_on,55 +1788 2x_standard_spells_on,56 +1789 2x_standard_spells_on,57 +1790 2x_standard_spells_on,58 +1791 2x_standard_spells_on,59 +1792 2x_standard_spells_on,60 +1793 2x_standard_spells_on,61 +1794 2x_standard_spells_on,62 +1795 2x_standard_spells_on,63 +1796 2x_standard_spells_on,64 +1797 2x_standard_spells_on,65 +1798 2x_standard_spells_on,66 +1799 2x_standard_spells_on,67 +1800 2x_standard_spells_on,68 +1801 2x_standard_spells_on,69 +1802 2x_standard_spells_on,70 +1803 2x_standard_spells_on,71 +1804 2x_standard_spells_on,72 +1805 2x_standard_spells_on,73 +1806 2x_standard_spells_on,74 +1807 2x_standard_spells_on,75 +1808 2x_standard_spells_on,76 +1809 2x_standard_spells_on,77 +1810 2x_standard_spells_on,78 +1811 2x_standard_spells_on,79 +1812 2x_standard_spells_off,0 +1813 2x_standard_spells_off,1 +1814 2x_standard_spells_off,2 +1815 2x_standard_spells_off,3 +1816 2x_standard_spells_off,4 +1817 2x_standard_spells_off,5 +1818 2x_standard_spells_off,6 +1819 2x_standard_spells_off,7 +1820 2x_standard_spells_off,8 +1821 2x_standard_spells_off,9 +1822 2x_standard_spells_off,10 +1823 2x_standard_spells_off,11 +1824 2x_standard_spells_off,12 +1825 2x_standard_spells_off,13 +1826 2x_standard_spells_off,14 +1827 2x_standard_spells_off,15 +1828 2x_standard_spells_off,16 +1829 2x_standard_spells_off,17 +1830 2x_standard_spells_off,18 +1831 2x_standard_spells_off,19 +1832 2x_standard_spells_off,20 +1833 2x_standard_spells_off,21 +1834 2x_standard_spells_off,22 +1835 2x_standard_spells_off,23 +1836 2x_standard_spells_off,24 +1837 2x_standard_spells_off,25 +1838 2x_standard_spells_off,26 +1839 2x_standard_spells_off,27 +1840 2x_standard_spells_off,28 +1841 2x_standard_spells_off,29 +1842 2x_standard_spells_off,30 +1843 2x_standard_spells_off,31 +1844 2x_standard_spells_off,32 +1845 2x_standard_spells_off,33 +1846 2x_standard_spells_off,34 +1847 2x_standard_spells_off,35 +1848 2x_standard_spells_off,36 +1849 2x_standard_spells_off,37 +1850 2x_standard_spells_off,38 +1851 2x_standard_spells_off,39 +1852 2x_standard_spells_off,40 +1853 2x_standard_spells_off,41 +1854 2x_standard_spells_off,42 +1855 2x_standard_spells_off,43 +1856 2x_standard_spells_off,44 +1857 2x_standard_spells_off,45 +1858 2x_standard_spells_off,46 +1859 2x_standard_spells_off,47 +1860 2x_standard_spells_off,48 +1861 2x_standard_spells_off,49 +1862 2x_standard_spells_off,50 +1863 2x_standard_spells_off,51 +1864 2x_standard_spells_off,52 +1865 2x_standard_spells_off,53 +1866 2x_standard_spells_off,54 +1867 2x_standard_spells_off,55 +1868 2x_standard_spells_off,56 +1869 2x_standard_spells_off,57 +1870 2x_standard_spells_off,58 +1871 2x_standard_spells_off,59 +1872 2x_standard_spells_off,60 +1873 2x_standard_spells_off,61 +1874 2x_standard_spells_off,62 +1875 2x_standard_spells_off,63 +1876 2x_standard_spells_off,64 +1877 2x_standard_spells_off,65 +1878 2x_standard_spells_off,66 +1879 2x_standard_spells_off,67 +1880 2x_standard_spells_off,68 +1881 2x_standard_spells_off,69 +1882 2x_standard_spells_off,70 +1883 2x_standard_spells_off,71 +1884 2x_standard_spells_off,72 +1885 2x_standard_spells_off,73 +1886 2x_standard_spells_off,74 +1887 2x_standard_spells_off,75 +1888 2x_standard_spells_off,76 +1889 2x_standard_spells_off,77 +1890 2x_standard_spells_off,78 +1891 2x_standard_spells_off,79 +1892 2x_ancient_spells_on,0 +1893 2x_ancient_spells_on,1 +1894 2x_ancient_spells_on,2 +1895 2x_ancient_spells_on,3 +1896 2x_ancient_spells_on,4 +1897 2x_ancient_spells_on,5 +1898 2x_ancient_spells_on,6 +1899 2x_ancient_spells_on,7 +1900 2x_ancient_spells_on,8 +1901 2x_ancient_spells_on,9 +1902 2x_ancient_spells_on,10 +1903 2x_ancient_spells_on,11 +1904 2x_ancient_spells_on,12 +1905 2x_ancient_spells_on,13 +1906 2x_ancient_spells_on,14 +1907 2x_ancient_spells_on,15 +1908 2x_ancient_spells_on,16 +1909 2x_ancient_spells_on,17 +1910 2x_ancient_spells_on,18 +1911 2x_ancient_spells_on,19 +1912 2x_ancient_spells_on,20 +1913 2x_ancient_spells_on,21 +1914 2x_ancient_spells_on,22 +1915 2x_ancient_spells_on,23 +1916 2x_ancient_spells_on,24 +1917 2x_ancient_spells_on,25 +1918 2x_ancient_spells_on,26 +1919 2x_ancient_spells_on,27 +1920 2x_ancient_spells_on,28 +1921 2x_ancient_spells_on,29 +1922 2x_ancient_spells_off,0 +1923 2x_ancient_spells_off,1 +1924 2x_ancient_spells_off,2 +1925 2x_ancient_spells_off,3 +1926 2x_ancient_spells_off,4 +1927 2x_ancient_spells_off,5 +1928 2x_ancient_spells_off,6 +1929 2x_ancient_spells_off,7 +1930 2x_ancient_spells_off,8 +1931 2x_ancient_spells_off,9 +1932 2x_ancient_spells_off,10 +1933 2x_ancient_spells_off,11 +1934 2x_ancient_spells_off,12 +1935 2x_ancient_spells_off,13 +1936 2x_ancient_spells_off,14 +1937 2x_ancient_spells_off,15 +1938 2x_ancient_spells_off,16 +1939 2x_ancient_spells_off,17 +1940 2x_ancient_spells_off,18 +1941 2x_ancient_spells_off,19 +1942 2x_ancient_spells_off,20 +1943 2x_ancient_spells_off,21 +1944 2x_ancient_spells_off,22 +1945 2x_ancient_spells_off,23 +1946 2x_ancient_spells_off,24 +1947 2x_ancient_spells_off,25 +1948 2x_ancient_spells_off,26 +1949 2x_ancient_spells_off,27 +1950 2x_ancient_spells_off,28 +1951 2x_ancient_spells_off,29 +1952 2x_lunar_spells_on,0 +1953 2x_lunar_spells_on,1 +1954 2x_lunar_spells_on,2 +1955 2x_lunar_spells_on,3 +1956 2x_lunar_spells_on,4 +1957 2x_lunar_spells_on,5 +1958 2x_lunar_spells_on,6 +1959 2x_lunar_spells_on,7 +1960 2x_lunar_spells_on,8 +1961 2x_lunar_spells_on,9 +1962 2x_lunar_spells_on,10 +1963 2x_lunar_spells_on,11 +1964 2x_lunar_spells_on,12 +1965 2x_lunar_spells_on,13 +1966 2x_lunar_spells_on,14 +1967 2x_lunar_spells_on,15 +1968 2x_lunar_spells_on,16 +1969 2x_lunar_spells_on,17 +1970 2x_lunar_spells_on,18 +1971 2x_lunar_spells_on,19 +1972 2x_lunar_spells_on,20 +1973 2x_lunar_spells_on,21 +1974 2x_lunar_spells_on,22 +1975 2x_lunar_spells_on,23 +1976 2x_lunar_spells_on,24 +1977 2x_lunar_spells_on,25 +1978 2x_lunar_spells_on,26 +1979 2x_lunar_spells_on,27 +1980 2x_lunar_spells_on,28 +1981 2x_lunar_spells_on,29 +1982 2x_lunar_spells_on,30 +1983 2x_lunar_spells_on,31 +1984 2x_lunar_spells_on,32 +1985 2x_lunar_spells_on,33 +1986 2x_lunar_spells_on,34 +1987 2x_lunar_spells_on,35 +1988 2x_lunar_spells_on,36 +1989 2x_lunar_spells_on,37 +1990 2x_lunar_spells_on,38 +1991 2x_lunar_spells_on,39 +1992 2x_lunar_spells_on,40 +1993 2x_lunar_spells_on,41 +1994 2x_lunar_spells_on,42 +1995 2x_lunar_spells_on,43 +1996 2x_lunar_spells_on,44 +1997 2x_lunar_spells_on,45 +1998 2x_lunar_spells_on,46 +1999 2x_lunar_spells_on,47 +2000 2x_lunar_spells_on,48 +2001 2x_lunar_spells_on,49 +2002 2x_lunar_spells_off,0 +2003 2x_lunar_spells_off,1 +2004 2x_lunar_spells_off,2 +2005 2x_lunar_spells_off,3 +2006 2x_lunar_spells_off,4 +2007 2x_lunar_spells_off,5 +2008 2x_lunar_spells_off,6 +2009 2x_lunar_spells_off,7 +2010 2x_lunar_spells_off,8 +2011 2x_lunar_spells_off,9 +2012 2x_lunar_spells_off,10 +2013 2x_lunar_spells_off,11 +2014 2x_lunar_spells_off,12 +2015 2x_lunar_spells_off,13 +2016 2x_lunar_spells_off,14 +2017 2x_lunar_spells_off,15 +2018 2x_lunar_spells_off,16 +2019 2x_lunar_spells_off,17 +2020 2x_lunar_spells_off,18 +2021 2x_lunar_spells_off,19 +2022 2x_lunar_spells_off,20 +2023 2x_lunar_spells_off,21 +2024 2x_lunar_spells_off,22 +2025 2x_lunar_spells_off,23 +2026 2x_lunar_spells_off,24 +2027 2x_lunar_spells_off,25 +2028 2x_lunar_spells_off,26 +2029 2x_lunar_spells_off,27 +2030 2x_lunar_spells_off,28 +2031 2x_lunar_spells_off,29 +2032 2x_lunar_spells_off,30 +2033 2x_lunar_spells_off,31 +2034 2x_lunar_spells_off,32 +2035 2x_lunar_spells_off,33 +2036 2x_lunar_spells_off,34 +2037 2x_lunar_spells_off,35 +2038 2x_lunar_spells_off,36 +2039 2x_lunar_spells_off,37 +2040 2x_lunar_spells_off,38 +2041 2x_lunar_spells_off,39 +2042 2x_lunar_spells_off,40 +2043 2x_lunar_spells_off,41 +2044 2x_lunar_spells_off,42 +2045 2x_lunar_spells_off,43 +2046 2x_lunar_spells_off,44 +2047 2x_lunar_spells_off,45 +2048 2x_lunar_spells_off,46 +2049 2x_lunar_spells_off,47 +2050 2x_lunar_spells_off,48 +2051 2x_lunar_spells_off,49 +2052 2x_necro_spells_on,0 +2053 2x_necro_spells_on,1 +2054 2x_necro_spells_on,2 +2055 2x_necro_spells_on,3 +2056 2x_necro_spells_on,4 +2057 2x_necro_spells_on,5 +2058 2x_necro_spells_on,6 +2059 2x_necro_spells_on,7 +2060 2x_necro_spells_on,8 +2061 2x_necro_spells_on,9 +2062 2x_necro_spells_on,10 +2063 2x_necro_spells_on,11 +2064 2x_necro_spells_on,12 +2065 2x_necro_spells_on,13 +2066 2x_necro_spells_on,14 +2067 2x_necro_spells_on,15 +2068 2x_necro_spells_on,16 +2069 2x_necro_spells_on,17 +2070 2x_necro_spells_on,18 +2071 2x_necro_spells_on,19 +2072 2x_necro_spells_on,20 +2073 2x_necro_spells_on,21 +2074 2x_necro_spells_on,22 +2075 2x_necro_spells_on,23 +2076 2x_necro_spells_on,24 +2077 2x_necro_spells_on,25 +2078 2x_necro_spells_on,26 +2079 2x_necro_spells_on,27 +2080 2x_necro_spells_on,28 +2081 2x_necro_spells_on,29 +2082 2x_necro_spells_on,30 +2083 2x_necro_spells_on,31 +2084 2x_necro_spells_on,32 +2085 2x_necro_spells_on,33 +2086 2x_necro_spells_on,34 +2087 2x_necro_spells_on,35 +2088 2x_necro_spells_on,36 +2089 2x_necro_spells_on,37 +2090 2x_necro_spells_on,38 +2091 2x_necro_spells_on,39 +2092 2x_necro_spells_off,0 +2093 2x_necro_spells_off,1 +2094 2x_necro_spells_off,2 +2095 2x_necro_spells_off,3 +2096 2x_necro_spells_off,4 +2097 2x_necro_spells_off,5 +2098 2x_necro_spells_off,6 +2099 2x_necro_spells_off,7 +2100 2x_necro_spells_off,8 +2101 2x_necro_spells_off,9 +2102 2x_necro_spells_off,10 +2103 2x_necro_spells_off,11 +2104 2x_necro_spells_off,12 +2105 2x_necro_spells_off,13 +2106 2x_necro_spells_off,14 +2107 2x_necro_spells_off,15 +2108 2x_necro_spells_off,16 +2109 2x_necro_spells_off,17 +2110 2x_necro_spells_off,18 +2111 2x_necro_spells_off,19 +2112 2x_necro_spells_off,20 +2113 2x_necro_spells_off,21 +2114 2x_necro_spells_off,22 +2115 2x_necro_spells_off,23 +2116 2x_necro_spells_off,24 +2117 2x_necro_spells_off,25 +2118 2x_necro_spells_off,26 +2119 2x_necro_spells_off,27 +2120 2x_necro_spells_off,28 +2121 2x_necro_spells_off,29 +2122 2x_necro_spells_off,30 +2123 2x_necro_spells_off,31 +2124 2x_necro_spells_off,32 +2125 2x_necro_spells_off,33 +2126 2x_necro_spells_off,34 +2127 2x_necro_spells_off,35 +2128 2x_necro_spells_off,36 +2129 2x_necro_spells_off,37 +2130 2x_necro_spells_off,38 +2131 2x_necro_spells_off,39 +2132 logo_osrs +2133 play_now_text +2134 login_icons +2135 titlebutton_wide +2136 titlebutton_wide42,0 +2137 titlebutton_wide42,1 +2138 ring_34,0 +2139 stony_basalt,0 +2140 orb_frame,2 +2141 stony_basalt,1 +2142 dashed_border,0 +2143 dashed_border,1 +2144 dashed_border,2 +2145 dashed_border,3 +2146 dashed_border,4 +2147 dashed_border,5 +2148 drag_handle +2149 graphic_2149 +2150 graphic_2150 +2151 graphic_2151 +2152 close_buttons,10 +2153 close_buttons,11 +2154 resize_map_clickmask +2155 graphic_2155 +2156 mod_icons_interface,0 +2157 mod_icons_interface,1 +2158 mod_icons_interface,2 +2159 mod_icons_interface,3 +2160 mod_icons_interface,4 +2161 mod_icons_interface,5 +2162 mod_icons_interface,6 +2163 mod_icons_interface,7 +2164 mod_icons_interface,8 +2165 mod_icons_interface,9 +2166 mod_icons_interface,10 +2167 mod_icons_interface,11 +2168 mod_icons_interface,12 +2169 mod_icons_interface,13 +2170 mod_icons_interface,14 +2171 mod_icons_interface,15 +2172 mod_icons_interface,16 +2173 mod_icons_interface,17 +2174 mod_icons_interface,18 +2175 mod_icons_interface,19 +2176 standard_health_30,0 +2177 standard_health_30,1 +2178 standard_health_50,0 +2179 standard_health_50,1 +2180 standard_health_60,0 +2181 standard_health_60,1 +2182 standard_health_80,0 +2183 standard_health_80,1 +2184 standard_health_100,0 +2185 standard_health_100,1 +2186 standard_health_120,0 +2187 standard_health_120,1 +2188 standard_health_140,0 +2189 standard_health_140,1 +2190 standard_health_160,0 +2191 standard_health_160,1 +2192 gauntlet_map_icons,0 +2193 gauntlet_map_icons,1 +2194 gauntlet_map_icons,2 +2195 gauntlet_map_icons,3 +2196 gauntlet_map_icons,4 +2197 gauntlet_map_icons,5 +2198 gauntlet_map_icons,6 +2199 gauntlet_map_icons,7 +2200 gauntlet_map_icons,8 +2201 gauntlet_map_icons,9 +2202 gauntlet_map_icons,10 +2203 gauntlet_map_icons,11 +2204 worldmap_icon,2 +2205 worldmap_icon,3 +2206 worldmap_icon_large,2 +2207 worldmap_icon_large,3 +2208 orb_filler,14 +2209 graphic_2209 +2210 mapfunction,119 +2211 mapfunction,120 +2212 mapfunction,121 +2213 mapfunction,122 +2214 mapfunction,123 +2215 mapfunction,124 +2216 mapfunction,125 +2217 close_buttons,8 +2218 close_buttons,9 +2219 achievement_diary_icons,4 +2220 adventurepath_placeholder +2221 graphic_2221 +2222 graphic_2222 +2223 graphic_2223 +2224 graphic_2224 +2225 pvpw_icons,3 +2226 bankbuttons,8 +2227 graphic_2227 +2228 getting_started_with_attack +2229 getting_a_grip_on_it +2230 weapons_master +2231 getting_started_with_strength +2232 building_muscle +2233 body_builder +2234 getting_started_with_defence +2235 sticks_and_stones +2236 mithril_defence +2237 getting_started_with_magic +2238 striking_a_pose +2239 bringer_of_chaos +2240 getting_started_with_ranged +2241 honing_my_aim +2242 sharpshooter +2243 logo_speedrunning +2244 graphic_2244 +2245 hitmark,36 +2246 close_arrows,0 +2247 brown_border +2248 button_brown_small +2249 rightarrow_small_stone,0 +2250 rightarrow_small_stone,1 +2251 poh_board_icons,0 +2252 poh_board_icons,1 +2253 poh_board_icons,2 +2254 poh_board_icons,3 +2255 poh_board_icons,4 +2256 poh_board_icons,5 +2257 poh_board_icons,6 +2258 poh_board_icons,7 +2259 graphic_2259 +2260 graphic_2260 +2261 graphic_2261 +2262 graphic_2262 +2263 graphic_2263 +2264 graphic_2264 +2265 graphic_2265 +2266 square_button,0 +2267 square_button,1 +2268 tradebacking_grey +2269 logo_seasonal_mode +2270 hitmark,17 +2271 headicons_pk_interface,15 +2272 achievement_diary_icons,5 +2273 achievement_diary_icons,6 +2274 achievement_diary_icons,7 +2275 achievement_diary_icons,8 +2276 achievement_diary_icons,9 +2277 tabs_short,0 +2278 tabs_short,1 +2279 tabs_short,2 +2280 tabs_short,3 +2281 tabs_short,4 +2282 tabs_short,5 +2283 tabs_tall,0 +2284 tabs_tall,1 +2285 tabs_tall,2 +2286 tabs_tall,3 +2287 tabs_tall,4 +2288 tabs_tall,5 +2289 menu_buttons,0 +2290 menu_buttons,1 +2291 open_buttons,4 +2292 open_buttons,5 +2293 open_buttons,6 +2294 open_buttons,7 +2295 side_icons,24 +2296 side_icons,25 +2297 side_icons,26 +2298 side_icons,27 +2299 side_icons,28 +2300 side_icons,29 +2301 side_icons,30 +2302 side_icons,31 +2303 sideicons_interface,24 +2304 sideicons_interface,25 +2305 sideicons_interface,26 +2306 sideicons_interface,27 +2307 sideicons_interface,28 +2308 sideicons_interface,29 +2309 sideicons_interface,30 +2310 sideicons_interface,31 +2311 worldswitcher_stars,5 +2312 open_buttons_small,0 +2313 open_buttons_small,1 +2314 open_buttons_small,2 +2315 open_buttons_small,3 +2316 league_task_tiers,0 +2317 league_task_tiers,1 +2318 league_task_tiers,2 +2319 league_task_tiers,3 +2320 league_task_tiers,4 +2321 league_relics_large,0 +2322 league_relics_large,1 +2323 league_relics_large,2 +2324 league_relics_large,3 +2325 league_relics_large,4 +2326 league_relics_large,5 +2327 league_relics_large,6 +2328 league_relics_large,7 +2329 league_relics_large,8 +2330 league_relics_large,9 +2331 league_relics_large,10 +2332 league_relics_large,11 +2333 league_relics_large,12 +2334 league_relics_large,13 +2335 league_relics_large,14 +2336 league_relics_large,15 +2337 league_relics_large,16 +2338 league_relics_large,17 +2339 league_relics_large,18 +2340 league_relics_large,19 +2341 league_relics_large,20 +2342 league_relics_large,21 +2343 league_relics_large,22 +2344 league_relics_large,23 +2345 league_relics_large,24 +2346 league_relics_large,25 +2347 league_relics_large,26 +2348 league_relics_large,27 +2349 league_relics_large,28 +2350 league_relics_large,29 +2351 league_relics_large,30 +2352 league_relics_large,31 +2353 league_relics,0 +2354 league_relics,1 +2355 league_relics,2 +2356 league_relics,3 +2357 league_relics,4 +2358 league_relics,5 +2359 league_relics,6 +2360 league_relics,7 +2361 league_relics,8 +2362 league_relics,9 +2363 league_relics,10 +2364 league_relics,11 +2365 league_relics,12 +2366 league_relics,13 +2367 league_relics,14 +2368 league_relics,15 +2369 league_relics,16 +2370 league_relics,17 +2371 league_relics,18 +2372 league_relics,19 +2373 league_relics,20 +2374 league_relics,21 +2375 league_relics,22 +2376 league_relics,23 +2377 league_relics,24 +2378 league_relics,25 +2379 league_relics,26 +2380 league_relics,27 +2381 league_relics,28 +2382 league_relics,29 +2383 league_relics,30 +2384 league_relics,31 +2385 leftarrow_highlight +2386 league_progress_bar,0 +2387 league_progress_bar,1 +2388 league_progress_bar_indicator +2389 rightarrow_highlight +2390 league_relics_tli,0 +2391 league_relics_tli,1 +2392 league_relics_tli,2 +2393 league_relics_tli,3 +2394 league_relics_tli,4 +2395 league_relics_tli,5 +2396 league_relics_tli,6 +2397 league_relics_tli,7 +2398 league_relics_tli,8 +2399 league_relics_tli,9 +2400 league_relics_tli,10 +2401 league_relics_tli,11 +2402 league_relics_tli,12 +2403 league_relics_tli,13 +2404 league_relics_tli,14 +2405 league_relics_tli,15 +2406 tabs_short,6 +2407 tabs_short,7 +2408 tabs_tall,6 +2409 tabs_tall,7 +2410 graphic_2410 +2411 graphic_2411 +2412 graphic_2412 +2413 graphic_2413 +2414 graphic_2414 +2415 graphic_2415 +2416 graphic_2416 +2417 banktab_icons,3 +2418 icy_basalt,0 +2419 icy_basalt,1 +2420 wiki_icon,0 +2421 wiki_icon,1 +2422 fixed_map_clickmask_wiki +2423 emotes,48 +2424 emotes,49 +2425 emotes,50 +2426 emotes,51 +2427 emotes_locked,28 +2428 emotes_locked,29 +2429 emotes_locked,30 +2430 emotes_locked,31 +2431 standard_health_40,0 +2432 standard_health_40,1 +2433 standard_shield_30,0 +2434 standard_shield_30,1 +2435 standard_shield_40,0 +2436 standard_shield_40,1 +2437 standard_shield_50,0 +2438 standard_shield_50,1 +2439 standard_shield_60,0 +2440 standard_shield_60,1 +2441 standard_shield_80,0 +2442 standard_shield_80,1 +2443 standard_shield_100,0 +2444 standard_shield_100,1 +2445 standard_shield_120,0 +2446 standard_shield_120,1 +2447 standard_shield_140,0 +2448 standard_shield_140,1 +2449 standard_shield_160,0 +2450 standard_shield_160,1 +2451 standard_armour_30,0 +2452 standard_armour_30,1 +2453 standard_armour_40,0 +2454 standard_armour_40,1 +2455 standard_armour_50,0 +2456 standard_armour_50,1 +2457 standard_armour_60,0 +2458 standard_armour_60,1 +2459 standard_armour_80,0 +2460 standard_armour_80,1 +2461 standard_armour_100,0 +2462 standard_armour_100,1 +2463 standard_armour_120,0 +2464 standard_armour_120,1 +2465 standard_armour_140,0 +2466 standard_armour_140,1 +2467 standard_armour_160,0 +2468 standard_armour_160,1 +2469 standard_charge_30,0 +2470 standard_charge_30,1 +2471 standard_charge_40,0 +2472 standard_charge_40,1 +2473 standard_charge_50,0 +2474 standard_charge_50,1 +2475 standard_charge_60,0 +2476 standard_charge_60,1 +2477 standard_charge_80,0 +2478 standard_charge_80,1 +2479 standard_charge_100,0 +2480 standard_charge_100,1 +2481 standard_charge_120,0 +2482 standard_charge_120,1 +2483 standard_charge_140,0 +2484 standard_charge_140,1 +2485 standard_charge_160,0 +2486 standard_charge_160,1 +2487 ap_border,0 +2488 ap_border,1 +2489 ap_border,2 +2490 ap_border,3 +2491 ap_border,4 +2492 ap_border,5 +2493 ap_border,6 +2494 ap_border,7 +2495 no_path +2496 graphic_2496 +2497 graphic_2497 +2498 getting_started_with_fishing +2499 getting_started_with_mining +2500 getting_started_with_woodcutting +2501 headicons_pk_interface,16 +2502 headicons_pk_interface,17 +2503 headicons_pk_interface,18 +2504 headicons_pk_interface,19 +2505 headicons_pk_interface,20 +2506 mod_icons_interface,20 +2507 mod_icons_interface,21 +2508 mod_icons_interface,22 +2509 mod_icons_interface,23 +2510 mod_icons_interface,24 +2511 mod_icons_interface,25 +2512 mod_icons_interface,26 +2513 mod_icons_interface,27 +2514 mod_icons_interface,28 +2515 mod_icons_interface,29 +2516 hud_brackets,0 +2517 hud_brackets,1 +2518 graphic_2518 +2519 single_arrow +2520 worldmap_marker,3 +2521 menu_buttons,2 +2522 menu_buttons,3 +2523 map_resize +2524 highlight_border,0 +2525 highlight_border,1 +2526 highlight_border,2 +2527 highlight_border,3 +2528 highlight_border,4 +2529 highlight_border,5 +2530 highlight_border,6 +2531 highlight_border,7 +2532 question_button,0 +2533 question_button,1 +2534 scroll_icon +2535 square_button_large,0 +2536 square_button_large,1 +2537 square_button_large,2 +2538 rect_button,0 +2539 rect_button,1 +2540 graphic_2540 +2541 graphic_2541 +2542 graphic_2542 +2543 graphic_2543 +2544 graphic_2544 +2545 graphic_2545 +2546 steelborder_divider,0 +2547 worldmap_marker,4 +2548 worldmap_marker,5 +2549 ap_path_icon,0 +2550 ap_path_icon,1 +2551 ap_path_icon,2 +2552 ap_path_icon,3 +2553 ap_path_icon,4 +2554 ap_path_icon,5 +2555 ap_path_banner,0 +2556 ap_path_banner,1 +2557 ap_path_banner,2 +2558 ap_path_banner,3 +2559 ap_path_banner,4 +2560 automatic_wizard +2561 steelborder_divider,1 +2562 steelborder_divider,2 +2563 steelborder_divider,3 +2564 graphic_2564 +2565 graphic_2565 +2566 graphic_2566 +2567 graphic_2567 +2568 graphic_2568 +2569 graphic_2569 +2570 graphic_2570 +2571 graphic_2571 +2572 graphic_2572 +2573 graphic_2573 +2574 graphic_2574 +2575 graphic_2575 +2576 graphic_2576 +2577 graphic_2577 +2578 graphic_2578 +2579 graphic_2579 +2580 mod_icons_interface,30 +2581 mod_icons_interface,31 +2582 mod_icons_interface,32 +2583 mod_icons_interface,33 +2584 mod_icons_interface,34 +2585 mod_icons_interface,35 +2586 mod_icons_interface,36 +2587 mod_icons_interface,37 +2588 mod_icons_interface,38 +2589 mod_icons_interface,39 +2590 leagues_membership_banner +2591 upgrade_membership_button +2592 upgrade_membership_button_highlight +2593 league_relics_outline,0 +2594 league_relics_outline,1 +2595 league_tiny_relic,0 +2596 league_tiny_relic,1 +2597 league_tutorial,0 +2598 league_tutorial,1 +2599 league_tutorial,2 +2600 league_tutorial,3 +2601 league_tutorial,4 +2602 league_tutorial,5 +2603 league_map +2604 trailblazer_relics_large,0 +2605 trailblazer_relics_large,1 +2606 trailblazer_relics_large,2 +2607 trailblazer_relics_large,3 +2608 trailblazer_relics_large,4 +2609 trailblazer_relics_large,5 +2610 trailblazer_relics_large,6 +2611 trailblazer_relics_large,7 +2612 trailblazer_relics_large,8 +2613 trailblazer_relics_large,9 +2614 trailblazer_relics_large,10 +2615 trailblazer_relics_large,11 +2616 trailblazer_relics_large,12 +2617 trailblazer_relics_large,13 +2618 trailblazer_relics_large,14 +2619 trailblazer_relics_large,15 +2620 trailblazer_relics_large,16 +2621 trailblazer_relics_large,17 +2622 trailblazer_relics_large,18 +2623 trailblazer_relics_large,19 +2624 trailblazer_relics_large,20 +2625 trailblazer_relics_large,21 +2626 trailblazer_relics_large,22 +2627 trailblazer_relics_large,23 +2628 trailblazer_relics_large,24 +2629 trailblazer_relics_large,25 +2630 trailblazer_relics_large,26 +2631 trailblazer_relics_large,27 +2632 trailblazer_relics_large,28 +2633 trailblazer_relics_large,29 +2634 trailblazer_relics_large,30 +2635 trailblazer_relics_large,31 +2636 trailblazer_relics_large,32 +2637 trailblazer_relics_large,33 +2638 trailblazer_relics_large,34 +2639 trailblazer_relics_large,35 +2640 trailblazer_relics_large,36 +2641 trailblazer_relics_large,37 +2642 trailblazer_relics_large,38 +2643 trailblazer_relics_large,39 +2644 trailblazer_relics_large,40 +2645 trailblazer_relics_large,41 +2646 trailblazer_relics_large,42 +2647 trailblazer_relics_large,43 +2648 trailblazer_relics_large,44 +2649 trailblazer_relics_large,45 +2650 trailblazer_relics_large,46 +2651 trailblazer_relics_large,47 +2652 trailblazer_relics,0 +2653 trailblazer_relics,1 +2654 trailblazer_relics,2 +2655 trailblazer_relics,3 +2656 trailblazer_relics,4 +2657 trailblazer_relics,5 +2658 trailblazer_relics,6 +2659 trailblazer_relics,7 +2660 trailblazer_relics,8 +2661 trailblazer_relics,9 +2662 trailblazer_relics,10 +2663 trailblazer_relics,11 +2664 trailblazer_relics,12 +2665 trailblazer_relics,13 +2666 trailblazer_relics,14 +2667 trailblazer_relics,15 +2668 trailblazer_relics,16 +2669 trailblazer_relics,17 +2670 trailblazer_relics,18 +2671 trailblazer_relics,19 +2672 trailblazer_relics,20 +2673 trailblazer_relics,21 +2674 trailblazer_relics,22 +2675 trailblazer_relics,23 +2676 trailblazer_relics,24 +2677 trailblazer_relics,25 +2678 trailblazer_relics,26 +2679 trailblazer_relics,27 +2680 trailblazer_relics,28 +2681 trailblazer_relics,29 +2682 trailblazer_relics,30 +2683 trailblazer_relics,31 +2684 trailblazer_relics,32 +2685 trailblazer_relics,33 +2686 trailblazer_relics,34 +2687 trailblazer_relics,35 +2688 trailblazer_relics,36 +2689 trailblazer_relics,37 +2690 trailblazer_relics,38 +2691 trailblazer_relics,39 +2692 trailblazer_relics,40 +2693 trailblazer_relics,41 +2694 trailblazer_relics,42 +2695 trailblazer_relics,43 +2696 trailblazer_relics,44 +2697 trailblazer_relics,45 +2698 trailblazer_relics,46 +2699 trailblazer_relics,47 +2700 trailblazer_relics_tli,0 +2701 trailblazer_relics_tli,1 +2702 trailblazer_relics_tli,2 +2703 trailblazer_relics_tli,3 +2704 trailblazer_relics_tli,4 +2705 trailblazer_relics_tli,5 +2706 trailblazer_relics_tli,6 +2707 trailblazer_relics_tli,7 +2708 trailblazer_relics_tli,8 +2709 trailblazer_relics_tli,9 +2710 trailblazer_relics_tli,10 +2711 trailblazer_relics_tli,11 +2712 trailblazer_relics_tli,12 +2713 trailblazer_relics_tli,13 +2714 trailblazer_relics_tli,14 +2715 trailblazer_relics_tli,15 +2716 trailblazer_relics_tli,16 +2717 trailblazer_relics_tli,17 +2718 trailblazer_progress_bar,0 +2719 trailblazer_progress_bar,1 +2720 trailblazer_progress_bar_indicator +2721 trailblazer_map_empty +2722 trailblazer_map_misthalin +2723 trailblazer_map_karamja +2724 trailblazer_map_wilderness +2725 trailblazer_map_asgarnia +2726 trailblazer_map_kandarin +2727 trailblazer_map_desert +2728 trailblazer_map_fremennik +2729 trailblazer_map_tirannwn +2730 trailblazer_map_morytania +2731 trailblazer_map_shields,0 +2732 trailblazer_map_shields,1 +2733 trailblazer_map_shields,2 +2734 trailblazer_map_shields,3 +2735 trailblazer_map_shields,4 +2736 trailblazer_map_shields,5 +2737 trailblazer_map_shields,6 +2738 trailblazer_map_shields,7 +2739 trailblazer_map_shields,8 +2740 trailblazer_map_shields,9 +2741 trailblazer_map_shields,10 +2742 trailblazer_map_shields,11 +2743 trailblazer_map_shields,12 +2744 trailblazer_map_shields,13 +2745 trailblazer_map_shields,14 +2746 trailblazer_map_shields,15 +2747 trailblazer_map_shields,16 +2748 trailblazer_map_shields,17 +2749 trailblazer_map_shields_large,0 +2750 trailblazer_map_shields_large,1 +2751 trailblazer_map_shields_large,2 +2752 trailblazer_map_shields_large,3 +2753 trailblazer_map_shields_large,4 +2754 trailblazer_map_shields_large,5 +2755 trailblazer_map_shields_large,6 +2756 trailblazer_map_shields_large,7 +2757 trailblazer_map_shields_large,8 +2758 trailblazer_map_shields_large,9 +2759 trailblazer_map_shields_large,10 +2760 trailblazer_map_shields_large,11 +2761 trailblazer_map_shields_large,12 +2762 trailblazer_map_shields_large,13 +2763 trailblazer_map_shields_large,14 +2764 trailblazer_map_shields_large,15 +2765 trailblazer_map_shields_large,16 +2766 trailblazer_map_shields_large,17 +2767 trailblazer_map_names,0 +2768 trailblazer_map_names,1 +2769 trailblazer_map_names,2 +2770 trailblazer_map_names,3 +2771 trailblazer_map_names,4 +2772 trailblazer_map_names,5 +2773 trailblazer_map_names,6 +2774 trailblazer_map_names,7 +2775 trailblazer_map_names,8 +2776 trailblazer_map_names,9 +2777 trailblazer_map_names,10 +2778 trailblazer_map_names,11 +2779 trailblazer_map_names,12 +2780 trailblazer_map_names,13 +2781 trailblazer_map_names,14 +2782 trailblazer_map_names,15 +2783 trailblazer_map_names,16 +2784 trailblazer_map_names,17 +2785 trailblazer_map_names,18 +2786 trailblazer_map_names,19 +2787 graphic_2787 +2788 graphic_2788 +2789 graphic_2789 +2790 graphic_2790 +2791 graphic_2791 +2792 graphic_2792 +2793 graphic_2793 +2794 graphic_2794 +2795 graphic_2795 +2796 graphic_2796 +2797 graphic_2797 +2798 graphic_2798 +2799 graphic_2799 +2800 graphic_2800 +2801 graphic_2801 +2802 graphic_2802 +2803 graphic_2803 +2804 graphic_2804 +2805 graphic_2805 +2806 graphic_2806 +2807 graphic_2807 +2808 graphic_2808 +2809 graphic_2809 +2810 graphic_2810 +2811 graphic_2811 +2812 graphic_2812 +2813 graphic_2813 +2814 graphic_2814 +2815 graphic_2815 +2816 graphic_2816 +2817 graphic_2817 +2818 bankbuttons,9 +2819 bankbuttons,10 +2820 bankbuttons,11 +2821 bankbuttons,12 +2822 bankbuttons,13 +2823 bankbuttons,14 +2824 bankbuttons,15 +2825 chatchannel_rank_icons,0 +2826 chatchannel_rank_icons,1 +2827 chatchannel_rank_icons,2 +2828 chatchannel_rank_icons,3 +2829 chatchannel_rank_icons,4 +2830 chatchannel_rank_icons,5 +2831 chatchannel_rank_icons,6 +2832 chatchannel_rank_icons,7 +2833 chatchannel_rank_icons,8 +2834 settings_tabs,11 +2835 settings_tabs,12 +2836 settings_tabs,13 +2837 settings_tabs,14 +2838 settings_tabs,15 +2839 settings_tabs,16 +2840 settings_tabs,17 +2841 settings_tabs,18 +2842 settings_tabs,19 +2843 settings_tabs,20 +2844 settings_tabs,21 +2845 settings_tabs,22 +2846 settings_tabs,23 +2847 toggle_box,0 +2848 toggle_box,1 +2849 toggle_box,2 +2850 toggle_box,3 +2851 toggle_box,4 +2852 settings_slider,0 +2853 settings_slider,1 +2854 settings_slider,2 +2855 settings_slider,3 +2856 settings_slider,4 +2857 settings_slider,5 +2858 settings_slider,6 +2859 settings_slider,7 +2860 settings_slider,8 +2861 settings_slider,9 +2862 settings_slider,10 +2863 settings_slider,11 +2864 v2_borders_slim,11 +2865 v2_borders_slim,12 +2866 v2_borders_slim,13 +2867 v2_borders_slim,14 +2868 v2_borders_slim,15 +2869 v2_borders_slim,16 +2870 v2_borders_slim,17 +2871 v2_borders_slim,18 +2872 v2_borders_slim,19 +2873 v2_borders_slim,20 +2874 v2_borders_slim,21 +2875 v2_borders_slim,22 +2876 v2_borders_slim,23 +2877 v2_borders_slim,24 +2878 v2_borders_slim,25 +2879 v2_borders_slim,26 +2880 v2_borders_slim,27 +2881 v2_borders_slim,28 +2882 v2_borders_slim,29 +2883 v2_borders_slim,30 +2884 v2_borders_slim,31 +2885 v2_borders_slim,32 +2886 v2_borders_slim,33 +2887 v2_borders_slim,34 +2888 v2_borders_slim,35 +2889 v2_borders_slim,36 +2890 v2_borders_slim,37 +2891 v2_borders_slim,38 +2892 v2_borders_slim,39 +2893 v2_borders_slim,40 +2894 v2_borders_slim,41 +2895 v2_borders_slim,42 +2896 v2_borders_slim,43 +2897 graphic_2897 +2898 graphic_2898 +2899 graphic_2899 +2900 graphic_2900 +2901 graphic_2901 +2902 graphic_2902 +2903 graphic_2903 +2904 graphic_2904 +2905 graphic_2905 +2906 graphic_2906 +2907 graphic_2907 +2908 graphic_2908 +2909 graphic_2909 +2910 graphic_2910 +2911 graphic_2911 +2912 graphic_2912 +2913 graphic_2913 +2914 graphic_2914 +2915 graphic_2915 +2916 graphic_2916 +2917 graphic_2917 +2918 graphic_2918 +2919 graphic_2919 +2920 graphic_2920 +2921 soul_wars_staticons,0 +2922 soul_wars_staticons,1 +2923 soul_wars_staticons,2 +2924 soul_wars_staticons,3 +2925 soul_wars_staticons,4 +2926 soul_wars_staticons,5 +2927 soul_wars_staticons,6 +2928 soul_wars_staticons,7 +2929 soul_wars_gameicons,0 +2930 soul_wars_gameicons,1 +2931 soul_wars_gameicons,2 +2932 graphic_2932 +2933 graphic_2933 +2934 graphic_2934 +2935 graphic_2935 +2936 graphic_2936 +2937 graphic_2937 +2938 graphic_2938 +2939 league_relics,32 +2940 league_relics,33 +2941 league_relics,34 +2942 league_relics,35 +2943 league_relics,36 +2944 league_relics,37 +2945 league_relics,38 +2946 league_relics,39 +2947 graphic_2947 +2948 close_buttons,12 +2949 close_buttons,13 +2950 close_buttons,14 +2951 close_buttons,15 +2952 close_buttons,16 +2953 close_buttons,17 +2954 scrollbar_v2_dark,0 +2955 scrollbar_v2_dark,1 +2956 scrollbar_dragger_v2_dark,0 +2957 scrollbar_dragger_v2_dark,1 +2958 scrollbar_dragger_v2_dark,2 +2959 scrollbar_dragger_v2_dark,3 +2960 mapfunction,126 +2961 mapfunction,127 +2962 mapfunction,128 +2963 mapfunction,129 +2964 mapfunction,130 +2965 mapfunction,131 +2966 mapfunction,132 +2967 graphic_2967 +2968 graphic_2968 +2969 graphic_2969 +2970 giants_foundry_moulds_big,0 +2971 standard_health_70,0 +2972 standard_health_70,1 +2973 standard_shield_70,0 +2974 standard_shield_70,1 +2975 standard_armour_70,0 +2976 standard_armour_70,1 +2977 standard_charge_70,0 +2978 standard_charge_70,1 +2979 magic_necro_on,44 +2980 magic_necro_on,45 +2981 magic_necro_on,46 +2982 magic_necro_on,47 +2983 magic_necro_on,48 +2984 magic_necro_on,49 +2985 magic_necro_off,44 +2986 magic_necro_off,45 +2987 magic_necro_off,46 +2988 magic_necro_off,47 +2989 magic_necro_off,48 +2990 magic_necro_off,49 +2991 2x_necro_spells_on,40 +2992 2x_necro_spells_on,41 +2993 2x_necro_spells_on,42 +2994 2x_necro_spells_on,43 +2995 2x_necro_spells_on,44 +2996 2x_necro_spells_on,45 +2997 2x_necro_spells_on,46 +2998 2x_necro_spells_on,47 +2999 2x_necro_spells_on,48 +3000 2x_necro_spells_on,49 +3001 2x_necro_spells_off,40 +3002 2x_necro_spells_off,41 +3003 2x_necro_spells_off,42 +3004 2x_necro_spells_off,43 +3005 2x_necro_spells_off,44 +3006 2x_necro_spells_off,45 +3007 2x_necro_spells_off,46 +3008 2x_necro_spells_off,47 +3009 2x_necro_spells_off,48 +3010 2x_necro_spells_off,49 +3011 graphic_3011 +3012 graphic_3012 +3013 graphic_3013 +3014 graphic_3014 +3015 orb_icon,8 +3016 orb_icon,9 +3017 orb_icon,10 +3018 orb_icon,11 +3019 orb_icon,12 +3020 orb_icon,13 +3021 orb_icon,14 +3022 orb_icon,15 +3023 blank +3024 icon_skull +3025 icon_crown +3026 icon_wizard +3027 icon_comedy +3028 icon_helmet +3029 icon_swords +3030 icon_tragedy +3031 icon_tools +3032 icon_tools2 +3033 icon_arrows +3034 icon_balance +3035 icon_bandit +3036 icon_bones +3037 icon_cabbage +3038 icon_cat +3039 icon_compass +3040 icon_fish +3041 icon_heart +3042 icon_holy +3043 icon_hunter +3044 icon_ring +3045 icon_robin +3046 icon_rose +3047 icon_shield +3048 icon_unholy +3049 icon_iron_standard +3050 icon_iron_ultimate +3051 chat_tab_button,0 +3052 chat_tab_button,1 +3053 chat_tab_button,2 +3054 chat_tab_button,3 +3055 chat_tab_button,4 +3056 chat_tab_button,5 +3057 report_button,0 +3058 report_button,1 +3059 chat_type_icon,0 +3060 chat_type_icon,1 +3061 chat_type_icon,2 +3062 clan_rank_icons,0 +3063 clan_rank_icons,1 +3064 clan_rank_icons,2 +3065 clan_rank_icons,3 +3066 clan_rank_icons,4 +3067 clan_rank_icons,5 +3068 clan_rank_icons,6 +3069 clan_rank_icons,7 +3070 clan_rank_icons,8 +3071 clan_rank_icons,9 +3072 clan_rank_icons,10 +3073 clan_rank_icons,11 +3074 clan_rank_icons,12 +3075 clan_rank_icons,13 +3076 clan_rank_icons,14 +3077 clan_rank_icons,15 +3078 clan_rank_icons,16 +3079 clan_rank_icons,17 +3080 clan_rank_icons,18 +3081 clan_rank_icons,19 +3082 clan_rank_icons,20 +3083 clan_rank_icons,21 +3084 clan_rank_icons,22 +3085 clan_rank_icons,23 +3086 clan_rank_icons,24 +3087 clan_rank_icons,25 +3088 clan_rank_icons,26 +3089 clan_rank_icons,27 +3090 clan_rank_icons,28 +3091 clan_rank_icons,29 +3092 clan_rank_icons,30 +3093 clan_rank_icons,31 +3094 clan_rank_icons,32 +3095 clan_rank_icons,33 +3096 clan_rank_icons,34 +3097 clan_rank_icons,35 +3098 clan_rank_icons,36 +3099 clan_rank_icons,37 +3100 clan_rank_icons,38 +3101 clan_rank_icons,39 +3102 clan_rank_icons,40 +3103 clan_rank_icons,41 +3104 clan_rank_icons,42 +3105 clan_rank_icons,43 +3106 clan_rank_icons,44 +3107 clan_rank_icons,45 +3108 clan_rank_icons,46 +3109 clan_rank_icons,47 +3110 clan_rank_icons,48 +3111 clan_rank_icons,49 +3112 clan_rank_icons,50 +3113 clan_rank_icons,51 +3114 clan_rank_icons,52 +3115 clan_rank_icons,53 +3116 clan_rank_icons,54 +3117 clan_rank_icons,55 +3118 clan_rank_icons,56 +3119 clan_rank_icons,57 +3120 clan_rank_icons,58 +3121 clan_rank_icons,59 +3122 clan_rank_icons,60 +3123 clan_rank_icons,61 +3124 clan_rank_icons,62 +3125 clan_rank_icons,63 +3126 clan_rank_icons,64 +3127 clan_rank_icons,65 +3128 clan_rank_icons,66 +3129 clan_rank_icons,67 +3130 clan_rank_icons,68 +3131 clan_rank_icons,69 +3132 clan_rank_icons,70 +3133 clan_rank_icons,71 +3134 clan_rank_icons,72 +3135 clan_rank_icons,73 +3136 clan_rank_icons,74 +3137 clan_rank_icons,75 +3138 clan_rank_icons,76 +3139 clan_rank_icons,77 +3140 clan_rank_icons,78 +3141 clan_rank_icons,79 +3142 clan_rank_icons,80 +3143 clan_rank_icons,81 +3144 clan_rank_icons,82 +3145 clan_rank_icons,83 +3146 clan_rank_icons,84 +3147 clan_rank_icons,85 +3148 clan_rank_icons,86 +3149 clan_rank_icons,87 +3150 clan_rank_icons,88 +3151 clan_rank_icons,89 +3152 clan_rank_icons,90 +3153 clan_rank_icons,91 +3154 clan_rank_icons,92 +3155 clan_rank_icons,93 +3156 clan_rank_icons,94 +3157 clan_rank_icons,95 +3158 clan_rank_icons,96 +3159 clan_rank_icons,97 +3160 clan_rank_icons,98 +3161 clan_rank_icons,99 +3162 clan_rank_icons,100 +3163 clan_rank_icons,101 +3164 clan_rank_icons,102 +3165 clan_rank_icons,103 +3166 clan_rank_icons,104 +3167 clan_rank_icons,105 +3168 clan_rank_icons,106 +3169 clan_rank_icons,107 +3170 clan_rank_icons,108 +3171 clan_rank_icons,109 +3172 clan_rank_icons,110 +3173 clan_rank_icons,111 +3174 clan_rank_icons,112 +3175 clan_rank_icons,113 +3176 clan_rank_icons,114 +3177 clan_rank_icons,115 +3178 clan_rank_icons,116 +3179 clan_rank_icons,117 +3180 clan_rank_icons,118 +3181 clan_rank_icons,119 +3182 clan_rank_icons,120 +3183 clan_rank_icons,121 +3184 clan_rank_icons,122 +3185 clan_rank_icons,123 +3186 clan_rank_icons,124 +3187 clan_rank_icons,125 +3188 clan_rank_icons,126 +3189 clan_rank_icons,127 +3190 clan_rank_icons,128 +3191 clan_rank_icons,129 +3192 clan_rank_icons,130 +3193 clan_rank_icons,131 +3194 clan_rank_icons,132 +3195 clan_rank_icons,133 +3196 clan_rank_icons,134 +3197 clan_rank_icons,135 +3198 clan_rank_icons,136 +3199 clan_rank_icons,137 +3200 clan_rank_icons,138 +3201 clan_rank_icons,139 +3202 clan_rank_icons,140 +3203 clan_rank_icons,141 +3204 clan_rank_icons,142 +3205 clan_rank_icons,143 +3206 clan_rank_icons,144 +3207 clan_rank_icons,145 +3208 clan_rank_icons,146 +3209 clan_rank_icons,147 +3210 clan_rank_icons,148 +3211 clan_rank_icons,149 +3212 clan_rank_icons,150 +3213 clan_rank_icons,151 +3214 clan_rank_icons,152 +3215 clan_rank_icons,153 +3216 clan_rank_icons,154 +3217 clan_rank_icons,155 +3218 clan_rank_icons,156 +3219 clan_rank_icons,157 +3220 clan_rank_icons,158 +3221 clan_rank_icons,159 +3222 clan_rank_icons,160 +3223 clan_rank_icons,161 +3224 clan_rank_icons,162 +3225 clan_rank_icons,163 +3226 clan_rank_icons,164 +3227 clan_rank_icons,165 +3228 clan_rank_icons,166 +3229 clan_rank_icons,167 +3230 clan_rank_icons,168 +3231 clan_rank_icons,169 +3232 clan_rank_icons,170 +3233 clan_rank_icons,171 +3234 clan_rank_icons,172 +3235 clan_rank_icons,173 +3236 clan_rank_icons,174 +3237 clan_rank_icons,175 +3238 clan_rank_icons,176 +3239 clan_rank_icons,177 +3240 clan_rank_icons,178 +3241 clan_rank_icons,179 +3242 clan_rank_icons,180 +3243 clan_rank_icons,181 +3244 clan_rank_icons,182 +3245 clan_rank_icons,183 +3246 clan_rank_icons,184 +3247 clan_rank_icons,185 +3248 clan_rank_icons,186 +3249 clan_rank_icons,187 +3250 clan_rank_icons,188 +3251 clan_rank_icons,189 +3252 clan_rank_icons,190 +3253 clan_rank_icons,191 +3254 clan_rank_icons,192 +3255 clan_rank_icons,193 +3256 clan_rank_icons,194 +3257 clan_rank_icons,195 +3258 clan_rank_icons,196 +3259 clan_rank_icons,197 +3260 clan_rank_icons,198 +3261 clan_rank_icons,199 +3262 clan_rank_icons,200 +3263 clan_rank_icons,201 +3264 clan_rank_icons,202 +3265 clan_rank_icons,203 +3266 clan_rank_icons,204 +3267 clan_rank_icons,205 +3268 clan_rank_icons,206 +3269 clan_rank_icons,207 +3270 clan_rank_icons,208 +3271 clan_rank_icons,209 +3272 clan_rank_icons,210 +3273 clan_rank_icons,211 +3274 clan_rank_icons,212 +3275 clan_rank_icons,213 +3276 clan_rank_icons,214 +3277 clan_rank_icons,215 +3278 clan_rank_icons,216 +3279 clan_rank_icons,217 +3280 clan_rank_icons,218 +3281 clan_rank_icons,219 +3282 clan_rank_icons,220 +3283 clan_rank_icons,221 +3284 clan_rank_icons,222 +3285 clan_rank_icons,223 +3286 clan_rank_icons,224 +3287 clan_rank_icons,225 +3288 clan_rank_icons,226 +3289 clan_rank_icons,227 +3290 clan_rank_icons,228 +3291 clan_rank_icons,229 +3292 clan_rank_icons,230 +3293 clan_rank_icons,231 +3294 clan_rank_icons,232 +3295 clan_rank_icons,233 +3296 clan_rank_icons,234 +3297 clan_rank_icons,235 +3298 clan_rank_icons,236 +3299 clan_rank_icons,237 +3300 clan_rank_icons,238 +3301 clan_rank_icons,239 +3302 clan_rank_icons,240 +3303 clan_rank_icons,241 +3304 clan_rank_icons,242 +3305 clan_rank_icons,243 +3306 clan_rank_icons,244 +3307 clan_rank_icons,245 +3308 clan_rank_icons,246 +3309 clan_rank_icons,247 +3310 clan_rank_icons,248 +3311 clan_rank_icons,249 +3312 clan_rank_icons,250 +3313 clan_rank_icons,251 +3314 clan_rank_icons,252 +3315 clan_rank_icons,253 +3316 clan_rank_icons,254 +3317 clan_rank_icons,255 +3318 clan_rank_icons,256 +3319 clan_rank_icons,257 +3320 clan_rank_icons,258 +3321 clan_rank_icons,259 +3322 clan_rank_icons,260 +3323 clan_rank_icons,261 +3324 clan_rank_icons,262 +3325 clan_rank_icons,263 +3326 clan_rank_icons,264 +3327 clan_rank_icons,265 +3328 clan_rank_icons,266 +3329 clan_rank_icons,267 +3330 clan_rank_icons,268 +3331 clan_rank_icons,269 +3332 clan_rank_icons,270 +3333 clan_rank_icons,271 +3334 clan_rank_icons,272 +3335 clan_rank_icons,273 +3336 clan_rank_icons,274 +3337 mapdots_interface,6 +3338 options_icons_small,7 +3339 options_icons_small,8 +3340 options_icons_small,9 +3341 options_icons_small,10 +3342 options_icons_small,11 +3343 options_icons_small,12 +3344 options_icons_small,13 +3345 options_icons_small,14 +3346 options_icons_small,15 +3347 options_icons_small,16 +3348 options_icons_small,17 +3349 options_icons_small,18 +3350 options_icons_small,19 +3351 magic_necro_off,51 +3352 magic_necro_off,52 +3353 magic_necro_off,53 +3354 magic_necro_off,54 +3355 magic_necro_off,55 +3356 magic_necro_off,56 +3357 magic_necro_off,57 +3358 magic_necro_off,58 +3359 magic_necro_off,59 +3360 graphic_3360 +3361 graphic_3361 +3362 graphic_3362 +3363 graphic_3363 +3364 leafytree_tiled02 +3365 leafytree02 +3366 god_wars_icons,0 +3367 god_wars_icons,1 +3368 god_wars_icons,2 +3369 god_wars_icons,3 +3370 mod_icons_interface,40 +3371 mod_icons_interface,41 +3372 mod_icons_interface,42 +3373 mod_icons_interface,43 +3374 mod_icons_interface,44 +3375 mod_icons_interface,45 +3376 mod_icons_interface,46 +3377 mod_icons_interface,47 +3378 mod_icons_interface,48 +3379 mod_icons_interface,49 +3380 pvpw_icons,5 +3381 pvpw_icons,6 +3382 graphic_3382 +3383 graphic_3383 +3384 graphic_3384 +3385 graphic_3385 +3386 account_icons,0 +3387 account_icons,1 +3388 account_icons,2 +3389 account_icons,3 +3390 account_icons,4 +3391 ca_progress_bar,0 +3392 ca_progress_bar,1 +3393 ca_tier_swords,0 +3394 ca_tier_swords,1 +3395 ca_tier_swords,2 +3396 ca_tier_swords,3 +3397 ca_tier_swords,4 +3398 ca_tier_swords,5 +3399 ca_tier_swords_small,0 +3400 ca_tier_swords_small,1 +3401 ca_tier_swords_small,2 +3402 ca_tier_swords_small,3 +3403 ca_tier_swords_small,4 +3404 ca_tier_swords_small,5 +3405 hitmark,18 +3406 pvpw_icons,7 +3407 deadman_sigil_icons,0 +3408 deadman_sigil_icons,1 +3409 deadman_sigil_icons,2 +3410 deadman_sigil_icons,3 +3411 deadman_sigil_icons,4 +3412 deadman_sigil_icons,5 +3413 deadman_sigil_icons,6 +3414 deadman_sigil_icons,7 +3415 deadman_sigil_icons,8 +3416 deadman_sigil_icons,9 +3417 deadman_sigil_icons,10 +3418 deadman_sigil_icons,11 +3419 deadman_sigil_icons,12 +3420 deadman_sigil_icons,13 +3421 deadman_sigil_icons,14 +3422 deadman_sigil_icons,15 +3423 deadman_sigil_icons,16 +3424 deadman_sigil_icons,17 +3425 deadman_sigil_icons,18 +3426 deadman_sigil_icons,19 +3427 deadman_sigil_icons,20 +3428 deadman_sigil_icons,21 +3429 deadman_sigil_icons,22 +3430 deadman_sigil_icons,23 +3431 deadman_sigil_icons,24 +3432 deadman_sigil_icons,25 +3433 deadman_sigil_icons,26 +3434 deadman_sigil_icons,27 +3435 deadman_sigil_icons,28 +3436 deadman_sigil_icons,29 +3437 deadman_sigil_icons,30 +3438 deadman_sigil_icons,31 +3439 deadman_sigil_icons,32 +3440 deadman_sigil_icons,33 +3441 deadman_sigil_icons,34 +3442 deadman_sigil_icons,35 +3443 deadman_sigil_icons,36 +3444 deadman_sigil_icons,37 +3445 deadman_sigil_icons,38 +3446 deadman_sigil_icons,39 +3447 deadman_sigil_icons,40 +3448 deadman_sigil_icons,41 +3449 deadman_sigil_icons,42 +3450 deadman_sigil_icons,43 +3451 deadman_sigil_icons,44 +3452 deadman_sigil_icons,45 +3453 deadman_sigil_icons,46 +3454 deadman_sigil_icons,47 +3455 deadman_sigil_icons_tli,0 +3456 deadman_sigil_icons_tli,1 +3457 deadman_sigil_icons_tli,2 +3458 deadman_sigil_icons_tli,3 +3459 deadman_sigil_icons_tli,4 +3460 deadman_sigil_icons_tli,5 +3461 deadman_sigil_icons_tli,6 +3462 deadman_sigil_icons_tli,7 +3463 deadman_sigil_icons_tli,8 +3464 deadman_sigil_icons_tli,9 +3465 deadman_sigil_icons_tli,10 +3466 deadman_sigil_icons_tli,11 +3467 deadman_sigil_icons_tli,12 +3468 deadman_sigil_icons_tli,13 +3469 deadman_sigil_icons_tli,14 +3470 deadman_sigil_icons_tli,15 +3471 deadman_sigil_icons_tli,16 +3472 deadman_sigil_icons_tli,17 +3473 deadman_sigil_icons_tli,18 +3474 deadman_sigil_icons_tli,19 +3475 deadman_sigil_icons_tli,20 +3476 deadman_sigil_icons_tli,21 +3477 deadman_sigil_icons_tli,22 +3478 deadman_sigil_icons_tli,23 +3479 deadman_sigil_icons_tli,24 +3480 deadman_sigil_icons_tli,25 +3481 deadman_sigil_icons_tli,26 +3482 deadman_sigil_icons_tli,27 +3483 deadman_sigil_icons_tli,28 +3484 deadman_sigil_icons_tli,29 +3485 deadman_sigil_icons_tli,30 +3486 deadman_sigil_icons_tli,31 +3487 deadman_sigil_icons_tli,32 +3488 deadman_sigil_icons_tli,33 +3489 deadman_sigil_icons_tli,34 +3490 deadman_sigil_icons_tli,35 +3491 deadman_sigil_icons_tli,36 +3492 deadman_sigil_icons_tli,37 +3493 deadman_sigil_icons_tli,38 +3494 deadman_sigil_icons_tli,39 +3495 deadman_sigil_icons_tli,40 +3496 deadman_sigil_icons_tli,41 +3497 deadman_sigil_icons_tli,42 +3498 deadman_sigil_icons_tli,43 +3499 deadman_sigil_icons_tli,44 +3500 deadman_sigil_icons_tli,45 +3501 deadman_sigil_icons_tli,46 +3502 deadman_sigil_icons_tli,47 +3503 deadman_sigil_skulls,0 +3504 deadman_sigil_skulls,1 +3505 deadman_sigil_skulls,2 +3506 deadman_sigil_skulls,3 +3507 deadman_sigil_skulls,4 +3508 deadman_sigil_skulls,5 +3509 deadman_sigil_skulls,6 +3510 deadman_sigil_skulls,7 +3511 deadman_sigil_skulls,8 +3512 hitmark,19 +3513 resize_map_clickmask_bond +3514 storeorb,0 +3515 storeorb,1 +3516 storeorb,2 +3517 ring_34,1 +3518 ring_34,2 +3519 hitmark,20 +3520 hitmark,21 +3521 hitmark_blocked +3522 heart_icon +3523 ice +3524 rounded_tabs,0 +3525 rounded_tabs,1 +3526 rounded_tabs,2 +3527 rounded_tabs,3 +3528 rounded_tabs,4 +3529 rounded_tabs,5 +3530 rounded_tabs,6 +3531 rounded_tabs,7 +3532 rounded_tabs,8 +3533 rounded_tabs,9 +3534 rounded_tabs,10 +3535 rounded_tabs,11 +3536 rounded_tabs,12 +3537 rounded_tabs,13 +3538 rounded_tabs,14 +3539 rounded_tabs,15 +3540 rounded_tabs,16 +3541 rounded_tabs,17 +3542 rounded_tabs,18 +3543 rounded_tabs,19 +3544 rounded_tabs,20 +3545 rounded_tabs,21 +3546 rounded_tabs,22 +3547 rounded_tabs,23 +3548 iron_icons,0 +3549 iron_icons,1 +3550 iron_icons,2 +3551 iron_icons,3 +3552 iron_icons,4 +3553 side_icons,32 +3554 side_icons,33 +3555 side_icons,34 +3556 side_icons,35 +3557 side_icons,36 +3558 side_icons,37 +3559 side_icons,38 +3560 side_icons,39 +3561 sideicons_interface,32 +3562 sideicons_interface,33 +3563 sideicons_interface,34 +3564 sideicons_interface,35 +3565 sideicons_interface,36 +3566 sideicons_interface,37 +3567 sideicons_interface,38 +3568 sideicons_interface,39 +3569 hitmark,22 +3570 hitmark,23 +3571 hitmark,24 +3572 hitmark,25 +3573 hitmark,26 +3574 orb_filler,15 +3575 orb_filler,16 +3576 orb_filler,17 +3577 popout_icons,0 +3578 popout_icons,1 +3579 popout_icons,2 +3580 graphic_3580 +3581 speedrun_trophies,0 +3582 speedrun_trophies,1 +3583 membership_banner_skill +3584 membership_banner_area +3585 membership_banner_boss +3586 membership_banner_diary +3587 membership_banner_minigame +3588 membership_banner_quest +3589 membership_banner_transport +3590 membership_banner_worn +3591 membership_banner_dragonslayer +3592 graphic_3592 +3593 giants_foundry_moulds_big,1 +3594 giants_foundry_moulds_big,2 +3595 group_icon,0 +3596 group_icon,1 +3597 graphic_3597 +3598 graphic_3598 +3599 graphic_3599 +3600 graphic_3600 +3601 roof_snow +3602 roof_snow02 +3603 graphic_3603 +3604 emotes,52 +3605 god_wars_icons,4 +3606 emotes,53 +3607 emotes,54 +3608 emotes,55 +3609 achievement_diary_icons,10 +3610 achievement_diary_icons,11 +3611 achievement_diary_icons,12 +3612 achievement_diary_icons,13 +3613 achievement_diary_icons,14 +3614 settings_tabs,24 +3615 settings_tabs,25 +3616 settings_tabs,26 +3617 settings_tabs,27 +3618 settings_tabs,28 +3619 settings_tabs,29 +3620 settings_tabs,30 +3621 settings_tabs,31 +3622 button_polished,0 +3623 button_polished,1 +3624 button_polished,2 +3625 button_polished,3 +3626 button_polished,4 +3627 button_polished,5 +3628 button_polished,6 +3629 button_polished,7 +3630 button_polished,8 +3631 button_polished,9 +3632 button_polished,10 +3633 button_polished,11 +3634 button_polished,12 +3635 button_polished,13 +3636 button_polished,14 +3637 button_polished,15 +3638 button_polished,16 +3639 button_polished,17 +3640 button_polished,18 +3641 button_polished,19 +3642 button_polished,20 +3643 button_polished,21 +3644 button_polished,22 +3645 button_polished,23 +3646 button_polished,24 +3647 button_polished,25 +3648 button_polished,26 +3649 button_polished,27 +3650 button_polished,28 +3651 button_polished,29 +3652 button_polished,30 +3653 button_polished,31 +3654 button_polished,32 +3655 button_polished,33 +3656 button_polished,34 +3657 button_polished,35 +3658 button_polished,36 +3659 button_polished,37 +3660 button_polished,38 +3661 button_polished,39 +3662 button_polished,40 +3663 button_polished,41 +3664 button_polished,42 +3665 button_polished,43 +3666 button_polished,44 +3667 button_polished,45 +3668 button_polished,46 +3669 button_polished,47 +3670 button_polished,48 +3671 button_polished,49 +3672 button_polished,50 +3673 button_polished,51 +3674 button_polished,52 +3675 button_polished,53 +3676 button_polished,54 +3677 button_polished,55 +3678 button_polished,56 +3679 button_polished,57 +3680 button_polished,58 +3681 button_polished,59 +3682 button_polished,60 +3683 button_polished,61 +3684 button_polished,62 +3685 button_polished,63 +3686 button_polished,64 +3687 button_polished,65 +3688 button_polished,66 +3689 button_polished,67 +3690 button_polished,68 +3691 button_polished,69 +3692 button_polished,70 +3693 button_polished,71 +3694 button_polished,72 +3695 button_polished,73 +3696 button_polished,74 +3697 button_polished,75 +3698 button_polished,76 +3699 button_polished,77 +3700 button_polished,78 +3701 button_polished,79 +3702 button_polished,80 +3703 button_polished,81 +3704 button_polished,82 +3705 button_polished,83 +3706 button_polished,84 +3707 button_polished,85 +3708 button_polished,86 +3709 button_polished,87 +3710 button_polished,88 +3711 button_polished,89 +3712 button_polished,90 +3713 button_polished,91 +3714 button_polished,92 +3715 button_polished,93 +3716 button_polished,94 +3717 button_polished,95 +3718 button_polished,96 +3719 button_polished,97 +3720 button_polished,98 +3721 button_polished,99 +3722 button_polished,100 +3723 button_polished,101 +3724 button_polished,102 +3725 button_polished,103 +3726 button_polished,104 +3727 button_polished,105 +3728 button_polished,106 +3729 button_polished,107 +3730 button_polished,108 +3731 button_polished,109 +3732 button_polished,110 +3733 button_polished,111 +3734 button_polished,112 +3735 button_polished,113 +3736 button_polished,114 +3737 button_polished,115 +3738 button_polished,116 +3739 league_task_tiers,5 +3740 league_task_tiers,6 +3741 league_task_tiers,7 +3742 league_task_tiers,8 +3743 league_task_tiers,9 +3744 league_tiny_relic,2 +3745 graphic_3745 +3746 graphic_3746 +3747 graphic_3747 +3748 graphic_3748 +3749 graphic_3749 +3750 graphic_3750 +3751 graphic_3751 +3752 graphic_3752 +3753 graphic_3753 +3754 graphic_3754 +3755 graphic_3755 +3756 graphic_3756 +3757 graphic_3757 +3758 graphic_3758 +3759 league_3_fragment_back,0 +3760 league_3_fragment_back,1 +3761 league_3_fragment_back,2 +3762 league_3_fragment_back,3 +3763 league_3_fragment_back,4 +3764 league_3_fragment_back,5 +3765 league_3_fragment_back,6 +3766 league_3_fragment_back,7 +3767 league_3_fragment_back,8 +3768 league_3_fragment_back,9 +3769 league_3_fragment_back,10 +3770 league_3_fragment_back,11 +3771 league_3_fragment_back,12 +3772 league_3_fragment_back,13 +3773 league_3_fragment_back,14 +3774 league_3_fragment_back,15 +3775 league_3_fragment_back,16 +3776 league_3_fragment_back,17 +3777 league_3_fragment_back,18 +3778 league_3_fragment_back,19 +3779 league_3_fragment_base,0 +3780 league_3_fragment_base,1 +3781 league_3_fragment_base,2 +3782 league_3_fragment_base,3 +3783 league_3_fragment_base,4 +3784 league_3_fragment_base,5 +3785 league_3_fragment_base,6 +3786 league_3_fragment_base,7 +3787 league_3_fragment_base,8 +3788 league_3_fragment_base,9 +3789 league_3_fragment_base,10 +3790 league_3_fragment_base,11 +3791 league_3_fragment_base,12 +3792 league_3_fragment_base,13 +3793 league_3_fragment_base,14 +3794 league_3_fragment_base,15 +3795 league_3_fragment_base,16 +3796 league_3_fragment_base,17 +3797 league_3_fragment_base,18 +3798 league_3_fragment_base,19 +3799 league_3_fragment_base,20 +3800 league_3_fragment_base,21 +3801 league_3_fragment_base,22 +3802 league_3_fragment_base,23 +3803 league_3_fragment_base,24 +3804 league_3_fragment_base,25 +3805 league_3_fragment_base,26 +3806 league_3_fragment_base,27 +3807 league_3_fragment_base,28 +3808 league_3_fragment_base,29 +3809 league_3_fragment_base,30 +3810 league_3_fragment_base,31 +3811 league_3_fragment_base,32 +3812 league_3_fragment_base,33 +3813 league_3_fragment_base,34 +3814 league_3_fragment_base,35 +3815 league_3_fragment_base,36 +3816 league_3_fragment_base,37 +3817 league_3_fragment_base,38 +3818 league_3_fragment_base,39 +3819 league_3_fragment_base,40 +3820 league_3_fragment_base,41 +3821 league_3_fragment_base,42 +3822 league_3_fragment_base,43 +3823 league_3_fragment_base,44 +3824 league_3_fragment_base,45 +3825 league_3_fragment_base,46 +3826 league_3_fragment_base,47 +3827 league_3_fragment_base,48 +3828 league_3_fragment_base,49 +3829 league_3_fragment_base,50 +3830 league_3_fragment_base,51 +3831 league_3_fragment_base,52 +3832 league_3_fragment_base,53 +3833 league_3_fragment_base,54 +3834 league_3_fragment_base,55 +3835 league_3_fragment_base,56 +3836 league_3_fragment_base,57 +3837 league_3_fragment_base,58 +3838 league_3_fragment_base,59 +3839 league_3_fragment_base,60 +3840 league_3_fragment_base,61 +3841 league_3_fragment_base,62 +3842 league_3_fragment_base,63 +3843 league_3_fragment_base,64 +3844 league_3_fragment_base,65 +3845 league_3_fragment_base,66 +3846 league_3_fragment_base,67 +3847 league_3_fragment_base,68 +3848 league_3_fragment_base,69 +3849 league_3_fragment_base,70 +3850 league_3_fragment_base,71 +3851 league_3_fragment_base,72 +3852 league_3_fragment_base,73 +3853 league_3_fragment_base,74 +3854 league_3_fragment_base,75 +3855 league_3_fragment_base,76 +3856 league_3_fragment_base,77 +3857 league_3_fragment_base,78 +3858 league_3_fragment_base,79 +3859 league_3_fragment_glow,0 +3860 league_3_fragment_glow,1 +3861 league_3_fragment_glow,2 +3862 league_3_fragment_glow,3 +3863 league_3_fragment_glow,4 +3864 league_3_fragment_glow,5 +3865 league_3_fragment_glow,6 +3866 league_3_fragment_glow,7 +3867 league_3_fragment_glow,8 +3868 league_3_fragment_glow,9 +3869 league_3_fragment_glow,10 +3870 league_3_fragment_glow,11 +3871 league_3_fragment_glow,12 +3872 league_3_fragment_glow,13 +3873 league_3_fragment_glow,14 +3874 league_3_fragment_glow,15 +3875 league_3_fragment_glow,16 +3876 league_3_fragment_glow,17 +3877 league_3_fragment_glow,18 +3878 league_3_fragment_glow,19 +3879 league_3_icons_light,0 +3880 league_3_icons_light,1 +3881 league_3_icons_light,2 +3882 league_3_icons_light,3 +3883 league_3_icons_light,4 +3884 league_3_icons_light,5 +3885 league_3_icons_light,6 +3886 league_3_icons_light,7 +3887 league_3_icons_light,8 +3888 league_3_icons_light,9 +3889 league_3_icons_light,10 +3890 league_3_icons_light,11 +3891 league_3_icons_light,12 +3892 league_3_icons_light,13 +3893 league_3_icons_light,14 +3894 league_3_icons_light,15 +3895 league_3_icons_light,16 +3896 league_3_icons_light,17 +3897 league_3_icons_light,18 +3898 league_3_icons_light,19 +3899 league_3_icons_light,20 +3900 league_3_icons_light,21 +3901 league_3_icons_light,22 +3902 league_3_icons_light,23 +3903 league_3_icons_light,24 +3904 league_3_icons_light,25 +3905 league_3_icons_light,26 +3906 league_3_icons_light,27 +3907 league_3_icons_light,28 +3908 league_3_icons_light,29 +3909 league_3_icons_light,30 +3910 league_3_icons_light,31 +3911 league_3_icons_light,32 +3912 league_3_icons_light,33 +3913 league_3_icons_light,34 +3914 league_3_icons_light,35 +3915 league_3_icons_light,36 +3916 league_3_icons_light,37 +3917 league_3_icons_light,38 +3918 league_3_icons_light,39 +3919 league_3_icons_light,40 +3920 league_3_icons_light,41 +3921 league_3_icons_light,42 +3922 league_3_icons_light,43 +3923 league_3_icons_light,44 +3924 league_3_icons_light,45 +3925 league_3_icons_light,46 +3926 league_3_icons_light,47 +3927 league_3_icons_light,48 +3928 league_3_icons_light,49 +3929 league_3_icons_light,50 +3930 league_3_icons_light,51 +3931 league_3_icons_light,52 +3932 league_3_icons_light,53 +3933 league_3_icons_light,54 +3934 league_3_icons_light,55 +3935 league_3_icons_light,56 +3936 league_3_icons_light,57 +3937 league_3_icons_light,58 +3938 league_3_icons_light,59 +3939 league_3_icons_light,60 +3940 league_3_icons_light,61 +3941 league_3_icons_light,62 +3942 league_3_icons_light,63 +3943 league_3_icons_light,64 +3944 league_3_icons_light,65 +3945 league_3_icons_light,66 +3946 league_3_icons_light,67 +3947 league_3_icons_light,68 +3948 league_3_icons_light,69 +3949 league_3_icons_light,70 +3950 league_3_icons_light,71 +3951 league_3_icons_light,72 +3952 league_3_icons_light,73 +3953 league_3_icons_light,74 +3954 league_3_icons_light,75 +3955 league_3_icons_light,76 +3956 league_3_icons_light,77 +3957 league_3_icons_light,78 +3958 league_3_icons_light,79 +3959 league_3_icons_light,80 +3960 league_3_icons_light,81 +3961 league_3_icons_light,82 +3962 league_3_icons_light,83 +3963 league_3_icons_light,84 +3964 league_3_icons_light,85 +3965 league_3_icons_light,86 +3966 league_3_icons_light,87 +3967 league_3_icons_light,88 +3968 league_3_icons_light,89 +3969 league_3_icons_light,90 +3970 league_3_icons_light,91 +3971 league_3_icons_light,92 +3972 league_3_icons_light,93 +3973 league_3_icons_light,94 +3974 league_3_icons_light,95 +3975 league_3_icons_light,96 +3976 league_3_icons_light,97 +3977 league_3_icons_light,98 +3978 league_3_icons_light,99 +3979 league_3_icons_light,100 +3980 league_3_icons_light,101 +3981 league_3_icons_light,102 +3982 league_3_icons_light,103 +3983 league_3_icons_light,104 +3984 league_3_icons_light,105 +3985 league_3_icons_light,106 +3986 league_3_icons_light,107 +3987 league_3_icons_light,108 +3988 league_3_icons_light,109 +3989 league_3_icons_light,110 +3990 league_3_icons_light,111 +3991 league_3_icons_light,112 +3992 league_3_icons_light,113 +3993 league_3_icons_light,114 +3994 league_3_icons_light,115 +3995 league_3_icons_light,116 +3996 league_3_icons_light,117 +3997 league_3_icons_light,118 +3998 league_3_icons_light,119 +3999 league_3_icons_light,120 +4000 league_3_icons_light,121 +4001 league_3_icons_light,122 +4002 league_3_icons_light,123 +4003 league_3_icons_light,124 +4004 league_3_icons_light,125 +4005 league_3_icons_light,126 +4006 league_3_icons_light,127 +4007 league_3_icons_light,128 +4008 league_3_icons_light,129 +4009 league_3_icons_light,130 +4010 league_3_icons_light,131 +4011 league_3_icons_light,132 +4012 league_3_icons_light,133 +4013 league_3_icons_light,134 +4014 league_3_icons_light,135 +4015 league_3_icons_light,136 +4016 league_3_icons_light,137 +4017 league_3_icons_light,138 +4018 league_3_icons_light,139 +4019 league_3_icons_light,140 +4020 league_3_icons_light,141 +4021 league_3_icons_light,142 +4022 league_3_icons_light,143 +4023 league_3_icons_light,144 +4024 league_3_icons_light,145 +4025 league_3_icons_light,146 +4026 league_3_icons_light,147 +4027 league_3_icons_light,148 +4028 league_3_icons_light,149 +4029 league_3_icons_light,150 +4030 league_3_icons_light,151 +4031 league_3_icons_light,152 +4032 league_3_icons_light,153 +4033 league_3_icons_light,154 +4034 league_3_icons_light,155 +4035 league_3_icons_light,156 +4036 league_3_icons_light,157 +4037 league_3_icons_light,158 +4038 league_3_icons_light,159 +4039 graphic_4039 +4040 graphic_4040 +4041 graphic_4041 +4042 graphic_4042 +4043 graphic_4043 +4044 graphic_4044 +4045 league_3_boss_icons,0 +4046 league_3_boss_icons,1 +4047 league_3_boss_icons,2 +4048 league_3_boss_icons,3 +4049 league_3_boss_icons,4 +4050 league_3_boss_icons,5 +4051 league_3_boss_icons,6 +4052 league_3_boss_icons,7 +4053 league_3_boss_icons,8 +4054 league_3_boss_icons,9 +4055 league_3_boss_icons,10 +4056 league_3_boss_icons,11 +4057 league_3_boss_icons,12 +4058 league_3_boss_icons,13 +4059 league_3_boss_icons,14 +4060 league_3_boss_icons,15 +4061 league_3_boss_icons,16 +4062 league_3_boss_icons,17 +4063 league_3_boss_icons,18 +4064 league_3_boss_icons,19 +4065 league_3_boss_icons,20 +4066 league_3_boss_icons,21 +4067 league_3_boss_icons,22 +4068 league_3_boss_icons,23 +4069 league_3_boss_icons,24 +4070 league_3_boss_icons,25 +4071 league_3_boss_icons,26 +4072 league_3_boss_icons,27 +4073 league_3_boss_icons,28 +4074 league_3_boss_icons,29 +4075 league_3_boss_icons,30 +4076 league_3_boss_icons,31 +4077 league_3_boss_icons,32 +4078 league_3_boss_icons,33 +4079 league_3_boss_icons,34 +4080 league_3_boss_icons,35 +4081 league_3_boss_icons,36 +4082 league_3_boss_icons,37 +4083 league_3_boss_icons,38 +4084 league_3_boss_icons,39 +4085 league_3_button,0 +4086 league_3_button,1 +4087 league_3_button,2 +4088 league_3_button,3 +4089 league_3_button,4 +4090 league_3_button,5 +4091 league_3_button,6 +4092 league_3_button,7 +4093 league_3_button,8 +4094 league_3_button,9 +4095 league_3_button,10 +4096 league_3_button,11 +4097 league_3_button,12 +4098 league_3_button,13 +4099 league_3_button,14 +4100 league_3_button,15 +4101 league_3_button,16 +4102 league_3_button,17 +4103 league_3_button,18 +4104 league_3_button,19 +4105 league_3_button,20 +4106 league_3_button,21 +4107 league_3_button,22 +4108 league_3_button,23 +4109 league_3_progress_bar,0 +4110 league_3_progress_bar,1 +4111 graphic_4111 +4112 graphic_4112 +4113 league_3_relics,0 +4114 league_3_relics,1 +4115 league_3_relics,2 +4116 league_3_relics,3 +4117 league_3_relics,4 +4118 league_3_relics,5 +4119 league_3_relics,6 +4120 league_3_relics,7 +4121 league_3_relics,8 +4122 league_3_relics,9 +4123 league_3_relics,10 +4124 league_3_relics,11 +4125 league_3_relics,12 +4126 league_3_relics,13 +4127 league_3_relics,14 +4128 league_3_relics,15 +4129 league_3_relics,16 +4130 league_3_relics,17 +4131 league_3_relics,18 +4132 league_3_relics,19 +4133 league_3_relics,20 +4134 league_3_relics,21 +4135 league_3_relics,22 +4136 league_3_relics,23 +4137 league_3_relics,24 +4138 league_3_relics,25 +4139 league_3_relics,26 +4140 league_3_relics,27 +4141 league_3_relics,28 +4142 league_3_relics,29 +4143 league_3_relics,30 +4144 league_3_relics,31 +4145 league_3_relics,32 +4146 league_3_relics,33 +4147 league_3_relics,34 +4148 league_3_relics,35 +4149 league_3_relics,36 +4150 league_3_relics,37 +4151 league_3_relics,38 +4152 league_3_relics,39 +4153 league_3_relics,40 +4154 league_3_relics,41 +4155 league_3_relics,42 +4156 league_3_relics,43 +4157 league_3_relics,44 +4158 league_3_relics,45 +4159 league_3_relics,46 +4160 league_3_relics,47 +4161 league_3_relics_tli,0 +4162 league_3_relics_tli,1 +4163 league_3_relics_tli,2 +4164 league_3_relics_tli,3 +4165 league_3_relics_tli,4 +4166 league_3_relics_tli,5 +4167 league_3_relics_tli,6 +4168 league_3_relics_tli,7 +4169 league_3_relics_tli,8 +4170 league_3_relics_tli,9 +4171 league_3_relics_tli,10 +4172 league_3_relics_tli,11 +4173 league_3_relics_tli,12 +4174 league_3_relics_tli,13 +4175 league_3_relics_tli,14 +4176 league_3_relics_tli,15 +4177 league_3_relics_tli,16 +4178 league_3_relics_tli,17 +4179 league_3_relics_large,0 +4180 league_3_relics_large,1 +4181 league_3_relics_large,2 +4182 league_3_relics_large,3 +4183 league_3_relics_large,4 +4184 league_3_relics_large,5 +4185 league_3_relics_large,6 +4186 league_3_relics_large,7 +4187 league_3_relics_large,8 +4188 league_3_relics_large,9 +4189 league_3_relics_large,10 +4190 league_3_relics_large,11 +4191 league_3_relics_large,12 +4192 league_3_relics_large,13 +4193 league_3_relics_large,14 +4194 league_3_relics_large,15 +4195 league_3_relics_large,16 +4196 league_3_relics_large,17 +4197 league_3_relics_large,18 +4198 league_3_relics_large,19 +4199 league_3_relics_large,20 +4200 league_3_relics_large,21 +4201 league_3_relics_large,22 +4202 league_3_relics_large,23 +4203 league_3_relics_large,24 +4204 league_3_relics_large,25 +4205 league_3_relics_large,26 +4206 league_3_relics_large,27 +4207 league_3_relics_large,28 +4208 league_3_relics_large,29 +4209 league_3_relics_large,30 +4210 league_3_relics_large,31 +4211 league_3_relics_large,32 +4212 league_3_relics_large,33 +4213 league_3_relics_large,34 +4214 league_3_relics_large,35 +4215 league_3_relics_large,36 +4216 league_3_relics_large,37 +4217 league_3_relics_large,38 +4218 league_3_relics_large,39 +4219 league_3_relics_large,40 +4220 league_3_relics_large,41 +4221 league_3_relics_large,42 +4222 league_3_relics_large,43 +4223 league_3_relics_large,44 +4224 league_3_relics_large,45 +4225 league_3_relics_large,46 +4226 league_3_relics_large,47 +4227 league_3_tutorial,0 +4228 league_3_tutorial,1 +4229 league_3_tutorial,2 +4230 league_3_tutorial,3 +4231 league_3_tutorial,4 +4232 league_3_tutorial,5 +4233 graphic_4233 +4234 graphic_4234 +4235 graphic_4235 +4236 graphic_4236 +4237 graphic_4237 +4238 graphic_4238 +4239 graphic_4239 +4240 graphic_4240 +4241 graphic_4241 +4242 graphic_4242 +4243 graphic_4243 +4244 graphic_4244 +4245 graphic_4245 +4246 graphic_4246 +4247 graphic_4247 +4248 graphic_4248 +4249 graphic_4249 +4250 graphic_4250 +4251 graphic_4251 +4252 graphic_4252 +4253 graphic_4253 +4254 graphic_4254 +4255 graphic_4255 +4256 graphic_4256 +4257 graphic_4257 +4258 graphic_4258 +4259 graphic_4259 +4260 graphic_4260 +4261 graphic_4261 +4262 graphic_4262 +4263 graphic_4263 +4264 graphic_4264 +4265 graphic_4265 +4266 graphic_4266 +4267 graphic_4267 +4268 graphic_4268 +4269 graphic_4269 +4270 graphic_4270 +4271 graphic_4271 +4272 graphic_4272 +4273 graphic_4273 +4274 graphic_4274 +4275 graphic_4275 +4276 graphic_4276 +4277 graphic_4277 +4278 graphic_4278 +4279 graphic_4279 +4280 graphic_4280 +4281 graphic_4281 +4282 graphic_4282 +4283 graphic_4283 +4284 graphic_4284 +4285 graphic_4285 +4286 graphic_4286 +4287 graphic_4287 +4288 graphic_4288 +4289 graphic_4289 +4290 graphic_4290 +4291 graphic_4291 +4292 graphic_4292 +4293 graphic_4293 +4294 graphic_4294 +4295 graphic_4295 +4296 graphic_4296 +4297 graphic_4297 +4298 graphic_4298 +4299 graphic_4299 +4300 graphic_4300 +4301 graphic_4301 +4302 graphic_4302 +4303 graphic_4303 +4304 graphic_4304 +4305 graphic_4305 +4306 graphic_4306 +4307 graphic_4307 +4308 graphic_4308 +4309 graphic_4309 +4310 graphic_4310 +4311 graphic_4311 +4312 graphic_4312 +4313 graphic_4313 +4314 graphic_4314 +4315 graphic_4315 +4316 graphic_4316 +4317 graphic_4317 +4318 graphic_4318 +4319 graphic_4319 +4320 graphic_4320 +4321 graphic_4321 +4322 graphic_4322 +4323 graphic_4323 +4324 graphic_4324 +4325 graphic_4325 +4326 graphic_4326 +4327 graphic_4327 +4328 graphic_4328 +4329 graphic_4329 +4330 graphic_4330 +4331 graphic_4331 +4332 graphic_4332 +4333 graphic_4333 +4334 graphic_4334 +4335 graphic_4335 +4336 graphic_4336 +4337 graphic_4337 +4338 graphic_4338 +4339 graphic_4339 +4340 speedrun_trophies,2 +4341 speedrun_trophies,3 +4342 arrows_curved,0 +4343 arrows_curved,1 +4344 arrows_curved,2 +4345 arrows_curved,3 +4346 lotg_buttons,0 +4347 lotg_buttons,1 +4348 lotg_buttons,2 +4349 lotg_buttons,3 +4350 worldswitcher_stars,6 +4351 graphic_4351 +4352 giants_foundry_moulds_big,3 +4353 graphic_4353 +4354 graphic_4354 +4355 graphic_4355 +4356 graphic_4356 +4357 graphic_4357 +4358 graphic_4358 +4359 graphic_4359 +4360 graphic_4360 +4361 graphic_4361 +4362 graphic_4362 +4363 graphic_4363 +4364 graphic_4364 +4365 graphic_4365 +4366 graphic_4366 +4367 graphic_4367 +4368 graphic_4368 +4369 graphic_4369 +4370 graphic_4370 +4371 graphic_4371 +4372 graphic_4372 +4373 graphic_4373 +4374 graphic_4374 +4375 graphic_4375 +4376 graphic_4376 +4377 graphic_4377 +4378 giants_foundry_moulds_big,4 +4379 giants_foundry_moulds_big,5 +4380 giants_foundry_moulds_big,6 +4381 giants_foundry_moulds_big,7 +4382 giants_foundry_moulds_big,8 +4383 giants_foundry_moulds_big,9 +4384 giants_foundry_moulds_big,10 +4385 giants_foundry_moulds_big,11 +4386 giants_foundry_moulds_big,12 +4387 giants_foundry_moulds_big,13 +4388 giants_foundry_moulds_big,14 +4389 giants_foundry_moulds_big,15 +4390 giants_foundry_moulds_big,16 +4391 giants_foundry_moulds_big,17 +4392 giants_foundry_moulds_big,18 +4393 giants_foundry_moulds_big,19 +4394 giants_foundry_moulds_big,20 +4395 giants_foundry_moulds_big,21 +4396 giants_foundry_moulds_big,22 +4397 giants_foundry_moulds_big,23 +4398 giants_foundry_moulds_big,24 +4399 giants_foundry_moulds_big,25 +4400 giants_foundry_moulds_big,26 +4401 giants_foundry_moulds_big,27 +4402 giants_foundry_moulds_big,28 +4403 giants_foundry_moulds_big,29 +4404 giants_foundry_moulds_big,30 +4405 giants_foundry_moulds_big,31 +4406 giants_foundry_moulds_big,32 +4407 giants_foundry_moulds_big,33 +4408 giants_foundry_moulds_big,34 +4409 giants_foundry_moulds_small,0 +4410 giants_foundry_moulds_small,1 +4411 giants_foundry_moulds_small,2 +4412 giants_foundry_moulds_small,3 +4413 giants_foundry_moulds_small,4 +4414 giants_foundry_moulds_small,5 +4415 giants_foundry_moulds_small,6 +4416 giants_foundry_moulds_small,7 +4417 giants_foundry_moulds_small,8 +4418 giants_foundry_moulds_small,9 +4419 giants_foundry_moulds_small,10 +4420 giants_foundry_moulds_small,11 +4421 giants_foundry_moulds_small,12 +4422 giants_foundry_moulds_small,13 +4423 giants_foundry_moulds_small,14 +4424 giants_foundry_moulds_small,15 +4425 giants_foundry_moulds_small,16 +4426 giants_foundry_moulds_small,17 +4427 giants_foundry_moulds_small,18 +4428 giants_foundry_moulds_small,19 +4429 giants_foundry_moulds_small,20 +4430 giants_foundry_moulds_small,21 +4431 giants_foundry_moulds_small,22 +4432 giants_foundry_moulds_small,23 +4433 giants_foundry_moulds_small,24 +4434 giants_foundry_moulds_small,25 +4435 giants_foundry_moulds_small,26 +4436 giants_foundry_moulds_small,27 +4437 giants_foundry_moulds_small,28 +4438 giants_foundry_moulds_small,29 +4439 giants_foundry_moulds_small,30 +4440 giants_foundry_moulds_small,31 +4441 giants_foundry_moulds_small,32 +4442 giants_foundry_tool_icon,0 +4443 giants_foundry_tool_icon,1 +4444 giants_foundry_tool_icon,2 +4445 giants_foundry_tool_icon,3 +4446 giants_foundry_tool_icon,4 +4447 giants_foundry_tool_icon,5 +4448 giants_foundry_tool_icon,6 +4449 giants_foundry_tool_icon,7 +4450 giants_foundry_hud_bar,0 +4451 giants_foundry_hud_bar,1 +4452 giants_foundry_hud_bar,2 +4453 giants_foundry_hud_bar,3 +4454 giants_foundry_hud_bar,4 +4455 giants_foundry_hud_bar,5 +4456 giants_foundry_hud_bar,6 +4457 giants_foundry_hud_bar,7 +4458 giants_foundry_hud_bar,8 +4459 giants_foundry_hud_bar,9 +4460 giants_foundry_hud_bar,10 +4461 giants_foundry_hud_bar,11 +4462 giants_foundry_hud_bar,12 +4463 giants_foundry_hud_bar,13 +4464 giants_foundry_hud_bar,14 +4465 giants_foundry_hud_bar,15 +4466 giants_foundry_hud_bar,16 +4467 giants_foundry_hud_bar,17 +4468 giants_foundry_hud_bar,18 +4469 giants_foundry_hud_bar,19 +4470 giants_foundry_hud_bar,20 +4471 giants_foundry_hud_bar,21 +4472 giants_foundry_hud_bar,22 +4473 giants_foundry_hud_bar,23 +4474 giants_foundry_hud_bar,24 +4475 giants_foundry_hud_bar,25 +4476 giants_foundry_hud_bar,26 +4477 giants_foundry_hud_bar,27 +4478 giants_foundry_hud_bar,28 +4479 giants_foundry_hud_bar,29 +4480 giants_foundry_hud_bar,30 +4481 giants_foundry_hud_bar,31 +4482 giants_foundry_hud_bar,32 +4483 giants_foundry_hud_bar,33 +4484 giants_foundry_hud_bar,34 +4485 giants_foundry_hud_bar,35 +4486 giants_foundry_hud_bar,36 +4487 giants_foundry_hud_bar,37 +4488 giants_foundry_hud_bar,38 +4489 giants_foundry_hud_bar,39 +4490 giants_foundry_hud_bar,40 +4491 giants_foundry_hud_bar,41 +4492 giants_foundry_hud_bar,42 +4493 giants_foundry_hud_bar,43 +4494 giants_foundry_hud_bar,44 +4495 giants_foundry_hud_bar,45 +4496 giants_foundry_hud_bar,46 +4497 giants_foundry_hud_bar,47 +4498 giants_foundry_hud_bar,48 +4499 giants_foundry_hud_bar,49 +4500 giants_foundry_hud_bar,50 +4501 giants_foundry_hud_bar,51 +4502 giants_foundry_hud_bar,52 +4503 giants_foundry_hud_bar,53 +4504 giants_foundry_hud_bar,54 +4505 giants_foundry_hud_bar,55 +4506 giants_foundry_hud_bar,56 +4507 giants_foundry_hud_bar,57 +4508 giants_foundry_hud_bar,58 +4509 giants_foundry_hud_bar,59 +4510 giants_foundry_hud_bar,60 +4511 giants_foundry_hud_bar,61 +4512 giants_foundry_hud_bar,62 +4513 graphic_4513 +4514 graphic_4514 +4515 graphic_4515 +4516 graphic_4516 +4517 pvpa_rankicons,3 +4518 pvpa_rankicons,4 +4519 pvpa_rankicons,5 +4520 pvpa_rankicons,6 +4521 pvpa_rankicons,7 +4522 pvpa_heart +4523 mod_icons_interface,50 +4524 mod_icons_interface,51 +4525 mod_icons_interface,52 +4526 mod_icons_interface,53 +4527 mod_icons_interface,54 +4528 mod_icons_interface,55 +4529 mod_icons_interface,56 +4530 mod_icons_interface,57 +4531 mod_icons_interface,58 +4532 mod_icons_interface,59 +4533 scrollbar_v2,2 +4534 scrollbar_v2,3 +4535 graphic_4535 +4536 graphic_4536 +4537 graphic_4537 +4538 graphic_4538 +4539 menu_buttons,6 +4540 menu_buttons,7 +4541 menu_buttons,8 +4542 menu_buttons,9 +4543 menu_buttons,10 +4544 menu_buttons,11 +4545 graphic_4545 +4546 graphic_4546 +4547 orb_filler,18 +4548 orb_filler,19 +4549 orb_filler,20 +4550 worldmap_marker,6 +4551 worldmap_marker,7 +4552 worldmap_marker,8 +4553 worldmap_marker_mini,0 +4554 worldmap_marker_mini,1 +4555 worldmap_marker_mini,2 +4556 hitmark,27 +4557 hitmark,28 +4558 hitmark,29 +4559 hitmark,30 +4560 hitmark,31 +4561 hitmark,32 +4562 hitmark,33 +4563 hitmark,34 +4564 hitmark,35 +4565 toa_grouping_icons,0 +4566 toa_grouping_icons,1 +4567 toa_grouping_icons,2 +4568 toa_grouping_icons,3 +4569 toa_grouping_icons,4 +4570 toa_grouping_icons,5 +4571 toa_grouping_icons,6 +4572 toa_grouping_icons,7 +4573 toa_grouping_icons,8 +4574 toa_grouping_icons,9 +4575 toa_grouping_icons,10 +4576 toa_grouping_icons,11 +4577 toa_grouping_icons,12 +4578 toa_grouping_icons,13 +4579 toa_grouping_icons,14 +4580 toa_grouping_icons,15 +4581 toa_grouping_icons,16 +4582 toa_grouping_icons,17 +4583 toa_invocation_icons,0 +4584 toa_invocation_icons,1 +4585 toa_invocation_icons,2 +4586 toa_invocation_icons,3 +4587 toa_invocation_icons,4 +4588 toa_invocation_icons,5 +4589 toa_invocation_icons,6 +4590 toa_invocation_icons,7 +4591 toa_invocation_icons,8 +4592 toa_invocation_icons,9 +4593 toa_invocation_icons,10 +4594 toa_invocation_icons,11 +4595 toa_invocation_icons,12 +4596 toa_invocation_icons,13 +4597 toa_invocation_icons,14 +4598 toa_invocation_icons,15 +4599 toa_invocation_icons,16 +4600 toa_invocation_icons,17 +4601 toa_invocation_icons,18 +4602 toa_invocation_icons,19 +4603 toa_invocation_icons,20 +4604 toa_invocation_icons,21 +4605 toa_invocation_icons,22 +4606 toa_invocation_icons,23 +4607 graphic_4607 +4608 graphic_4608 +4609 graphic_4609 +4610 graphic_4610 +4611 graphic_4611 +4612 graphic_4612 +4613 graphic_4613 +4614 graphic_4614 +4615 graphic_4615 +4616 graphic_4616 +4617 graphic_4617 +4618 graphic_4618 +4619 graphic_4619 +4620 graphic_4620 +4621 graphic_4621 +4622 graphic_4622 +4623 graphic_4623 +4624 graphic_4624 +4625 graphic_4625 +4626 graphic_4626 +4627 graphic_4627 +4628 graphic_4628 +4629 graphic_4629 +4630 graphic_4630 +4631 graphic_4631 +4632 graphic_4632 +4633 graphic_4633 +4634 graphic_4634 +4635 graphic_4635 +4636 graphic_4636 +4637 graphic_4637 +4638 graphic_4638 +4639 graphic_4639 +4640 graphic_4640 +4641 graphic_4641 +4642 graphic_4642 +4643 graphic_4643 +4644 graphic_4644 +4645 graphic_4645 +4646 graphic_4646 +4647 graphic_4647 +4648 graphic_4648 +4649 graphic_4649 +4650 graphic_4650 +4651 graphic_4651 +4652 graphic_4652 +4653 graphic_4653 +4654 graphic_4654 +4655 graphic_4655 +4656 graphic_4656 +4657 graphic_4657 +4658 graphic_4658 +4659 graphic_4659 +4660 graphic_4660 +4661 graphic_4661 +4662 graphic_4662 +4663 toa_info_icon +4664 fog01 +4665 fog02 +4666 fog03 +4667 skulls01 +4668 close_arrows,1 +4669 close_arrows,2 +4670 close_arrows,3 +4671 worldswitcher_stars,7 +4672 graphic_4672 +4673 graphic_4673 +4674 graphic_4674 +4675 graphic_4675 +4676 graphic_4676 +4677 graphic_4677 +4678 graphic_4678 +4679 graphic_4679 +4680 graphic_4680 +4681 graphic_4681 +4682 graphic_4682 +4683 graphic_4683 +4684 graphic_4684 +4685 graphic_4685 +4686 graphic_4686 +4687 graphic_4687 +4688 graphic_4688 +4689 graphic_4689 +4690 graphic_4690 +4691 graphic_4691 +4692 v2_stone_button_in,16 +4693 v2_stone_button_in,17 +4694 v2_stone_button_in,18 +4695 v2_stone_button_in,19 +4696 v2_stone_button_in,20 +4697 v2_stone_button_in,21 +4698 v2_stone_button_in,22 +4699 v2_stone_button_in,23 +4700 v2_stone_button_out,16 +4701 v2_stone_button_out,17 +4702 v2_stone_button_out,18 +4703 v2_stone_button_out,19 +4704 v2_stone_button_out,20 +4705 v2_stone_button_out,21 +4706 v2_stone_button_out,22 +4707 v2_stone_button_out,23 +4708 standard_prayer_30,0 +4709 standard_prayer_30,1 +4710 standard_prayer_40,0 +4711 standard_prayer_40,1 +4712 standard_prayer_50,0 +4713 standard_prayer_50,1 +4714 standard_prayer_60,0 +4715 standard_prayer_60,1 +4716 standard_prayer_70,0 +4717 standard_prayer_70,1 +4718 standard_prayer_80,0 +4719 standard_prayer_80,1 +4720 standard_prayer_100,0 +4721 standard_prayer_100,1 +4722 standard_prayer_120,0 +4723 standard_prayer_120,1 +4724 standard_prayer_140,0 +4725 standard_prayer_140,1 +4726 standard_prayer_160,0 +4727 standard_prayer_160,1 +4728 leftarrow_small_highlight +4729 rightarrow_small_highlight +4730 graphic_4730 +4731 graphic_4731 +4732 graphic_4732 +4733 graphic_4733 +4734 graphic_4734 +4735 graphic_4735 +4736 graphic_4736 +4737 graphic_4737 +4738 combination_lock +4739 combination_lock_join,0 +4740 combination_lock_join,1 +4741 combination_lock_join,2 +4742 combination_lock_arrows,0 +4743 combination_lock_arrows,1 +4744 combination_lock_arrows,2 +4745 combination_lock_arrows,3 +4746 graphic_4746 +4747 graphic_4747 +4748 graphic_4748 +4749 graphic_4749 +4750 graphic_4750 +4751 graphic_4751 +4752 graphic_4752 +4753 graphic_4753 +4754 graphic_4754 +4755 graphic_4755 +4756 graphic_4756 +4757 graphic_4757 +4758 graphic_4758 +4759 graphic_4759 +4760 graphic_4760 +4761 graphic_4761 +4762 graphic_4762 +4763 hitmark,37 +4764 hitmark,38 +4765 hitmark,39 +4766 hitmark,40 +4767 hitmark,41 +4768 hitmark,42 +4769 hitmark,43 +4770 hitmark,44 +4771 standard_poison_30,0 +4772 standard_poison_30,1 +4773 standard_poison_40,0 +4774 standard_poison_40,1 +4775 standard_poison_50,0 +4776 standard_poison_50,1 +4777 standard_poison_60,0 +4778 standard_poison_60,1 +4779 standard_poison_70,0 +4780 standard_poison_70,1 +4781 standard_poison_80,0 +4782 standard_poison_80,1 +4783 standard_poison_100,0 +4784 standard_poison_100,1 +4785 standard_poison_120,0 +4786 standard_poison_120,1 +4787 standard_poison_140,0 +4788 standard_poison_140,1 +4789 standard_poison_160,0 +4790 standard_poison_160,1 +4791 cross_big,0 +4792 cross_big,1 +4793 cross_big,2 +4794 side_icons,40 +4795 side_icons,41 +4796 side_icons,42 +4797 side_icons,43 +4798 side_icons,44 +4799 side_icons,45 +4800 side_icons,46 +4801 side_icons,47 +4802 side_icons,48 +4803 side_icons,49 +4804 side_icons,50 +4805 side_icons,51 +4806 side_icons,52 +4807 side_icons,53 +4808 side_icons,54 +4809 side_icons,55 +4810 side_icons,56 +4811 side_icons,57 +4812 side_icons,58 +4813 side_icons,59 +4814 side_icons,60 +4815 side_icons,61 +4816 side_icons,62 +4817 side_icons,63 +4818 sideicons_interface,40 +4819 sideicons_interface,41 +4820 sideicons_interface,42 +4821 sideicons_interface,43 +4822 sideicons_interface,44 +4823 sideicons_interface,45 +4824 sideicons_interface,46 +4825 sideicons_interface,47 +4826 sideicons_interface,48 +4827 sideicons_interface,49 +4828 sideicons_interface,50 +4829 sideicons_interface,51 +4830 sideicons_interface,52 +4831 sideicons_interface,53 +4832 sideicons_interface,54 +4833 sideicons_interface,55 +4834 sideicons_interface,56 +4835 sideicons_interface,57 +4836 sideicons_interface,58 +4837 sideicons_interface,59 +4838 sideicons_interface,60 +4839 sideicons_interface,61 +4840 sideicons_interface,62 +4841 sideicons_interface,63 +4842 graphic_4842 +4843 graphic_4843 +4844 graphic_4844 +4845 graphic_4845 +4846 graphic_4846 +4847 graphic_4847 +4848 graphic_4848 +4849 graphic_4849 +4850 graphic_4850 +4851 graphic_4851 +4852 graphic_4852 +4853 graphic_4853 +4854 graphic_4854 +4855 graphic_4855 +4856 graphic_4856 +4857 graphic_4857 +4858 graphic_4858 +4859 graphic_4859 +4860 graphic_4860 +4861 graphic_4861 +4862 graphic_4862 +4863 graphic_4863 +4864 graphic_4864 +4865 graphic_4865 +4866 graphic_4866 +4867 graphic_4867 +4868 graphic_4868 +4869 graphic_4869 +4870 graphic_4870 +4871 graphic_4871 +4872 graphic_4872 +4873 graphic_4873 +4874 graphic_4874 +4875 graphic_4875 +4876 graphic_4876 +4877 graphic_4877 +4878 graphic_4878 +4879 graphic_4879 +4880 graphic_4880 +4881 graphic_4881 +4882 graphic_4882 +4883 graphic_4883 +4884 graphic_4884 +4885 graphic_4885 +4886 graphic_4886 +4887 graphic_4887 +4888 graphic_4888 +4889 graphic_4889 +4890 graphic_4890 +4891 graphic_4891 +4892 prayerglow,0 +4893 prayerglow,1 +4894 settings_slider,12 +4895 settings_slider,13 +4896 settings_slider,14 +4897 settings_slider,15 +4898 settings_slider,16 +4899 settings_slider,17 +4900 popout_icons,3 +4901 graphic_4901 +4902 graphic_4902 +4903 graphic_4903 +4904 graphic_4904 +4905 graphic_4905 +4906 graphic_4906 +4907 graphic_4907 +4908 graphic_4908 +4909 ground_items_visibility,0 +4910 ground_items_visibility,1 +4911 graphic_4911 +4912 graphic_4912 +4913 graphic_4913 +4914 graphic_4914 +4915 graphic_4915 +4916 graphic_4916 +4917 graphic_4917 +4918 graphic_4918 +4919 graphic_4919 +4920 graphic_4920 +4921 graphic_4921 +4922 graphic_4922 +4923 graphic_4923 +4924 worldswitcher_stars,8 +4925 graphic_4925 +4926 graphic_4926 +4927 worldswitcher_stars,9 +4928 worldswitcher_stars,10 +4929 worldswitcher_stars,11 +4930 worldswitcher_stars,12 +4931 worldswitcher_stars,13 +4932 worldswitcher_stars,14 +4933 worldswitcher_stars,15 +4934 worldswitcher_stars,16 +4935 worldswitcher_stars,17 +4936 worldswitcher_flags,9 +4937 worldswitcher_flags,10 +4938 worldswitcher_flags,11 +4939 graphic_4939 +4940 graphic_4940 +4941 mapfunction,133 +4942 mapfunction,134 +4943 mapfunction,135 +4944 mapfunction,136 +4945 mapfunction,137 +4946 mapfunction,138 +4947 mapfunction,139 +4948 clan_rank_icons,275 +4949 clan_rank_icons,276 +4950 clan_rank_icons,277 +4951 clan_rank_icons,278 +4952 clan_rank_icons,279 +4953 clan_rank_icons,280 +4954 clan_rank_icons,281 +4955 clan_rank_icons,282 +4956 clan_rank_icons,283 +4957 clan_rank_icons,284 +4958 clan_rank_icons,285 +4959 clan_rank_icons,286 +4960 clan_rank_icons,287 +4961 clan_rank_icons,288 +4962 graphic_4948 +4963 graphic_4963 +4964 graphic_4964 +4965 graphic_4965 +4966 graphic_4966 +4967 graphic_4967 +4968 graphic_4968 +4969 graphic_4969 +4970 graphic_4970 +4971 graphic_4971 +4972 graphic_4972 +4973 graphic_4973 +4974 graphic_4974 +4975 graphic_4975 +4976 graphic_4976 +4977 graphic_4977 +4978 graphic_4978 +4979 graphic_4979 +4980 graphic_4980 +4981 graphic_4981 +4982 graphic_4982 +4983 graphic_4983 +4984 graphic_4984 +4985 graphic_4985 +4986 graphic_4986 +4987 graphic_4987 +4988 graphic_4988 +4989 graphic_4989 +4990 graphic_4990 +4991 graphic_4991 +4992 graphic_4992 +4993 graphic_4993 +4994 graphic_4994 +4995 graphic_4995 +4996 graphic_4996 +4997 graphic_4997 +4998 graphic_4998 +4999 graphic_4999 +5000 graphic_5000 +5001 graphic_5001 +5002 graphic_5002 +5003 graphic_5003 +5004 graphic_5004 +5005 graphic_5005 +5006 graphic_5006 +5007 graphic_5007 +5008 graphic_5008 +5009 graphic_5009 +5010 graphic_5010 +5011 graphic_5011 +5012 graphic_5012 +5013 graphic_5013 +5014 graphic_5014 +5015 graphic_5015 +5016 graphic_5016 +5017 graphic_5017 +5018 graphic_5018 +5019 graphic_5019 +5020 graphic_5020 +5021 graphic_5021 +5022 graphic_5022 +5023 graphic_5023 +5024 graphic_5024 +5025 graphic_5025 +5026 graphic_5026 +5027 graphic_5027 +5028 graphic_5028 +5029 graphic_5029 +5030 graphic_5030 +5031 graphic_5031 +5032 graphic_5032 +5033 graphic_5033 +5034 graphic_5034 +5035 graphic_5035 +5036 graphic_5036 +5037 graphic_5037 +5038 graphic_5038 +5039 graphic_5039 +5040 graphic_5040 +10001 graphic_10001 +10002 graphic_10002 +10003 graphic_10003 +10004 graphic_10004 +10005 graphic_10005 +10006 graphic_10006 +10007 graphic_10007 +10008 graphic_10008 +10009 graphic_10009 +10023 graphic_10023 +10030 graphic_10030 +10031 graphic_10031 +10037 graphic_10037 + */ diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/TextAlign.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/TextAlign.kt new file mode 100644 index 0000000..0447bd1 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/TextAlign.kt @@ -0,0 +1,10 @@ +package stan.qodat.scene.runescape.widget.component + +sealed class TextAlign(val id: Int) { + data object left : TextAlign(0) + data object centre : TextAlign(1) + data object right : TextAlign(2) + data object justify : TextAlign(3) + data object top : TextAlign(0) + data object bottom : TextAlign(2) +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Graphic.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Graphic.kt new file mode 100644 index 0000000..7e6bd28 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Graphic.kt @@ -0,0 +1,36 @@ +package stan.qodat.scene.runescape.widget.component.impl + +import stan.qodat.scene.runescape.widget.component.* + +class Graphic( + override var name: String? = null, + override var x: Int = 0, + override var y: Int = 0, + override var width: Int = 0, + override var height: Int = 0, + override var hSize: Size = Size.abs, + override var vSize: Size = Size.abs, + override var hPos: Pos = Pos.abs_left, + override var vPos: Pos = Pos.abs_top +) : Component() { + + var sprite: Sprites? = null + var secondarySprite: Sprites? = null + var repeatSprite: Boolean = false + var spriteRotation: Int = 0 + + var flipH: Boolean = false + var flipV: Boolean = false + var borderThickness: Int = 0 + + var itemId: Int? = null + var itemAmount: Int? = null + var shadowColour: Colour? = null + var colour: Colour? = null + + var trans: Int = 0 + var op1: String? = null + + override fun toReference(name: String) = + GraphicRef(name, this) +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Inventory.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Inventory.kt new file mode 100644 index 0000000..ee76b6e --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Inventory.kt @@ -0,0 +1,22 @@ +package stan.qodat.scene.runescape.widget.component.impl + +import stan.qodat.scene.runescape.widget.component.Component +import stan.qodat.scene.runescape.widget.component.InventoryRef +import stan.qodat.scene.runescape.widget.component.Pos +import stan.qodat.scene.runescape.widget.component.Size + +class Inventory( + override var name: String? = null, + override var x: Int = 0, + override var y: Int = 0, + override var width: Int = 0, + override var height: Int = 0, + override var hSize: Size = Size.abs, + override var vSize: Size = Size.abs, + override var hPos: Pos = Pos.abs_left, + override var vPos: Pos = Pos.abs_top +) : Component() { + + override fun toReference(name: String) = + InventoryRef(name, this) +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Layer.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Layer.kt new file mode 100644 index 0000000..3e7bbe0 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Layer.kt @@ -0,0 +1,40 @@ +package stan.qodat.scene.runescape.widget.component.impl + +import stan.qodat.scene.runescape.widget.component.Component +import stan.qodat.scene.runescape.widget.component.LayerRef +import stan.qodat.scene.runescape.widget.component.Pos +import stan.qodat.scene.runescape.widget.component.Size + +class Layer( + override var name: String? = null, + override var x: Int = 0, + override var y: Int = 0, + override var width: Int = 0, + override var height: Int = 0, + override var hSize: Size = Size.abs, + override var vSize: Size = Size.abs, + override var hPos: Pos = Pos.abs_left, + override var vPos: Pos = Pos.abs_top +) : Component() { + + val children = mutableListOf>() + + var scrollX : Int = 0 + var scrollY : Int = 0 + var scrollHeight: Int = 0 + + fun layer(name: String? = null, x: Int = 0, y: Int = 0, width: Int = 0, height: Int = 0, hSize: Size = Size.abs, vSize: Size = Size.abs, hPos: Pos = Pos.abs_left, vPos: Pos = Pos.abs_top, init: Layer.() -> Unit = {}): Layer = + Layer(name, x, y, width, height, hSize, vSize, hPos, vPos).apply(init).apply(children::add) + + fun rectangle(name: String? = null, x : Int = 0, y : Int = 0, width : Int = 0, height : Int = 0, hSize : Size = Size.abs, vSize : Size = Size.abs, hPos: Pos = Pos.abs_left, vPos: Pos = Pos.abs_top, init : Rectangle.() -> Unit = {}) : Rectangle = + Rectangle(name, x, y, width, height, hSize, vSize, hPos, vPos).apply(init).apply(children::add) + + fun graphic(name: String? = null, x: Int = 0, y: Int = 0, width: Int = 0, height: Int = 0, hSize: Size = Size.abs, vSize: Size = Size.abs, hPos: Pos = Pos.abs_left, vPos: Pos = Pos.abs_top, init: Graphic.() -> Unit = {}): Graphic = + Graphic(name, x, y, width, height, hSize, vSize, hPos, vPos).apply(init).apply(children::add) + + fun text(name: String? = null, x: Int = 0, y: Int = 0, width: Int = 0, height: Int = 0, hSize: Size = Size.abs, vSize: Size = Size.abs, hPos: Pos = Pos.abs_left, vPos: Pos = Pos.abs_top, init: Text.() -> Unit = {}): Text = + Text(name, x, y, width, height, hSize, vSize, hPos, vPos).apply(init).apply(children::add) + + override fun toReference(name: String) = + LayerRef(name, this) +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Line.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Line.kt new file mode 100644 index 0000000..31e9bef --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Line.kt @@ -0,0 +1,22 @@ +package stan.qodat.scene.runescape.widget.component.impl + +import stan.qodat.scene.runescape.widget.component.Component +import stan.qodat.scene.runescape.widget.component.LineRef +import stan.qodat.scene.runescape.widget.component.Pos +import stan.qodat.scene.runescape.widget.component.Size + +class Line( + override var name: String? = null, + override var x: Int = 0, + override var y: Int = 0, + override var width: Int = 0, + override var height: Int = 0, + override var hSize: Size = Size.abs, + override var vSize: Size = Size.abs, + override var hPos: Pos = Pos.abs_left, + override var vPos: Pos = Pos.abs_top +) : Component() { + + override fun toReference(name: String) = + LineRef(name, this) +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Model.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Model.kt new file mode 100644 index 0000000..06a1569 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Model.kt @@ -0,0 +1,21 @@ +package stan.qodat.scene.runescape.widget.component.impl + +import stan.qodat.scene.runescape.widget.component.* + +class Model( + override var name: String? = null, + override var x: Int = 0, + override var y: Int = 0, + override var width: Int = 0, + override var height: Int = 0, + override var hSize: Size = Size.abs, + override var vSize: Size = Size.abs, + override var hPos: Pos = Pos.abs_left, + override var vPos: Pos = Pos.abs_top +) : Component() { + + var model : Models? = null + + override fun toReference(name: String) = + ModelRef(name, this) +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/ModelList.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/ModelList.kt new file mode 100644 index 0000000..fbdb20a --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/ModelList.kt @@ -0,0 +1,21 @@ +package stan.qodat.scene.runescape.widget.component.impl + +import stan.qodat.scene.runescape.widget.component.Component +import stan.qodat.scene.runescape.widget.component.ModelListRef +import stan.qodat.scene.runescape.widget.component.Pos +import stan.qodat.scene.runescape.widget.component.Size + +class ModelList( + override var name: String? = null, + override var x: Int = 0, + override var y: Int = 0, + override var width: Int = 0, + override var height: Int = 0, + override var hSize: Size = Size.abs, + override var vSize: Size = Size.abs, + override var hPos: Pos = Pos.abs_left, + override var vPos: Pos = Pos.abs_top +) : Component() { + override fun toReference(name: String) = + ModelListRef(name, this) +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Rectangle.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Rectangle.kt new file mode 100644 index 0000000..966d160 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Rectangle.kt @@ -0,0 +1,21 @@ +package stan.qodat.scene.runescape.widget.component.impl + +import stan.qodat.scene.runescape.widget.component.* + +class Rectangle( + override var name: String? = null, + override var x: Int = 0, + override var y: Int = 0, + override var width: Int = 0, + override var height: Int = 0, + override var hSize: Size = Size.abs, + override var vSize: Size = Size.abs, + override var hPos: Pos = Pos.abs_left, + override var vPos: Pos = Pos.abs_top +) : Component() { + var colour: Colour? = null + var trans: Int = 0 + var filled: Boolean = false + override fun toReference(name: String) = + RectangleRef(name, this) +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Text.kt b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Text.kt new file mode 100644 index 0000000..441a302 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/widget/component/impl/Text.kt @@ -0,0 +1,28 @@ +package stan.qodat.scene.runescape.widget.component.impl + +import stan.qodat.scene.runescape.widget.component.* + +class Text( + override var name: String? = null, + override var x: Int = 0, + override var y: Int = 0, + override var width: Int = 0, + override var height: Int = 0, + override var hSize: Size = Size.abs, + override var vSize: Size = Size.abs, + override var hPos: Pos = Pos.abs_left, + override var vPos: Pos = Pos.abs_top +) : Component() { + var trans: Int = 0 + var text: String? = null + var itemId: Int? = null + var itemAmount: Int? = null + var shadowed: Boolean = false + var colour: Colour? = null + var font: Font? = null + var hAlign: TextAlign = TextAlign.left + var vAlign: TextAlign = TextAlign.top + var lineHeight: Int = 0 + override fun toReference(name: String) = + TextRef(name, this) +} From f529bf4955d19fe5f6dc172338e7cbff18593e63 Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 30 Apr 2024 22:00:25 +0200 Subject: [PATCH 11/14] Set ViewNodeListView.fixedCellSize to prevent virtual scrolling bugs --- src/main/kotlin/stan/qodat/scene/control/ViewNodeListView.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/stan/qodat/scene/control/ViewNodeListView.kt b/src/main/kotlin/stan/qodat/scene/control/ViewNodeListView.kt index 2f27130..ee59ff3 100644 --- a/src/main/kotlin/stan/qodat/scene/control/ViewNodeListView.kt +++ b/src/main/kotlin/stan/qodat/scene/control/ViewNodeListView.kt @@ -26,6 +26,7 @@ open class ViewNodeListView : ListView() { lateinit var contextMenuBuilder: (N) -> ContextMenu init { + fixedCellSize = 22.0 cellFactory = Callback { val listCell = object : ListCell() { override fun updateItem(item: N?, empty: Boolean) { From 9eb5fe33f769e5053b59a202dc73b82246c5453c Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 30 Apr 2024 22:01:41 +0200 Subject: [PATCH 12/14] Added better exception handling and logging --- build.gradle.kts | 3 +- src/main/kotlin/stan/qodat/Qodat.kt | 18 ++++-- .../impl/oldschool/OldschoolCacheRuneLite.kt | 4 +- .../control/dialog/CacheChooserDialog.kt | 11 ++-- .../controller/CacheChooserController.kt | 56 +++++++++++------- .../kotlin/stan/qodat/task/BackgroundTasks.kt | 8 +-- .../kotlin/stan/qodat/util/ExceptionDialog.kt | 53 +++++++++++++++++ src/main/resources/logback.xml | 58 +++++++++++++++++++ 8 files changed, 171 insertions(+), 40 deletions(-) create mode 100644 src/main/kotlin/stan/qodat/util/ExceptionDialog.kt create mode 100644 src/main/resources/logback.xml diff --git a/build.gradle.kts b/build.gradle.kts index dbeb08b..9887b37 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ repositories { jcenter() } -version = "0.1.9" +version = "0.2.6" allprojects { group = "stan.qodat" @@ -65,6 +65,7 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.8.0") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0") implementation("org.orbisgis:poly2tri-core:0.1.2") + implementation(group = "ch.qos.logback", name = "logback-classic", version = "1.2.9") implementation("com.displee:rs-cache-library:7.1.3") implementation("org.joml:joml-primitives:1.10.0") implementation("org.joml:joml:1.10.5") diff --git a/src/main/kotlin/stan/qodat/Qodat.kt b/src/main/kotlin/stan/qodat/Qodat.kt index d9f2ea8..a8f7871 100644 --- a/src/main/kotlin/stan/qodat/Qodat.kt +++ b/src/main/kotlin/stan/qodat/Qodat.kt @@ -19,6 +19,7 @@ import stan.qodat.scene.controller.MainController import stan.qodat.scene.controller.ModelController import stan.qodat.util.ActionCache import stan.qodat.util.PropertiesManager +import stan.qodat.util.runCatchingWithDialog import java.nio.file.Path import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @@ -34,15 +35,20 @@ import kotlin.system.exitProcess class Qodat : Application() { override fun start(primaryStage: Stage) { + runCatchingWithDialog(activityName = "Starting Qodat") { - stage = primaryStage - val loadedProperties = propertiesManager.loadFromFile() + stage = primaryStage - Properties.bind(propertiesManager) + val loadedProperties = propertiesManager.loadFromFile() - SubScene3D.init() + Properties.bind(propertiesManager) - loadMainController(primaryStage, loadedProperties) + SubScene3D.init() + + loadMainController(primaryStage, loadedProperties) + }.onFailure { + logException("Failed to start Qodat", it) + } } private fun loadMainController(primaryStage: Stage, loadedProperties: Boolean) { @@ -159,4 +165,4 @@ class Qodat : Application() { // } } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt b/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt index 004dcd3..e73fa9e 100644 --- a/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt +++ b/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt @@ -210,7 +210,9 @@ object OldschoolCacheRuneLite : Cache("LIVE") { animations = seqArchiveFiles.files.map { try { - val sequence = SequenceLoader().load(it.fileId, it.contents) + val sequence = SequenceLoader().apply { + configureForRevision(seqArchive.revision) + }.load(it.fileId, it.contents) object : AnimationDefinition { override val id: String = it.fileId.toString() override val frameHashes: IntArray = sequence.frameIDs?: IntArray(0) diff --git a/src/main/kotlin/stan/qodat/scene/control/dialog/CacheChooserDialog.kt b/src/main/kotlin/stan/qodat/scene/control/dialog/CacheChooserDialog.kt index 00a955f..a17be34 100644 --- a/src/main/kotlin/stan/qodat/scene/control/dialog/CacheChooserDialog.kt +++ b/src/main/kotlin/stan/qodat/scene/control/dialog/CacheChooserDialog.kt @@ -6,12 +6,13 @@ import javafx.scene.control.Dialog import javafx.scene.layout.AnchorPane import stan.qodat.Qodat import stan.qodat.scene.controller.CacheChooserController +import stan.qodat.util.runCatchingWithDialog import java.nio.file.Path class CacheChooserDialog : Dialog>() { init { - try { + runCatchingWithDialog(activityName = "Creating CacheChooserDialog") { val cacheChooserLoader = FXMLLoader(Qodat::class.java.getResource("cachechooser.fxml")) val root = cacheChooserLoader.load() @@ -35,10 +36,8 @@ class CacheChooserDialog : Dialog>() { else -> throw Exception("Unexpected button type {$it}") } } - - - } catch (e: Exception) { - e.printStackTrace() + }.onFailure { + Qodat.logException("Failed to create CacheChooserDialog", it) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/controller/CacheChooserController.kt b/src/main/kotlin/stan/qodat/scene/controller/CacheChooserController.kt index 931e0ba..3615cce 100644 --- a/src/main/kotlin/stan/qodat/scene/controller/CacheChooserController.kt +++ b/src/main/kotlin/stan/qodat/scene/controller/CacheChooserController.kt @@ -19,8 +19,8 @@ import javafx.scene.layout.Priority import javafx.scene.layout.VBox import javafx.scene.text.TextAlignment import javafx.stage.DirectoryChooser +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.apache.commons.compress.archivers.tar.TarArchiveEntry @@ -28,7 +28,12 @@ import org.apache.commons.compress.archivers.tar.TarArchiveInputStream import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream import org.jsoup.Jsoup import stan.qodat.Properties -import java.io.* +import stan.qodat.Qodat +import stan.qodat.util.runCatchingWithDialog +import java.io.BufferedInputStream +import java.io.BufferedOutputStream +import java.io.File +import java.io.FileOutputStream import java.net.URL import java.nio.file.Path import java.nio.file.Paths @@ -121,19 +126,10 @@ class CacheChooserController : Initializable { } listCaches.placeholder = listCachesPlaceholder - try { - val doc = Jsoup.connect(RUNESTATS_URL).get() - entries.addAll(doc.select("a") - .map { col -> col.attr("href") } - .filter { it.length > 10 } // get rid of ../ and ./types - .reversed() - ) - } catch (e: Exception) { - e.printStackTrace() - listCachesPlaceholder.text += "\n\n${e.message}" - if (e is SSLHandshakeException) { - listCachesPlaceholder.text += "\n\nSSLHandshakeException is a known bug with certain Java versions, try updating." - } + runCatchingWithDialog("Fetching caches") { + fetchRuneStatsCaches(listCachesPlaceholder) + }.onFailure { + Qodat.logException("Failed to fetch caches", it) } val filterableEntries = FilteredList(entries) @@ -161,6 +157,23 @@ class CacheChooserController : Initializable { } } + private fun fetchRuneStatsCaches(listCachesPlaceholder: Label) { + try { + val doc = Jsoup.connect(RUNESTATS_URL).get() + entries.addAll(doc.select("a") + .map { col -> col.attr("href") } + .filter { it.length > 10 } // get rid of ../ and ./types + .reversed() + ) + } catch (e: Exception) { + e.printStackTrace() + listCachesPlaceholder.text += "\n\n${e.message}" + if (e is SSLHandshakeException) { + listCachesPlaceholder.text += "\n\nSSLHandshakeException is a known bug with certain Java versions, try updating." + } + } + } + private fun downloadCache(cacheName: String, dirChooser: DirChooserHBox) { lblStatusText.isVisible = true lblStatusText.text = "Downloading cache $cacheName please wait.." @@ -169,9 +182,8 @@ class CacheChooserController : Initializable { .pathProperty.get() .resolve(cacheName.removeSuffix(".tar.gz")) .toFile() - - GlobalScope.launch { - try { + runCatchingWithDialog("Downloading cache $cacheName") { + CoroutineScope(Dispatchers.IO).launch { val conn = URL("$RUNESTATS_URL/$cacheName").openConnection() conn.addRequestProperty("User-Agent", "qodat") BufferedInputStream(withContext(Dispatchers.IO) { @@ -201,9 +213,9 @@ class CacheChooserController : Initializable { lblStatusText.isVisible = false dirChooser.field.text = destFolder.resolve("cache").absolutePath.toString() } - } catch (e: IOException) { - e.printStackTrace() } + }.onFailure { + Qodat.logException("Failed to download cache $cacheName", it) } } @@ -220,7 +232,7 @@ class CacheChooserController : Initializable { property: ObjectProperty, lblErrorText: Label, editable: Boolean = true, - disableOkButtonIfEmpty: Boolean = false + disableOkButtonIfEmpty: Boolean = false, ) : VBox(5.0) { var pathProperty = SimpleObjectProperty(property.get()) val field = TextField().apply { @@ -277,4 +289,4 @@ class CacheChooserController : Initializable { }) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/task/BackgroundTasks.kt b/src/main/kotlin/stan/qodat/task/BackgroundTasks.kt index dc5c881..d2d835a 100644 --- a/src/main/kotlin/stan/qodat/task/BackgroundTasks.kt +++ b/src/main/kotlin/stan/qodat/task/BackgroundTasks.kt @@ -56,7 +56,7 @@ object BackgroundTasks { if (addProgressIndicator) { val stackPane = StackPane() - GlobalScope.launch(Dispatchers.JavaFx) { + CoroutineScope(Dispatchers.JavaFx).launch { val progressPane = ProgressIndicatorPane() progressPane.bindPrefWidth(mainPane) @@ -67,7 +67,7 @@ object BackgroundTasks { withContext(Dispatchers.Default) { try { task.run() - GlobalScope.launch(Dispatchers.IO) { + launch(Dispatchers.IO) { when (val result = task.get(240, TimeUnit.SECONDS)) { is ExportTaskResult.Success -> showOpenFileOption(result.saveDir, progressBox) @@ -85,7 +85,7 @@ object BackgroundTasks { } } } else { - GlobalScope.launch(Dispatchers.Default) { + CoroutineScope(Dispatchers.Default).launch { try { task.run() } catch (e: Exception) { @@ -163,4 +163,4 @@ object BackgroundTasks { progressBar.progressProperty().bind(task.progressProperty()) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/util/ExceptionDialog.kt b/src/main/kotlin/stan/qodat/util/ExceptionDialog.kt new file mode 100644 index 0000000..fb6c645 --- /dev/null +++ b/src/main/kotlin/stan/qodat/util/ExceptionDialog.kt @@ -0,0 +1,53 @@ +package stan.qodat.util + +import javafx.scene.control.Alert +import javafx.scene.control.Alert.AlertType +import javafx.scene.control.Label +import javafx.scene.control.TextArea +import javafx.scene.layout.GridPane +import javafx.scene.layout.Priority +import java.io.PrintWriter +import java.io.StringWriter + + +fun runCatchingWithDialog( + activityName: String, + block: () -> T +) : Result { + return runCatching { + block() + }.onFailure { + + val alert = Alert(AlertType.ERROR) + alert.title = "Exception Dialog" + alert.headerText = "Something went wrong in $activityName" + alert.contentText = it.localizedMessage + + // Create expandable Exception. + val sw = StringWriter() + val pw = PrintWriter(sw) + it.printStackTrace(pw) + val exceptionText = sw.toString() + + val label: Label = Label("The exception stacktrace was:") + + val textArea: TextArea = TextArea(exceptionText) + textArea.isEditable = false + textArea.isWrapText = true + + textArea.maxWidth = Double.MAX_VALUE + textArea.maxHeight = Double.MAX_VALUE + GridPane.setVgrow(textArea, Priority.ALWAYS) + GridPane.setHgrow(textArea, Priority.ALWAYS) + + val expContent = GridPane() + expContent.maxWidth = Double.MAX_VALUE + expContent.add(label, 0, 0) + expContent.add(textArea, 0, 1) + + + // Set expandable Exception into the dialog pane. + alert.dialogPane.expandableContent = expContent + alert.showAndWait() + } +} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..40ad44c --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,58 @@ + + + + + + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + + + + + ${user.home}/.qodat/logs/launcher.log + + + + ${user.home}/.qodat/logs/launcher_%d{yyyy-MM-dd}.%i.log + + + + 10MB + + + + 30 + + + + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + From de122fa4a49d4106a670e525385e8cec76477b10 Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Tue, 30 Apr 2024 22:17:17 +0200 Subject: [PATCH 13/14] Configure SequenceLoader for seq archive revision of target cache --- src/main/kotlin/stan/qodat/util/osrs_anim_parser.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/stan/qodat/util/osrs_anim_parser.kt b/src/main/kotlin/stan/qodat/util/osrs_anim_parser.kt index dc27ad5..bac175b 100644 --- a/src/main/kotlin/stan/qodat/util/osrs_anim_parser.kt +++ b/src/main/kotlin/stan/qodat/util/osrs_anim_parser.kt @@ -11,10 +11,7 @@ import net.runelite.cache.definitions.loaders.SequenceLoader import net.runelite.cache.fs.ArchiveFiles import net.runelite.cache.fs.Store import stan.qodat.Properties -import stan.qodat.cache.impl.oldschool.OldschoolCacheRuneLite -import stan.qodat.cache.impl.oldschool.loader.SequenceLoader206 import java.io.FileWriter -import java.nio.file.Paths import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger import java.util.stream.Collectors @@ -44,6 +41,7 @@ fun createNpcAnimsJsonDir( val animationFiles = files.files val animations: Map> = animationFiles.parallelStream().map { file -> val loader = SequenceLoader() + loader.configureForRevision(seqArchive.revision) val anim = loader.load(file.fileId, file.contents) Platform.runLater { val progress = (100.0 * anim.id.toFloat().div(animationFiles.size)) @@ -74,9 +72,6 @@ fun createNpcAnimsJsonDir( val total = npcManager.npcs.size val counter = AtomicInteger(0) npcManager.npcs.parallelStream().forEach { npc -> - if (npc.name.contains("Akkha")){ - println() - } val animationRef = intArrayOf( npc.walkingAnimation, npc.standingAnimation, @@ -142,6 +137,7 @@ fun createObjectAnimsJsonDir( val animationFiles = files.files val animations = animationFiles.parallelStream().map { file -> val loader = SequenceLoader() + loader.configureForRevision(seqArchive.revision) val anim = loader.load(file.fileId, file.contents) Platform.runLater { val progress = (100.0 * anim.id.toFloat().div(animationFiles.size)) From 13312b7fbe7f6324d188f05e5a913d4b648e14bd Mon Sep 17 00:00:00 2001 From: stanvanderbend Date: Wed, 1 May 2024 21:17:52 +0200 Subject: [PATCH 14/14] Added support for new animation system, decreased cpu usage a lot --- .../src/main/kotlin/qodat/cache/Cache.kt | 2 +- .../definition/AnimationFrameDefinition.kt | 17 +- .../AnimationFrameLegacyDefinition.kt | 18 + .../definition/AnimationMayaDefinition.kt | 13 + .../kotlin/qodat/cache/models/RS2Model.java | 21 +- .../qodat/cache/models/RSModelLoader.kt | 3 + src/main/kotlin/jagex/AnimationState.java | 35 + src/main/kotlin/jagex/Bone.java | 137 ++++ src/main/kotlin/jagex/BoneTransform.java | 349 +++++++++ src/main/kotlin/jagex/Buffer.java | 52 ++ src/main/kotlin/jagex/KeyFrame.java | 23 + src/main/kotlin/jagex/MayaAnimation.java | 376 ++++++++++ src/main/kotlin/jagex/MayaAnimationFrame.java | 696 ++++++++++++++++++ .../kotlin/jagex/MayaAnimationFrameData.java | 34 + .../kotlin/jagex/MayaAnimationFrameFlag.java | 44 ++ .../kotlin/jagex/MayaAnimationFrameType.java | 81 ++ .../jagex/MayaAnimationLoadFrameTask.java | 26 + .../kotlin/jagex/MayaAnimationLoadTask.java | 15 + .../kotlin/jagex/MayaAnimationSkeleton.java | 55 ++ src/main/kotlin/jagex/MouseWheel.java | 6 + src/main/kotlin/jagex/PolynomialSolver.java | 12 + src/main/kotlin/jagex/Quaternion.java | 84 +++ src/main/kotlin/jagex/Skeleton.java | 52 ++ .../kotlin/jagex/TransformationMatrix.java | 101 +++ src/main/kotlin/jagex/class121.java | 18 + src/main/kotlin/jagex/class130.java | 10 + src/main/kotlin/jagex/class134.java | 26 + src/main/kotlin/jagex/class277.java | 8 + src/main/kotlin/jagex/class4.java | 13 + src/main/kotlin/jagex/class415.java | 32 + src/main/kotlin/jagex/class461.java | 6 + .../kotlin/net/runelite/mapping/Export.java | 6 + .../runelite/mapping/ObfuscatedGetter.java | 8 + .../net/runelite/mapping/ObfuscatedName.java | 6 + .../runelite/mapping/ObfuscatedSignature.java | 8 + src/main/kotlin/stan/qodat/Properties.kt | 1 + .../stan/qodat/cache/CacheAssetLoader.kt | 14 +- .../cache/impl/legacy/LegacyDefinitions.kt | 2 +- .../impl/oldschool/OldschoolCacheRuneLite.kt | 41 +- .../stan/qodat/cache/impl/qodat/QodatCache.kt | 9 +- .../cache/impl/qodat/QodatDefinitions.kt | 2 +- .../qodat/scene/control/export/ExportMenu.kt | 5 +- .../control/export/gif/AnimationToGifTask.kt | 7 +- .../scene/control/export/gif/GifFormat.kt | 10 +- .../control/export/mp4/AnimationToMp4Task.kt | 6 +- .../control/tree/AnimationFrameTreeItem.kt | 55 +- .../scene/control/tree/AnimationTreeItem.kt | 26 +- .../scene/control/tree/EntityTreeItem.kt | 4 +- .../scene/control/tree/TransformTreeItem.kt | 29 +- .../scene/controller/AnimationController.kt | 9 +- .../scene/controller/EditorController.kt | 5 +- .../qodat/scene/controller/ModelController.kt | 1 + .../scene/controller/TimeLineController.kt | 21 +- .../scene/runescape/animation/Animation.kt | 98 +-- .../runescape/animation/AnimationExporter.kt | 17 +- .../runescape/animation/AnimationFrame.kt | 75 +- .../animation/AnimationFrameLegacy.kt | 68 ++ .../runescape/animation/AnimationFrameMaya.kt | 13 + .../runescape/animation/AnimationLegacy.kt | 98 +++ .../runescape/animation/AnimationMaya.kt | 58 ++ .../runescape/animation/AnimationPlayer.kt | 2 +- .../scene/runescape/model/ModelSkeleton.kt | 109 ++- .../stan/qodat/util/osrs_anim_parser.kt | 168 ++--- 63 files changed, 2966 insertions(+), 380 deletions(-) create mode 100644 qodat-api/src/main/kotlin/qodat/cache/definition/AnimationFrameLegacyDefinition.kt create mode 100644 qodat-api/src/main/kotlin/qodat/cache/definition/AnimationMayaDefinition.kt create mode 100644 src/main/kotlin/jagex/AnimationState.java create mode 100644 src/main/kotlin/jagex/Bone.java create mode 100644 src/main/kotlin/jagex/BoneTransform.java create mode 100644 src/main/kotlin/jagex/Buffer.java create mode 100644 src/main/kotlin/jagex/KeyFrame.java create mode 100644 src/main/kotlin/jagex/MayaAnimation.java create mode 100644 src/main/kotlin/jagex/MayaAnimationFrame.java create mode 100644 src/main/kotlin/jagex/MayaAnimationFrameData.java create mode 100644 src/main/kotlin/jagex/MayaAnimationFrameFlag.java create mode 100644 src/main/kotlin/jagex/MayaAnimationFrameType.java create mode 100644 src/main/kotlin/jagex/MayaAnimationLoadFrameTask.java create mode 100644 src/main/kotlin/jagex/MayaAnimationLoadTask.java create mode 100644 src/main/kotlin/jagex/MayaAnimationSkeleton.java create mode 100644 src/main/kotlin/jagex/MouseWheel.java create mode 100644 src/main/kotlin/jagex/PolynomialSolver.java create mode 100644 src/main/kotlin/jagex/Quaternion.java create mode 100644 src/main/kotlin/jagex/Skeleton.java create mode 100644 src/main/kotlin/jagex/TransformationMatrix.java create mode 100644 src/main/kotlin/jagex/class121.java create mode 100644 src/main/kotlin/jagex/class130.java create mode 100644 src/main/kotlin/jagex/class134.java create mode 100644 src/main/kotlin/jagex/class277.java create mode 100644 src/main/kotlin/jagex/class4.java create mode 100644 src/main/kotlin/jagex/class415.java create mode 100644 src/main/kotlin/jagex/class461.java create mode 100644 src/main/kotlin/net/runelite/mapping/Export.java create mode 100644 src/main/kotlin/net/runelite/mapping/ObfuscatedGetter.java create mode 100644 src/main/kotlin/net/runelite/mapping/ObfuscatedName.java create mode 100644 src/main/kotlin/net/runelite/mapping/ObfuscatedSignature.java create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrameLegacy.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrameMaya.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationLegacy.kt create mode 100644 src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationMaya.kt diff --git a/qodat-api/src/main/kotlin/qodat/cache/Cache.kt b/qodat-api/src/main/kotlin/qodat/cache/Cache.kt index b794faa..361f7d2 100644 --- a/qodat-api/src/main/kotlin/qodat/cache/Cache.kt +++ b/qodat-api/src/main/kotlin/qodat/cache/Cache.kt @@ -49,7 +49,7 @@ abstract class Cache(val name: String) { abstract fun getAnimationSkeletonDefinition(frameHash: Int) : AnimationTransformationGroup - abstract fun getFrameDefinition(frameHash: Int) : AnimationFrameDefinition? + abstract fun getFrameDefinition(frameHash: Int) : AnimationFrameLegacyDefinition? abstract fun getInterface(groupId: Int): Array diff --git a/qodat-api/src/main/kotlin/qodat/cache/definition/AnimationFrameDefinition.kt b/qodat-api/src/main/kotlin/qodat/cache/definition/AnimationFrameDefinition.kt index 6afb9b1..be77ccd 100644 --- a/qodat-api/src/main/kotlin/qodat/cache/definition/AnimationFrameDefinition.kt +++ b/qodat-api/src/main/kotlin/qodat/cache/definition/AnimationFrameDefinition.kt @@ -1,18 +1,3 @@ package qodat.cache.definition -/** - * TODO: add documentation - * - * @author Stan van der Bend (https://www.rune-server.ee/members/StanDev/) - * @since 28/01/2021 - */ -interface AnimationFrameDefinition { - - val transformationCount : Int - val transformationGroupAccessIndices : IntArray - val transformationDeltaX : IntArray - val transformationDeltaY : IntArray - val transformationDeltaZ : IntArray - - val transformationGroup : AnimationTransformationGroup -} +interface AnimationFrameDefinition diff --git a/qodat-api/src/main/kotlin/qodat/cache/definition/AnimationFrameLegacyDefinition.kt b/qodat-api/src/main/kotlin/qodat/cache/definition/AnimationFrameLegacyDefinition.kt new file mode 100644 index 0000000..1ed3c14 --- /dev/null +++ b/qodat-api/src/main/kotlin/qodat/cache/definition/AnimationFrameLegacyDefinition.kt @@ -0,0 +1,18 @@ +package qodat.cache.definition + +/** + * TODO: add documentation + * + * @author Stan van der Bend (https://www.rune-server.ee/members/StanDev/) + * @since 28/01/2021 + */ +interface AnimationFrameLegacyDefinition : AnimationFrameDefinition { + + val transformationCount : Int + val transformationGroupAccessIndices : IntArray + val transformationDeltaX : IntArray + val transformationDeltaY : IntArray + val transformationDeltaZ : IntArray + + val transformationGroup : AnimationTransformationGroup +} diff --git a/qodat-api/src/main/kotlin/qodat/cache/definition/AnimationMayaDefinition.kt b/qodat-api/src/main/kotlin/qodat/cache/definition/AnimationMayaDefinition.kt new file mode 100644 index 0000000..1871d94 --- /dev/null +++ b/qodat-api/src/main/kotlin/qodat/cache/definition/AnimationMayaDefinition.kt @@ -0,0 +1,13 @@ +package qodat.cache.definition + +import net.runelite.cache.definitions.SequenceDefinition + +interface AnimationMayaDefinition : AnimationDefinition { + + val animMayaID : Int + val animMayaFrameSounds: Map + val animMayaStart: Int + val animMayaEnd: Int + val animMayaMasks: BooleanArray + +} diff --git a/qodat-api/src/main/kotlin/qodat/cache/models/RS2Model.java b/qodat-api/src/main/kotlin/qodat/cache/models/RS2Model.java index aa68d55..af2d97b 100644 --- a/qodat-api/src/main/kotlin/qodat/cache/models/RS2Model.java +++ b/qodat-api/src/main/kotlin/qodat/cache/models/RS2Model.java @@ -78,6 +78,9 @@ public class RS2Model implements ModelDefinition private transient int[][] vertexGroups; private transient int[][] faceGroups; + private int[][] animayaGroups; + private int[][] animayaScales; + /** * Computes the UV coordinates for every three-vertex face that has a @@ -580,6 +583,22 @@ public void setTexturePrimaryColors(short[] texturePrimaryColors) { this.texturePrimaryColors = texturePrimaryColors; } + public void setAnimayaGroups(int[][] animayaGroups) { + this.animayaGroups = animayaGroups; + } + + public void setAnimayaScales(int[][] animayaScales) { + this.animayaScales = animayaScales; + } + + public int[][] getAnimayaGroups() { + return animayaGroups; + } + + public int[][] getAnimayaScales() { + return animayaScales; + } + public void setPriority(byte priority) { this.priority = priority; } @@ -642,4 +661,4 @@ public void setTextureTriangleVertexIndices3(short[] textureTriangleVertexIndice this.textureTriangleVertexIndices3 = textureTriangleVertexIndices3; } -} \ No newline at end of file +} diff --git a/qodat-api/src/main/kotlin/qodat/cache/models/RSModelLoader.kt b/qodat-api/src/main/kotlin/qodat/cache/models/RSModelLoader.kt index 31a861b..45e9553 100644 --- a/qodat-api/src/main/kotlin/qodat/cache/models/RSModelLoader.kt +++ b/qodat-api/src/main/kotlin/qodat/cache/models/RSModelLoader.kt @@ -54,6 +54,9 @@ class RSModelLoader { setFaceTextureConfigs(it.textureCoords) faceRenderPriorities = it.faceRenderPriorities faceRenderTypes = it.faceRenderTypes + setAnimayaGroups(it.animayaGroups) + setAnimayaScales(it.animayaScales) + } } } catch (e: Exception) { diff --git a/src/main/kotlin/jagex/AnimationState.java b/src/main/kotlin/jagex/AnimationState.java new file mode 100644 index 0000000..d2508d4 --- /dev/null +++ b/src/main/kotlin/jagex/AnimationState.java @@ -0,0 +1,35 @@ +package jagex; + +public enum AnimationState implements MouseWheel { + + DEFAULT(0, 0), + CUSTOM1(1, 1), + CUSTOM2(2, 2), + REPEAT(3, 3), + MIRROR(4, 4); + + final int field1508; + final int field1507; + + AnimationState(int var3, int var4) { + this.field1508 = var3; + this.field1507 = var4; + } + + static AnimationState method2292(int var0) { + AnimationState var1 = (AnimationState)class4.findEnumerated(method2852(), var0); + if (var1 == null) { + var1 = DEFAULT; + } + + return var1; + } + + public int rsOrdinal() { + return this.field1507; + } + + static AnimationState[] method2852() { + return new AnimationState[]{AnimationState.DEFAULT, AnimationState.CUSTOM1, AnimationState.CUSTOM2, AnimationState.REPEAT, AnimationState.MIRROR}; + } +} diff --git a/src/main/kotlin/jagex/Bone.java b/src/main/kotlin/jagex/Bone.java new file mode 100644 index 0000000..afb4b73 --- /dev/null +++ b/src/main/kotlin/jagex/Bone.java @@ -0,0 +1,137 @@ +package jagex; + +public class Bone { + + public final int index; + public Bone parentBone; + float[][] rotations; + final BoneTransform[] boneTransforms; + BoneTransform[] computedBoneTransforms; + BoneTransform[] finalBoneTransforms; + BoneTransform baseTransform = new BoneTransform(); + boolean baseTransformUpdate = true; + BoneTransform globalTransform = new BoneTransform(); + boolean globalTransformUpdated = true; + BoneTransform localTransform = new BoneTransform(); + float[][] inverseBindMatrices; + float[][] bindMatrices; + float[][] positions; + + public Bone(int transformCount, Buffer buffer, boolean readTransforms) { + this.index = buffer.readShort(); + this.boneTransforms = new BoneTransform[transformCount]; + this.computedBoneTransforms = new BoneTransform[this.boneTransforms.length]; + this.finalBoneTransforms = new BoneTransform[this.boneTransforms.length]; + this.rotations = new float[this.boneTransforms.length][3]; + + for(int i = 0; i < this.boneTransforms.length; ++i) { + this.boneTransforms[i] = new BoneTransform(buffer, readTransforms); + this.rotations[i][0] = buffer.readIntAsFloat(); + this.rotations[i][1] = buffer.readIntAsFloat(); + this.rotations[i][2] = buffer.readIntAsFloat(); + } + + this.updateTransforms(); + } + + void updateTransforms() { + this.inverseBindMatrices = new float[this.boneTransforms.length][3]; + this.bindMatrices = new float[this.boneTransforms.length][3]; + this.positions = new float[this.boneTransforms.length][3]; + BoneTransform tempTransform; + synchronized(BoneTransform.classPool) { + if (BoneTransform.poolSize == 0) { + tempTransform = new BoneTransform(); + } else { + BoneTransform.classPool[--BoneTransform.poolSize].identityMatrix(); + tempTransform = BoneTransform.classPool[BoneTransform.poolSize]; + } + } + + BoneTransform transform = tempTransform; + + for(int i = 0; i < this.boneTransforms.length; ++i) { + BoneTransform bone = this.getBoneTransform(i); + transform.copy(bone); + transform.normalize(); + this.inverseBindMatrices[i] = transform.extractRotation(); + this.bindMatrices[i][0] = bone.matrix[12]; + this.bindMatrices[i][1] = bone.matrix[13]; + this.bindMatrices[i][2] = bone.matrix[14]; + this.positions[i] = bone.extractScale(); + } + + transform.release(); + } + + BoneTransform getBoneTransform(int index) { + return this.boneTransforms[index]; + } + + BoneTransform getComputedBoneTransform(int index) { + if (this.computedBoneTransforms[index] == null) { + this.computedBoneTransforms[index] = new BoneTransform(this.getBoneTransform(index)); + if (this.parentBone != null) { + this.computedBoneTransforms[index].combine(this.parentBone.getComputedBoneTransform(index)); + } else { + this.computedBoneTransforms[index].combine(BoneTransform.identity); + } + } + + return this.computedBoneTransforms[index]; + } + + BoneTransform getFinalBoneTransform(int index) { + if (this.finalBoneTransforms[index] == null) { + this.finalBoneTransforms[index] = new BoneTransform(this.getComputedBoneTransform(index)); + this.finalBoneTransforms[index].normalize(); + } + + return this.finalBoneTransforms[index]; + } + + void setBaseTransform(BoneTransform var1) { + this.baseTransform.copy(var1); + this.baseTransformUpdate = true; + this.globalTransformUpdated = true; + } + + BoneTransform getBaseTransform() { + return this.baseTransform; + } + + BoneTransform getGlobalTransform() { + if (this.baseTransformUpdate) { + this.globalTransform.copy(this.getBaseTransform()); + if (this.parentBone != null) { + this.globalTransform.combine(this.parentBone.getGlobalTransform()); + } + + this.baseTransformUpdate = false; + } + + return this.globalTransform; + } + + public BoneTransform getTransform(int index) { + if (this.globalTransformUpdated) { + this.localTransform.copy(this.getFinalBoneTransform(index)); + this.localTransform.combine(this.getGlobalTransform()); + this.globalTransformUpdated = false; + } + + return this.localTransform; + } + + float[] getInverseBindMatrix(int index) { + return this.inverseBindMatrices[index]; + } + + float[] getBindMatrix(int index) { + return this.bindMatrices[index]; + } + + float[] getPosition(int index) { + return this.positions[index]; + } +} diff --git a/src/main/kotlin/jagex/BoneTransform.java b/src/main/kotlin/jagex/BoneTransform.java new file mode 100644 index 0000000..06d77fd --- /dev/null +++ b/src/main/kotlin/jagex/BoneTransform.java @@ -0,0 +1,349 @@ +package jagex; + +import java.util.Arrays; + +public final class BoneTransform { + public static final BoneTransform identity; + public static BoneTransform[] classPool = new BoneTransform[0]; + static int maxPoolSize = 100; + public static int poolSize; + public float[] matrix = new float[16]; + + static { + classPool = new BoneTransform[100]; + poolSize = 0; + identity = new BoneTransform(); + } + + public BoneTransform() { + this.identityMatrix(); + } + + public BoneTransform(BoneTransform other) { + this.copy(other); + } + + public BoneTransform(Buffer buffer, boolean readOrientation) { + this.read(buffer, readOrientation); + } + + public void release() { + synchronized(classPool) { + if (poolSize < maxPoolSize - 1) { + classPool[++poolSize - 1] = this; + } + + } + } + + void read(Buffer buffer, boolean readOrientation) { + if (readOrientation) { + TransformationMatrix tempMatrix = new TransformationMatrix(); + + int xRotation = buffer.readShort(); + xRotation &= 16383; + float xRadians = (float)(6.283185307179586 * (double)((float)xRotation / 16384.0F)); + tempMatrix.rotateX(xRadians); + + int yRotation = buffer.readShort(); + yRotation &= 16383; + float yRadians = (float)(6.283185307179586 * (double)((float)yRotation / 16384.0F)); + tempMatrix.rotateY(yRadians); + + int zRotation = buffer.readShort(); + zRotation &= 16383; + float zRadians = (float)((double)((float)zRotation / 16384.0F) * 6.283185307179586); + tempMatrix.rotateZ(zRadians); + + float tx = buffer.readShort(); + float ty = buffer.readShort(); + float tz = buffer.readShort(); + tempMatrix.translate(tx, ty, tz); + + this.applyTransformation(tempMatrix); + } else { + for(int i = 0; i < 16; ++i) { + this.matrix[i] = buffer.readIntAsFloat(); + } + } + + } + + float[] extractEulerAngles() { + float[] eulerAngles = new float[3]; + if ((double)this.matrix[2] < 0.999 && (double)this.matrix[2] > -0.999) { + eulerAngles[1] = (float)(-Math.asin((double)this.matrix[2])); + double cosPitch = Math.cos((double)eulerAngles[1]); + eulerAngles[0] = (float)Math.atan2((double)this.matrix[6] / cosPitch, (double)this.matrix[10] / cosPitch); + eulerAngles[2] = (float)Math.atan2((double)this.matrix[1] / cosPitch, (double)this.matrix[0] / cosPitch); + } else { + eulerAngles[0] = 0.0F; + eulerAngles[1] = (float)Math.atan2((double)this.matrix[2], 0.0); + eulerAngles[2] = (float)Math.atan2((double)(-this.matrix[9]), (double)this.matrix[5]); + } + + return eulerAngles; + } + + public float[] extractRotation() { + float[] var1 = new float[]{(float)(-Math.asin((double)this.matrix[6])), 0.0F, 0.0F}; + double var2 = Math.cos((double)var1[0]); + double var4; + double var6; + if (Math.abs(var2) > 0.005) { + var4 = (double)this.matrix[2]; + var6 = (double)this.matrix[10]; + double var8 = (double)this.matrix[4]; + double var10 = (double)this.matrix[5]; + var1[1] = (float)Math.atan2(var4, var6); + var1[2] = (float)Math.atan2(var8, var10); + } else { + var4 = (double)this.matrix[1]; + var6 = (double)this.matrix[0]; + if (this.matrix[6] < 0.0F) { + var1[1] = (float)Math.atan2(var4, var6); + } else { + var1[1] = (float)(-Math.atan2(var4, var6)); + } + + var1[2] = 0.0F; + } + + return var1; + } + + public void identityMatrix() { + this.matrix[0] = 1.0F; + this.matrix[1] = 0.0F; + this.matrix[2] = 0.0F; + this.matrix[3] = 0.0F; + this.matrix[4] = 0.0F; + this.matrix[5] = 1.0F; + this.matrix[6] = 0.0F; + this.matrix[7] = 0.0F; + this.matrix[8] = 0.0F; + this.matrix[9] = 0.0F; + this.matrix[10] = 1.0F; + this.matrix[11] = 0.0F; + this.matrix[12] = 0.0F; + this.matrix[13] = 0.0F; + this.matrix[14] = 0.0F; + this.matrix[15] = 1.0F; + } + + public void zeroMatrix() { + this.matrix[0] = 0.0F; + this.matrix[1] = 0.0F; + this.matrix[2] = 0.0F; + this.matrix[3] = 0.0F; + this.matrix[4] = 0.0F; + this.matrix[5] = 0.0F; + this.matrix[6] = 0.0F; + this.matrix[7] = 0.0F; + this.matrix[8] = 0.0F; + this.matrix[9] = 0.0F; + this.matrix[10] = 0.0F; + this.matrix[11] = 0.0F; + this.matrix[12] = 0.0F; + this.matrix[13] = 0.0F; + this.matrix[14] = 0.0F; + this.matrix[15] = 0.0F; + } + + public void copy(BoneTransform var1) { + System.arraycopy(var1.matrix, 0, this.matrix, 0, 16); + } + + public void setUniformScale(float scale) { + this.setScale(scale, scale, scale); + } + + public void setScale(float scaleX, float scaleY, float scaleZ) { + this.identityMatrix(); + this.matrix[0] = scaleX; + this.matrix[5] = scaleY; + this.matrix[10] = scaleZ; + } + + public void addTransform(BoneTransform transform) { + for(int i = 0; i < this.matrix.length; ++i) { + float[] matrix = this.matrix; + matrix[i] += transform.matrix[i]; + } + } + + public void combine(BoneTransform transform) { + float var2 = transform.matrix[0] * this.matrix[0] + transform.matrix[4] * this.matrix[1] + transform.matrix[8] * this.matrix[2] + transform.matrix[12] * this.matrix[3]; + float var3 = this.matrix[3] * transform.matrix[13] + this.matrix[1] * transform.matrix[5] + this.matrix[0] * transform.matrix[1] + transform.matrix[9] * this.matrix[2]; + float var4 = this.matrix[3] * transform.matrix[14] + transform.matrix[2] * this.matrix[0] + transform.matrix[6] * this.matrix[1] + this.matrix[2] * transform.matrix[10]; + float var5 = this.matrix[3] * transform.matrix[15] + this.matrix[2] * transform.matrix[11] + this.matrix[0] * transform.matrix[3] + transform.matrix[7] * this.matrix[1]; + float var6 = this.matrix[7] * transform.matrix[12] + this.matrix[6] * transform.matrix[8] + transform.matrix[0] * this.matrix[4] + this.matrix[5] * transform.matrix[4]; + float var7 = this.matrix[5] * transform.matrix[5] + this.matrix[4] * transform.matrix[1] + transform.matrix[9] * this.matrix[6] + this.matrix[7] * transform.matrix[13]; + float var8 = transform.matrix[14] * this.matrix[7] + transform.matrix[10] * this.matrix[6] + transform.matrix[6] * this.matrix[5] + transform.matrix[2] * this.matrix[4]; + float var9 = transform.matrix[11] * this.matrix[6] + this.matrix[5] * transform.matrix[7] + this.matrix[4] * transform.matrix[3] + this.matrix[7] * transform.matrix[15]; + float var10 = this.matrix[9] * transform.matrix[4] + transform.matrix[0] * this.matrix[8] + this.matrix[10] * transform.matrix[8] + this.matrix[11] * transform.matrix[12]; + float var11 = transform.matrix[13] * this.matrix[11] + this.matrix[10] * transform.matrix[9] + transform.matrix[5] * this.matrix[9] + transform.matrix[1] * this.matrix[8]; + float var12 = this.matrix[11] * transform.matrix[14] + transform.matrix[6] * this.matrix[9] + transform.matrix[2] * this.matrix[8] + this.matrix[10] * transform.matrix[10]; + float var13 = transform.matrix[15] * this.matrix[11] + transform.matrix[3] * this.matrix[8] + this.matrix[9] * transform.matrix[7] + this.matrix[10] * transform.matrix[11]; + float var14 = this.matrix[14] * transform.matrix[8] + this.matrix[12] * transform.matrix[0] + this.matrix[13] * transform.matrix[4] + transform.matrix[12] * this.matrix[15]; + float var15 = transform.matrix[13] * this.matrix[15] + this.matrix[12] * transform.matrix[1] + this.matrix[13] * transform.matrix[5] + transform.matrix[9] * this.matrix[14]; + float var16 = this.matrix[15] * transform.matrix[14] + transform.matrix[2] * this.matrix[12] + transform.matrix[6] * this.matrix[13] + this.matrix[14] * transform.matrix[10]; + float var17 = this.matrix[15] * transform.matrix[15] + this.matrix[14] * transform.matrix[11] + this.matrix[13] * transform.matrix[7] + this.matrix[12] * transform.matrix[3]; + this.matrix[0] = var2; + this.matrix[1] = var3; + this.matrix[2] = var4; + this.matrix[3] = var5; + this.matrix[4] = var6; + this.matrix[5] = var7; + this.matrix[6] = var8; + this.matrix[7] = var9; + this.matrix[8] = var10; + this.matrix[9] = var11; + this.matrix[10] = var12; + this.matrix[11] = var13; + this.matrix[12] = var14; + this.matrix[13] = var15; + this.matrix[14] = var16; + this.matrix[15] = var17; + } + + public void applyQuaternion(Quaternion quaternion) { + float var2 = quaternion.qW * quaternion.qW; + float var3 = quaternion.qX * quaternion.qW; + float var4 = quaternion.qW * quaternion.qY; + float var5 = quaternion.qZ * quaternion.qW; + float var6 = quaternion.qX * quaternion.qX; + float var7 = quaternion.qY * quaternion.qX; + float var8 = quaternion.qX * quaternion.qZ; + float var9 = quaternion.qY * quaternion.qY; + float var10 = quaternion.qZ * quaternion.qY; + float var11 = quaternion.qZ * quaternion.qZ; + this.matrix[0] = var2 + var6 - var11 - var9; + this.matrix[1] = var5 + var5 + var7 + var7; + this.matrix[2] = var8 - var4 - var4 + var8; + this.matrix[4] = var7 + (var7 - var5 - var5); + this.matrix[5] = var2 + var9 - var6 - var11; + this.matrix[6] = var10 + var3 + var10 + var3; + this.matrix[8] = var4 + var8 + var8 + var4; + this.matrix[9] = var10 - var3 - var3 + var10; + this.matrix[10] = var2 + var11 - var9 - var6; + } + + void applyTransformation(TransformationMatrix other) { + this.matrix[0] = other.scaleX; + this.matrix[1] = other.skewYX; + this.matrix[2] = other.skewZX; + this.matrix[3] = 0.0F; + this.matrix[4] = other.skewXY; + this.matrix[5] = other.scaleY; + this.matrix[6] = other.skewZY; + this.matrix[7] = 0.0F; + this.matrix[8] = other.skewXZ; + this.matrix[9] = other.skewYZ; + this.matrix[10] = other.scaleZ; + this.matrix[11] = 0.0F; + this.matrix[12] = other.translateX; + this.matrix[13] = other.translateY; + this.matrix[14] = other.translateZ; + this.matrix[15] = 1.0F; + } + + float calculateDeterminant() { + return this.matrix[8] * this.matrix[5] * this.matrix[3] * this.matrix[14] + this.matrix[13] * this.matrix[10] * this.matrix[3] * this.matrix[4] + (this.matrix[8] * this.matrix[1] * this.matrix[6] * this.matrix[15] + this.matrix[14] * this.matrix[1] * this.matrix[4] * this.matrix[11] + (this.matrix[14] * this.matrix[9] * this.matrix[0] * this.matrix[7] + this.matrix[11] * this.matrix[6] * this.matrix[0] * this.matrix[13] + (this.matrix[15] * this.matrix[10] * this.matrix[5] * this.matrix[0] - this.matrix[11] * this.matrix[5] * this.matrix[0] * this.matrix[14] - this.matrix[15] * this.matrix[9] * this.matrix[0] * this.matrix[6]) - this.matrix[0] * this.matrix[7] * this.matrix[10] * this.matrix[13] - this.matrix[1] * this.matrix[4] * this.matrix[10] * this.matrix[15]) - this.matrix[12] * this.matrix[6] * this.matrix[1] * this.matrix[11] - this.matrix[8] * this.matrix[1] * this.matrix[7] * this.matrix[14] + this.matrix[12] * this.matrix[10] * this.matrix[7] * this.matrix[1] + this.matrix[2] * this.matrix[4] * this.matrix[9] * this.matrix[15] - this.matrix[13] * this.matrix[11] * this.matrix[4] * this.matrix[2] - this.matrix[2] * this.matrix[5] * this.matrix[8] * this.matrix[15] + this.matrix[12] * this.matrix[11] * this.matrix[5] * this.matrix[2] + this.matrix[13] * this.matrix[2] * this.matrix[7] * this.matrix[8] - this.matrix[7] * this.matrix[2] * this.matrix[9] * this.matrix[12] - this.matrix[14] * this.matrix[4] * this.matrix[3] * this.matrix[9]) - this.matrix[3] * this.matrix[5] * this.matrix[10] * this.matrix[12] - this.matrix[13] * this.matrix[3] * this.matrix[6] * this.matrix[8] + this.matrix[9] * this.matrix[6] * this.matrix[3] * this.matrix[12]; + } + + public void normalize() { + float var1 = 1.0F / this.calculateDeterminant(); + float var2 = var1 * (this.matrix[13] * this.matrix[6] * this.matrix[11] + (this.matrix[10] * this.matrix[5] * this.matrix[15] - this.matrix[14] * this.matrix[11] * this.matrix[5] - this.matrix[6] * this.matrix[9] * this.matrix[15]) + this.matrix[9] * this.matrix[7] * this.matrix[14] - this.matrix[13] * this.matrix[10] * this.matrix[7]); + float var3 = var1 * (this.matrix[13] * this.matrix[10] * this.matrix[3] + (this.matrix[10] * -this.matrix[1] * this.matrix[15] + this.matrix[14] * this.matrix[11] * this.matrix[1] + this.matrix[9] * this.matrix[2] * this.matrix[15] - this.matrix[13] * this.matrix[11] * this.matrix[2] - this.matrix[3] * this.matrix[9] * this.matrix[14])); + float var4 = (this.matrix[15] * this.matrix[1] * this.matrix[6] - this.matrix[14] * this.matrix[7] * this.matrix[1] - this.matrix[2] * this.matrix[5] * this.matrix[15] + this.matrix[2] * this.matrix[7] * this.matrix[13] + this.matrix[5] * this.matrix[3] * this.matrix[14] - this.matrix[13] * this.matrix[6] * this.matrix[3]) * var1; + float var5 = (this.matrix[9] * this.matrix[6] * this.matrix[3] + (this.matrix[2] * this.matrix[5] * this.matrix[11] + this.matrix[1] * this.matrix[7] * this.matrix[10] + this.matrix[11] * this.matrix[6] * -this.matrix[1] - this.matrix[9] * this.matrix[7] * this.matrix[2] - this.matrix[3] * this.matrix[5] * this.matrix[10])) * var1; + float var6 = (this.matrix[6] * this.matrix[8] * this.matrix[15] + this.matrix[15] * -this.matrix[4] * this.matrix[10] + this.matrix[14] * this.matrix[4] * this.matrix[11] - this.matrix[12] * this.matrix[11] * this.matrix[6] - this.matrix[7] * this.matrix[8] * this.matrix[14] + this.matrix[12] * this.matrix[10] * this.matrix[7]) * var1; + float var7 = var1 * (this.matrix[8] * this.matrix[3] * this.matrix[14] + this.matrix[2] * this.matrix[11] * this.matrix[12] + (this.matrix[10] * this.matrix[0] * this.matrix[15] - this.matrix[0] * this.matrix[11] * this.matrix[14] - this.matrix[15] * this.matrix[8] * this.matrix[2]) - this.matrix[3] * this.matrix[10] * this.matrix[12]); + float var8 = var1 * (this.matrix[2] * this.matrix[4] * this.matrix[15] + this.matrix[15] * this.matrix[6] * -this.matrix[0] + this.matrix[7] * this.matrix[0] * this.matrix[14] - this.matrix[12] * this.matrix[7] * this.matrix[2] - this.matrix[14] * this.matrix[3] * this.matrix[4] + this.matrix[3] * this.matrix[6] * this.matrix[12]); + float var9 = (this.matrix[4] * this.matrix[3] * this.matrix[10] + this.matrix[11] * this.matrix[0] * this.matrix[6] - this.matrix[10] * this.matrix[7] * this.matrix[0] - this.matrix[11] * this.matrix[4] * this.matrix[2] + this.matrix[8] * this.matrix[2] * this.matrix[7] - this.matrix[8] * this.matrix[3] * this.matrix[6]) * var1; + float var10 = (this.matrix[13] * this.matrix[7] * this.matrix[8] + this.matrix[11] * this.matrix[5] * this.matrix[12] + (this.matrix[9] * this.matrix[4] * this.matrix[15] - this.matrix[4] * this.matrix[11] * this.matrix[13] - this.matrix[15] * this.matrix[5] * this.matrix[8]) - this.matrix[12] * this.matrix[7] * this.matrix[9]) * var1; + float var11 = (this.matrix[3] * this.matrix[9] * this.matrix[12] + (this.matrix[8] * this.matrix[1] * this.matrix[15] + this.matrix[9] * -this.matrix[0] * this.matrix[15] + this.matrix[11] * this.matrix[0] * this.matrix[13] - this.matrix[1] * this.matrix[11] * this.matrix[12] - this.matrix[13] * this.matrix[3] * this.matrix[8])) * var1; + float var12 = (this.matrix[15] * this.matrix[5] * this.matrix[0] - this.matrix[0] * this.matrix[7] * this.matrix[13] - this.matrix[1] * this.matrix[4] * this.matrix[15] + this.matrix[1] * this.matrix[7] * this.matrix[12] + this.matrix[13] * this.matrix[4] * this.matrix[3] - this.matrix[12] * this.matrix[5] * this.matrix[3]) * var1; + float var13 = var1 * (this.matrix[3] * this.matrix[5] * this.matrix[8] + (this.matrix[11] * this.matrix[4] * this.matrix[1] + -this.matrix[0] * this.matrix[5] * this.matrix[11] + this.matrix[7] * this.matrix[0] * this.matrix[9] - this.matrix[1] * this.matrix[7] * this.matrix[8] - this.matrix[3] * this.matrix[4] * this.matrix[9])); + float var14 = var1 * (this.matrix[14] * this.matrix[9] * -this.matrix[4] + this.matrix[13] * this.matrix[4] * this.matrix[10] + this.matrix[8] * this.matrix[5] * this.matrix[14] - this.matrix[12] * this.matrix[10] * this.matrix[5] - this.matrix[8] * this.matrix[6] * this.matrix[13] + this.matrix[12] * this.matrix[9] * this.matrix[6]); + float var15 = (this.matrix[14] * this.matrix[0] * this.matrix[9] - this.matrix[13] * this.matrix[0] * this.matrix[10] - this.matrix[14] * this.matrix[8] * this.matrix[1] + this.matrix[12] * this.matrix[1] * this.matrix[10] + this.matrix[2] * this.matrix[8] * this.matrix[13] - this.matrix[12] * this.matrix[2] * this.matrix[9]) * var1; + float var16 = var1 * (this.matrix[14] * this.matrix[1] * this.matrix[4] + this.matrix[5] * -this.matrix[0] * this.matrix[14] + this.matrix[6] * this.matrix[0] * this.matrix[13] - this.matrix[12] * this.matrix[6] * this.matrix[1] - this.matrix[4] * this.matrix[2] * this.matrix[13] + this.matrix[12] * this.matrix[2] * this.matrix[5]); + float var17 = (this.matrix[9] * this.matrix[2] * this.matrix[4] + this.matrix[1] * this.matrix[6] * this.matrix[8] + (this.matrix[5] * this.matrix[0] * this.matrix[10] - this.matrix[0] * this.matrix[6] * this.matrix[9] - this.matrix[4] * this.matrix[1] * this.matrix[10]) - this.matrix[2] * this.matrix[5] * this.matrix[8]) * var1; + this.matrix[0] = var2; + this.matrix[1] = var3; + this.matrix[2] = var4; + this.matrix[3] = var5; + this.matrix[4] = var6; + this.matrix[5] = var7; + this.matrix[6] = var8; + this.matrix[7] = var9; + this.matrix[8] = var10; + this.matrix[9] = var11; + this.matrix[10] = var12; + this.matrix[11] = var13; + this.matrix[12] = var14; + this.matrix[13] = var15; + this.matrix[14] = var16; + this.matrix[15] = var17; + } + + public float[] extractScale() { + float[] var1 = new float[3]; + class415 var2 = new class415(this.matrix[0], this.matrix[1], this.matrix[2]); + class415 var3 = new class415(this.matrix[4], this.matrix[5], this.matrix[6]); + class415 var4 = new class415(this.matrix[8], this.matrix[9], this.matrix[10]); + var1[0] = var2.method7872(); + var1[1] = var3.method7872(); + var1[2] = var4.method7872(); + return var1; + } + + public String toString() { + StringBuilder var1 = new StringBuilder(); + this.extractRotation(); + this.extractEulerAngles(); + + for(int var2 = 0; var2 < 4; ++var2) { + for(int var3 = 0; var3 < 4; ++var3) { + if (var3 > 0) { + var1.append("\t"); + } + + float var4 = this.matrix[var3 + var2 * 4]; + if (Math.sqrt((double)(var4 * var4)) < 9.999999747378752E-5) { + var4 = 0.0F; + } + + var1.append(var4); + } + + var1.append("\n"); + } + + return var1.toString(); + } + + public int hashCode() { + boolean var1 = true; + int var2 = 1; + var2 = var2 * 31 + Arrays.hashCode(this.matrix); + return var2; + } + + public boolean equals(Object other) { + if (!(other instanceof BoneTransform)) { + return false; + } else { + BoneTransform otherTransform = (BoneTransform)other; + + for(int var3 = 0; var3 < 16; ++var3) { + if (otherTransform.matrix[var3] != this.matrix[var3]) { + return false; + } + } + + return true; + } + } +} diff --git a/src/main/kotlin/jagex/Buffer.java b/src/main/kotlin/jagex/Buffer.java new file mode 100644 index 0000000..6b65c86 --- /dev/null +++ b/src/main/kotlin/jagex/Buffer.java @@ -0,0 +1,52 @@ +package jagex; + +public class Buffer { + public byte[] array; + public int offset; + + public Buffer(byte[] var1) { + this.array = var1; // L: 61 + this.offset = 0; // L: 62 + } // L: 63 + + + public byte readByte() { + return array[offset++]; + } + + public int readUnsignedByte() { + return readByte() & 255; // L: 242 + } + + public int readShort() { + this.offset += 2; // L: 255 + int var1 = (this.array[this.offset - 1] & 255) + ((this.array[this.offset - 2] & 255) << 8); // L: 256 + if (var1 > 32767) { // L: 257 + var1 -= 65536; + } + return var1; // L: 258 + } + + public int readUnsignedShort() { + this.offset += 2; // L: 250 + return (this.array[this.offset - 1] & 255) + ((this.array[this.offset - 2] & 255) << 8); // L: 251 + } + + public int readShortSmart() { + int var1 = this.array[this.offset] & 255; // L: 369 + return var1 < 128 ? this.readUnsignedByte() - 64 : this.readUnsignedShort() - 49152; // L: 370 371 + } + + public int readInt() { + this.offset += 4; // L: 267 + return ((this.array[this.offset - 3] & 255) << 16) + (this.array[this.offset - 1] & 255) + ((this.array[this.offset - 2] & 255) << 8) + ((this.array[this.offset - 4] & 255) << 24); // L: 268 + } + + public float readFloat() { + return Float.intBitsToFloat(this.readInt()); // L: 278 + } + + public float readIntAsFloat() { + return Float.intBitsToFloat(this.readInt()); + } +} diff --git a/src/main/kotlin/jagex/KeyFrame.java b/src/main/kotlin/jagex/KeyFrame.java new file mode 100644 index 0000000..d87a676 --- /dev/null +++ b/src/main/kotlin/jagex/KeyFrame.java @@ -0,0 +1,23 @@ +package jagex; + +public class KeyFrame { + int frameNumber; + float value; + float field1469 = Float.MAX_VALUE; + float field1470 = Float.MAX_VALUE; + float controlPoint1 = Float.MAX_VALUE; + float controlPoint2 = Float.MAX_VALUE; + KeyFrame next; + + KeyFrame() { + } + + void read(Buffer var1, int var2) { + this.frameNumber = var1.readShort(); + this.value = var1.readIntAsFloat(); + this.field1469 = var1.readIntAsFloat(); + this.field1470 = var1.readIntAsFloat(); + this.controlPoint1 = var1.readIntAsFloat(); + this.controlPoint2 = var1.readIntAsFloat(); + } +} diff --git a/src/main/kotlin/jagex/MayaAnimation.java b/src/main/kotlin/jagex/MayaAnimation.java new file mode 100644 index 0000000..78dd2e0 --- /dev/null +++ b/src/main/kotlin/jagex/MayaAnimation.java @@ -0,0 +1,376 @@ +package jagex; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import net.runelite.cache.fs.Archive; +import net.runelite.cache.fs.ArchiveFiles; +import net.runelite.cache.fs.FSFile; +import net.runelite.cache.fs.Index; +import stan.qodat.cache.impl.oldschool.OldschoolCacheRuneLite; + +public class MayaAnimation { + int id; + public MayaAnimationFrame[][] primaryFrames = null; + public MayaAnimationFrame[][] secondaryFrames = null; + public Skeleton skeleton; + int totalDuration = 0; + boolean hasTransformations; + Future animationLoadTask; + List frameLoadTask; + + public static MayaAnimation loadMayaAnimation(Index seqs, Index frames, int mayaAnimationId, boolean fileIndexOrGroupIndex) throws IOException { + boolean var4 = true; + final Archive seqArchive = seqs.getArchive(mayaAnimationId >> 16 & '\uffff'); + final byte[] seqData = OldschoolCacheRuneLite.INSTANCE.getStore().getStorage().loadArchive(seqArchive); + final ArchiveFiles seqFiles = seqArchive.getFiles(seqData); + final FSFile seqFile = seqFiles.findFile(mayaAnimationId & '\uffff'); + + byte[] mayaAnimationData = seqFile.getContents(); + int mayaAnimationFrameGroupId = (mayaAnimationData[1] & 255) << 8 | mayaAnimationData[2] & 255; + byte[] mayaAnimationFrameGroupData; + if (fileIndexOrGroupIndex) { + + final Archive frameArchive = frames.getArchive(0); + final byte[] frameData = OldschoolCacheRuneLite.INSTANCE.getStore().getStorage().loadArchive(frameArchive); + final ArchiveFiles frameFiles = frameArchive.getFiles(frameData); + mayaAnimationFrameGroupData = frameFiles.findFile(mayaAnimationFrameGroupId).getContents(); + } else { + final Archive frameArchive = frames.getArchive(mayaAnimationFrameGroupId); + final byte[] frameData = OldschoolCacheRuneLite.INSTANCE.getStore().getStorage().loadArchive(frameArchive); + final ArchiveFiles frameFiles = frameArchive.getFiles(frameData); + mayaAnimationFrameGroupData = frameFiles.findFile(0).getContents(); + } + + if (mayaAnimationFrameGroupData == null) { + var4 = false; + } + + if (!var4) { + return null; + } else { + if (class277.threadPoolExecutor == null) { + class461.threadPoolExecutorThreadCount = Runtime.getRuntime().availableProcessors(); + class277.threadPoolExecutor = new ThreadPoolExecutor(0, class461.threadPoolExecutorThreadCount, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue(class461.threadPoolExecutorThreadCount * 100 + 100), new class130()); + } + + try { + return new MayaAnimation(seqs, frames, mayaAnimationId, fileIndexOrGroupIndex); + } catch (Exception var9) { + var9.printStackTrace(); + return null; + } + } + } + + MayaAnimation(Index seqs, Index frames, int mayaAnimationId, boolean fileIndexOrGroupIndex) throws IOException { + this.id = mayaAnimationId; + + final Archive seqArchive = seqs.getArchive(this.id >> 16 & '\uffff'); + final byte[] seqData = OldschoolCacheRuneLite.INSTANCE.getStore().getStorage().loadArchive(seqArchive); + final ArchiveFiles seqFiles = seqArchive.getFiles(seqData); + final FSFile seqFile = seqFiles.findFile(this.id & '\uffff'); + + byte[] data = seqFile.getContents(); + Buffer buffer = new Buffer(data); + int version = buffer.readUnsignedByte(); + int frameGroupId = buffer.readUnsignedShort(); + byte[] frameGroupData; + if (fileIndexOrGroupIndex) { + final Archive frameArchive = frames.getArchive(0); + final byte[] frameData = OldschoolCacheRuneLite.INSTANCE.getStore().getStorage().loadArchive(frameArchive); + final ArchiveFiles frameFiles = frameArchive.getFiles(frameData); + frameGroupData = frameFiles.findFile(frameGroupId).getContents(); + } else { + final Archive frameArchive = frames.getArchive(frameGroupId); + final byte[] frameData = OldschoolCacheRuneLite.INSTANCE.getStore().getStorage().loadArchive(frameArchive); + final ArchiveFiles frameFiles = frameArchive.getFiles(frameData); + frameGroupData = frameFiles.findFile(0).getContents(); + } + + this.skeleton = new Skeleton(frameGroupId, frameGroupData); + this.frameLoadTask = new ArrayList<>(); + this.animationLoadTask = class277.threadPoolExecutor.submit(new MayaAnimationLoadTask(this, buffer, version)); + } + + void read(Buffer buffer, int version) { + buffer.readUnsignedShort(); + buffer.readUnsignedShort(); + this.totalDuration = buffer.readUnsignedByte(); + int frameCount = buffer.readUnsignedShort(); + this.secondaryFrames = new MayaAnimationFrame[this.skeleton.getMayaAnimationSkeleton().frameCount()][]; + this.primaryFrames = new MayaAnimationFrame[this.skeleton.getCount()][]; + MayaAnimationFrameData[] frameData = new MayaAnimationFrameData[frameCount]; + + int i; + int frameType; + int index; + for(i = 0; i < frameCount; ++i) { + frameType = buffer.readUnsignedByte(); + MayaAnimationFrameType[] animationTypes = new MayaAnimationFrameType[]{MayaAnimationFrameType.DEFAULT, MayaAnimationFrameType.field1548, MayaAnimationFrameType.field1555, MayaAnimationFrameType.field1550, MayaAnimationFrameType.TRANSFORMATION, MayaAnimationFrameType.field1551}; + MayaAnimationFrameType animationType = (MayaAnimationFrameType)class4.findEnumerated(animationTypes, frameType); + if (animationType == null) { + animationType = MayaAnimationFrameType.DEFAULT; + } + + index = buffer.readShortSmart(); + int frameFlag = buffer.readUnsignedByte(); + MayaAnimationFrameFlag flag = (MayaAnimationFrameFlag)class4.findEnumerated(MayaAnimationFrameFlag.values(), frameFlag); + if (flag == null) { + flag = MayaAnimationFrameFlag.NORMAL; + } + + MayaAnimationFrame animationFrame = new MayaAnimationFrame(); + animationFrame.read(buffer, version); + frameData[i] = new MayaAnimationFrameData(this, animationFrame, animationType, flag, index); + int maxIndex = animationType.getMaxIndex(); + MayaAnimationFrame[][] frames; + if (animationType == MayaAnimationFrameType.field1548) { + frames = this.secondaryFrames; + } else { + frames = this.primaryFrames; + } + + if (frames[index] == null) { + frames[index] = new MayaAnimationFrame[maxIndex]; + } + + if (animationType == MayaAnimationFrameType.TRANSFORMATION) { + this.hasTransformations = true; + } + } + + i = frameCount / class461.threadPoolExecutorThreadCount; + int remainder = frameCount % class461.threadPoolExecutorThreadCount; + int start = 0; + + for(index = 0; index < class461.threadPoolExecutorThreadCount; ++index) { + frameType = start; + start += i; + if (remainder > 0) { + ++start; + --remainder; + } + + if (start == frameType) { + break; + } + + this.frameLoadTask.add(class277.threadPoolExecutor.submit(new MayaAnimationLoadFrameTask(this, frameType, start, frameData))); + } + } + + public boolean isAnimationLoaded() { + if (this.animationLoadTask == null && this.frameLoadTask == null) { + return true; + } else { + if (this.animationLoadTask != null) { + if (!this.animationLoadTask.isDone()) { + return false; + } + + this.animationLoadTask = null; + } + + boolean allTasksCompleted = true; + + for(int var2 = 0; var2 < this.frameLoadTask.size(); ++var2) { + if (!((Future)this.frameLoadTask.get(var2)).isDone()) { + allTasksCompleted = false; + } else { + this.frameLoadTask.remove(var2); + --var2; + } + } + + if (!allTasksCompleted) { + return false; + } else { + this.frameLoadTask = null; + return true; + } + } + } + + public int getDuration() { + return this.totalDuration; + } + + public boolean hasTransformations() { + return this.hasTransformations; + } + + public void apply(int animationStep, Bone bone, int frameIndex, int var4) { + BoneTransform boneTransform; + synchronized(BoneTransform.classPool) { + if (BoneTransform.poolSize == 0) { + boneTransform = new BoneTransform(); + } else { + BoneTransform.classPool[--BoneTransform.poolSize].identityMatrix(); + boneTransform = BoneTransform.classPool[BoneTransform.poolSize]; + } + } + + this.calculateBoneTransform(boneTransform, frameIndex, bone, animationStep); + this.adjustBonePosition(boneTransform, frameIndex, bone, animationStep); + this.adjustBoneScale(boneTransform, frameIndex, bone, animationStep); + bone.setBaseTransform(boneTransform); + boneTransform.release(); + } + + void calculateBoneTransform(BoneTransform boneTransform, int frameIndex, Bone bone, int animationStep) { + float[] rotationAnglex = bone.getInverseBindMatrix(this.totalDuration); + float xRotation = rotationAnglex[0]; + float yRotation = rotationAnglex[1]; + float zRotation = rotationAnglex[2]; + if (this.secondaryFrames[frameIndex] != null) { + MayaAnimationFrame xFrame = this.secondaryFrames[frameIndex][0]; + MayaAnimationFrame yFrame = this.secondaryFrames[frameIndex][1]; + MayaAnimationFrame zFrame = this.secondaryFrames[frameIndex][2]; + if (xFrame != null) { + xRotation = xFrame.evaluate(animationStep); + } + + if (yFrame != null) { + yRotation = yFrame.evaluate(animationStep); + } + + if (zFrame != null) { + zRotation = zFrame.evaluate(animationStep); + } + } + + Quaternion quaternionX; + synchronized(Quaternion.pool) { + if (Quaternion.poolSize == 0) { + quaternionX = new Quaternion(); + } else { + Quaternion.pool[--Quaternion.poolSize].reset(); + quaternionX = Quaternion.pool[Quaternion.poolSize]; + } + } + + quaternionX.setFromAxisAngle(1.0F, 0.0F, 0.0F, xRotation); + Quaternion quaternionY; + synchronized(Quaternion.pool) { + if (Quaternion.poolSize == 0) { + quaternionY = new Quaternion(); + } else { + Quaternion.pool[--Quaternion.poolSize].reset(); + quaternionY = Quaternion.pool[Quaternion.poolSize]; + } + } + + quaternionY.setFromAxisAngle(0.0F, 1.0F, 0.0F, yRotation); + Quaternion quaternionZ; + synchronized(Quaternion.pool) { + if (Quaternion.poolSize == 0) { + quaternionZ = new Quaternion(); + } else { + Quaternion.pool[--Quaternion.poolSize].reset(); + quaternionZ = Quaternion.pool[Quaternion.poolSize]; + } + } + + quaternionZ.setFromAxisAngle(0.0F, 0.0F, 1.0F, zRotation); + Quaternion combinedQuaternion; + synchronized(Quaternion.pool) { + if (Quaternion.poolSize == 0) { + combinedQuaternion = new Quaternion(); + } else { + Quaternion.pool[--Quaternion.poolSize].reset(); + combinedQuaternion = Quaternion.pool[Quaternion.poolSize]; + } + } + + combinedQuaternion.multiply(quaternionZ); + combinedQuaternion.multiply(quaternionX); + combinedQuaternion.multiply(quaternionY); + BoneTransform temporaryTransform; + synchronized(BoneTransform.classPool) { + if (BoneTransform.poolSize == 0) { + temporaryTransform = new BoneTransform(); + } else { + BoneTransform.classPool[--BoneTransform.poolSize].identityMatrix(); + temporaryTransform = BoneTransform.classPool[BoneTransform.poolSize]; + } + } + + temporaryTransform.applyQuaternion(combinedQuaternion); + boneTransform.combine(temporaryTransform); + quaternionX.release(); + quaternionY.release(); + quaternionZ.release(); + combinedQuaternion.release(); + temporaryTransform.release(); + } + + void adjustBoneScale(BoneTransform transform, int frameIndex, Bone bone, int animationStep) { + float[] scaleFactors = bone.getBindMatrix(this.totalDuration); + float scaleX = scaleFactors[0]; + float scaleY = scaleFactors[1]; + float scaleZ = scaleFactors[2]; + if (this.secondaryFrames[frameIndex] != null) { + MayaAnimationFrame scaleFrameX = this.secondaryFrames[frameIndex][3]; + MayaAnimationFrame scaleFrameY = this.secondaryFrames[frameIndex][4]; + MayaAnimationFrame scaleFrameZ = this.secondaryFrames[frameIndex][5]; + if (scaleFrameX != null) { + scaleX = scaleFrameX.evaluate(animationStep); + } + + if (scaleFrameY != null) { + scaleY = scaleFrameY.evaluate(animationStep); + } + + if (scaleFrameZ != null) { + scaleZ = scaleFrameZ.evaluate(animationStep); + } + } + + transform.matrix[12] = scaleX; + transform.matrix[13] = scaleY; + transform.matrix[14] = scaleZ; + } + + void adjustBonePosition(BoneTransform transform, int frameIndex, Bone bone, int animationStep) { + float[] position = bone.getPosition(this.totalDuration); + float positionX = position[0]; + float positionY = position[1]; + float positionZ = position[2]; + if (this.secondaryFrames[frameIndex] != null) { + MayaAnimationFrame posXFrame = this.secondaryFrames[frameIndex][6]; + MayaAnimationFrame posYFrame = this.secondaryFrames[frameIndex][7]; + MayaAnimationFrame posZFrame = this.secondaryFrames[frameIndex][8]; + if (posXFrame != null) { + positionX = posXFrame.evaluate(animationStep); + } + + if (posYFrame != null) { + positionY = posYFrame.evaluate(animationStep); + } + + if (posZFrame != null) { + positionZ = posZFrame.evaluate(animationStep); + } + } + + BoneTransform positionTransform; + synchronized(BoneTransform.classPool) { + if (BoneTransform.poolSize == 0) { + positionTransform = new BoneTransform(); + } else { + BoneTransform.classPool[--BoneTransform.poolSize].identityMatrix(); + positionTransform = BoneTransform.classPool[BoneTransform.poolSize]; + } + } + + positionTransform.setScale(positionX, positionY, positionZ); + transform.combine(positionTransform); + positionTransform.release(); + } +} diff --git a/src/main/kotlin/jagex/MayaAnimationFrame.java b/src/main/kotlin/jagex/MayaAnimationFrame.java new file mode 100644 index 0000000..bfd5f4d --- /dev/null +++ b/src/main/kotlin/jagex/MayaAnimationFrame.java @@ -0,0 +1,696 @@ +package jagex; + +import qodat.cache.definition.AnimationFrameDefinition; + +public class MayaAnimationFrame implements AnimationFrameDefinition { + + boolean isInitialised; + boolean isProcessed; + AnimationState initialAnimationState; + AnimationState finalAnimationState; + KeyFrame[] keyFrames; + boolean isActive; + float initialX; + float field1527; + float startY; + float controlW; + float controlZ; + float controlY; + float adjustedX; + float adjustedW; + float adjustedZ; + float adjustedY; + boolean needsUpdate = true; + int currentKeyframeIndex = 0; + float[] frameValues; + int startFrame; + int endFrame; + float lowerBoundFrameValue; + float upperBoundFrameValue; + + MayaAnimationFrame() { + } + + static class134 validate(int var0) { + class134[] var1 = new class134[]{class134.field1621, class134.field1607, class134.field1608, class134.field1609, class134.field1610, class134.field1617, class134.field1612, class134.field1611, class134.field1614}; + class134 var2 = (class134)class4.findEnumerated(var1, var0); + if (var2 == null) { + var2 = class134.field1614; + } + + return var2; + } + + static float calculateFrameValue(MayaAnimationFrame frame, float currentFrame) { + if (frame != null && frame.getLastFrameIndex() != 0) { + if (currentFrame < (float)frame.keyFrames[0].frameNumber) { + return frame.initialAnimationState == AnimationState.DEFAULT ? frame.keyFrames[0].value : interpolate(frame, currentFrame, true); + } else if (currentFrame > (float)frame.keyFrames[frame.getLastFrameIndex() - 1].frameNumber) { + return frame.finalAnimationState == AnimationState.DEFAULT ? frame.keyFrames[frame.getLastFrameIndex() - 1].value : interpolate(frame, currentFrame, false); + } else if (frame.isProcessed) { + return frame.keyFrames[0].value; + } else { + KeyFrame keyFrame = frame.getKeyFrame(currentFrame); + boolean isLinearInterpolation = false; + boolean isInfiniteControlPoints = false; + if (keyFrame == null) { + return 0.0F; + } else { + if (0.0 == (double)keyFrame.controlPoint1 && 0.0 == (double)keyFrame.controlPoint2) { + isLinearInterpolation = true; + } else if (keyFrame.controlPoint1 == Float.MAX_VALUE && keyFrame.controlPoint2 == Float.MAX_VALUE) { + isInfiniteControlPoints = true; + } else if (keyFrame.next != null) { + if (frame.needsUpdate) { + float frameStart = (float)keyFrame.frameNumber; + float valueStart = keyFrame.value; + float controlPointsStart = frameStart + 0.33333334F * keyFrame.controlPoint1; + float valueControlStart = keyFrame.controlPoint2 * 0.33333334F + valueStart; + float frameEnd = (float)keyFrame.next.frameNumber; + float valueEnd = keyFrame.next.value; + float controlPointEnd = frameEnd - 0.33333334F * keyFrame.next.field1469; + float valueControlEnd = valueEnd - keyFrame.next.field1470 * 0.33333334F; + if (frame.isInitialised) { + float interpolatedStart = valueControlStart; + float interpolatedEnd = valueControlEnd; + if (frame != null) { + float frameLength = frameEnd - frameStart; + if (0.0 != (double)frameLength) { + float normalizedStart = controlPointsStart - frameStart; + float normalizedEnd = controlPointEnd - frameStart; + float[] normalizedValues = new float[]{normalizedStart / frameLength, normalizedEnd / frameLength}; + frame.isActive = normalizedValues[0] == 0.33333334F && normalizedValues[1] == 0.6666667F; + float var21 = normalizedValues[0]; + float var22 = normalizedValues[1]; + if ((double)normalizedValues[0] < 0.0) { + normalizedValues[0] = 0.0F; + } + + if ((double)normalizedValues[1] > 1.0) { + normalizedValues[1] = 1.0F; + } + + if ((double)normalizedValues[0] > 1.0 || normalizedValues[1] < -1.0F) { + normalizedValues[1] = 1.0F - normalizedValues[1]; + if (normalizedValues[0] < 0.0F) { + normalizedValues[0] = 0.0F; + } + + if (normalizedValues[1] < 0.0F) { + normalizedValues[1] = 0.0F; + } + + if (normalizedValues[0] > 1.0F || normalizedValues[1] > 1.0F) { + float var23 = (float)(((double)normalizedValues[1] - 2.0) * (double)normalizedValues[1] + (double)((normalizedValues[1] + (normalizedValues[0] - 2.0F)) * normalizedValues[0]) + 1.0); + if (var23 + class121.field1479 > 0.0F) { + if (class121.field1479 + normalizedValues[0] < 1.3333334F) { + float var24 = normalizedValues[0] - 2.0F; + float var25 = normalizedValues[0] - 1.0F; + float var26 = (float)Math.sqrt((double)(var24 * var24 - 4.0F * var25 * var25)); + float var27 = 0.5F * (-var24 + var26); + if (normalizedValues[1] + class121.field1479 > var27) { + normalizedValues[1] = var27 - class121.field1479; + } else { + var27 = (-var24 - var26) * 0.5F; + if (normalizedValues[1] < var27 + class121.field1479) { + normalizedValues[1] = class121.field1479 + var27; + } + } + } else { + normalizedValues[0] = 1.3333334F - class121.field1479; + normalizedValues[1] = 0.33333334F - class121.field1479; + } + } + } + + normalizedValues[1] = 1.0F - normalizedValues[1]; + } + + float var10000; + if (var21 != normalizedValues[0]) { + var10000 = frameStart + frameLength * normalizedValues[0]; + if ((double)var21 != 0.0) { + interpolatedStart = valueStart + (valueControlStart - valueStart) * normalizedValues[0] / var21; + } + } + + if (var22 != normalizedValues[1]) { + var10000 = frameStart + frameLength * normalizedValues[1]; + if ((double)var22 != 1.0) { + interpolatedEnd = (float)((double)valueEnd - (1.0 - (double)normalizedValues[1]) * (double)(valueEnd - valueControlEnd) / (1.0 - (double)var22)); + } + } + + frame.initialX = frameStart; + frame.field1527 = frameEnd; + calculateBezierCoefficients(0.0F, normalizedValues[0], normalizedValues[1], 1.0F, frame); + adjustBezierValues(valueStart, interpolatedStart, interpolatedEnd, valueEnd, frame); + } + } + } else { + interpolateValues(frame, frameStart, controlPointsStart, controlPointEnd, frameEnd, valueStart, valueControlStart, valueControlEnd, valueEnd); + } + + frame.needsUpdate = false; + } + } else { + isLinearInterpolation = true; + } + + if (isLinearInterpolation) { + return keyFrame.value; + } else if (isInfiniteControlPoints) { + return (float)keyFrame.frameNumber != currentFrame && keyFrame.next != null ? keyFrame.next.value : keyFrame.value; + } else { + return frame.isInitialised ? advancedCalculate(frame, currentFrame) : simpleCalculate(frame, currentFrame); + } + } + } + } else { + return 0.0F; + } + } + + static float interpolate(MayaAnimationFrame animationFrame, float currentFrame, boolean useInitial) { + float interpolateValue = 0.0F; + if (animationFrame != null && animationFrame.getLastFrameIndex() != 0) { + float firstFrameNumber = (float)animationFrame.keyFrames[0].frameNumber; + float lastFrameNumber = (float)animationFrame.keyFrames[animationFrame.getLastFrameIndex() - 1].frameNumber; + float frameRange = lastFrameNumber - firstFrameNumber; + if ((double)frameRange == 0.0) { + return animationFrame.keyFrames[0].value; + } else { + float relativePosition = 0.0F; + if (currentFrame > lastFrameNumber) { + relativePosition = (currentFrame - lastFrameNumber) / frameRange; + } else { + relativePosition = (currentFrame - firstFrameNumber) / frameRange; + } + + double integerPart = (double)((int)relativePosition); + float fractionalPart = Math.abs((float)((double)relativePosition - integerPart)); + float adjustedFrame = fractionalPart * frameRange; + integerPart = Math.abs(1.0 + integerPart); + double halfIntegerPart = integerPart / 2.0; + double floorHalfIntegerPart = (double)((int)halfIntegerPart); + fractionalPart = (float)(halfIntegerPart - floorHalfIntegerPart); + float adjustedValue; + float scale; + if (useInitial) { + if (animationFrame.initialAnimationState == AnimationState.MIRROR) { + if ((double)fractionalPart != 0.0) { + adjustedFrame += firstFrameNumber; + } else { + adjustedFrame = lastFrameNumber - adjustedFrame; + } + } else if (animationFrame.initialAnimationState != AnimationState.CUSTOM2 && animationFrame.initialAnimationState != AnimationState.REPEAT) { + if (animationFrame.initialAnimationState == AnimationState.CUSTOM1) { + adjustedFrame = firstFrameNumber - currentFrame; + adjustedValue = animationFrame.keyFrames[0].field1469; + scale = animationFrame.keyFrames[0].field1470; + interpolateValue = animationFrame.keyFrames[0].value; + if (0.0 != (double)adjustedValue) { + interpolateValue -= scale * adjustedFrame / adjustedValue; + } + + return interpolateValue; + } + } else { + adjustedFrame = lastFrameNumber - adjustedFrame; + } + } else if (animationFrame.finalAnimationState == AnimationState.MIRROR) { + if (0.0 != (double)fractionalPart) { + adjustedFrame = lastFrameNumber - adjustedFrame; + } else { + adjustedFrame += firstFrameNumber; + } + } else if (animationFrame.finalAnimationState != AnimationState.CUSTOM2 && animationFrame.finalAnimationState != AnimationState.REPEAT) { + if (animationFrame.finalAnimationState == AnimationState.CUSTOM1) { + adjustedFrame = currentFrame - lastFrameNumber; + adjustedValue = animationFrame.keyFrames[animationFrame.getLastFrameIndex() - 1].controlPoint1; + scale = animationFrame.keyFrames[animationFrame.getLastFrameIndex() - 1].controlPoint2; + interpolateValue = animationFrame.keyFrames[animationFrame.getLastFrameIndex() - 1].value; + if (0.0 != (double)adjustedValue) { + interpolateValue += scale * adjustedFrame / adjustedValue; + } + + return interpolateValue; + } + } else { + adjustedFrame += firstFrameNumber; + } + + interpolateValue = calculateFrameValue(animationFrame, adjustedFrame); + float rangeDifference; + if (useInitial && animationFrame.initialAnimationState == AnimationState.REPEAT) { + rangeDifference = animationFrame.keyFrames[animationFrame.getLastFrameIndex() - 1].value - animationFrame.keyFrames[0].value; + interpolateValue = (float)((double)interpolateValue - integerPart * (double)rangeDifference); + } else if (!useInitial && animationFrame.finalAnimationState == AnimationState.REPEAT) { + rangeDifference = animationFrame.keyFrames[animationFrame.getLastFrameIndex() - 1].value - animationFrame.keyFrames[0].value; + interpolateValue = (float)((double)interpolateValue + integerPart * (double)rangeDifference); + } + + return interpolateValue; + } + } else { + return interpolateValue; + } + } + + static void calculateBezierCoefficients(float start, float control1, float control2, float end, MayaAnimationFrame animationFrame) { + float diff1 = control1 - start; + float diff2 = control2 - control1; + float diff3 = end - control2; + float delta1 = diff2 - diff1; + animationFrame.controlY = diff3 - diff2 - delta1; + animationFrame.controlZ = delta1 + delta1 + delta1; + animationFrame.controlW = diff1 + diff1 + diff1; + animationFrame.startY = start; + } + + static void adjustBezierValues(float start, float control1, float control2, float end, MayaAnimationFrame animationFrame) { + float diff1 = control1 - start; + float diff2 = control2 - control1; + float diff3 = end - control2; + float delta1 = diff2 - diff1; + animationFrame.adjustedY = diff3 - diff2 - delta1; + animationFrame.adjustedZ = delta1 + delta1 + delta1; + animationFrame.adjustedW = diff1 + diff1 + diff1; + animationFrame.adjustedX = start; + } + + static void interpolateValues(MayaAnimationFrame animationFrame, float start, float controlStart, float var3, float end, float valueStart, float var6, float var7, float valueEnd) { + if (animationFrame != null) { + animationFrame.initialX = start; + float span = end - start; + float valueSpan = valueEnd - valueStart; + float normalizedStart = controlStart - start; + float normalizedEnd = 0.0F; + float adjustedStart = 0.0F; + if (0.0 != (double)normalizedStart) { + normalizedEnd = (var6 - valueStart) / normalizedStart; + } + + normalizedStart = end - var3; + if (0.0 != (double)normalizedStart) { + adjustedStart = (valueEnd - var7) / normalizedStart; + } + + float squareSpan = 1.0F / (span * span); + float squareStart = normalizedEnd * span; + float squareEnd = span * adjustedStart; + animationFrame.startY = (squareEnd + squareStart - valueSpan - valueSpan) * squareSpan / span; + animationFrame.controlW = (valueSpan + valueSpan + valueSpan - squareStart - squareStart - squareEnd) * squareSpan; + animationFrame.controlZ = normalizedEnd; + animationFrame.controlY = valueStart; + } + } + + static float advancedCalculate(MayaAnimationFrame animationFrame, float currentFrame) { + if (animationFrame == null) { + return 0.0F; + } else { + float normalizedFrame; + if (currentFrame == animationFrame.initialX) { + normalizedFrame = 0.0F; + } else if (currentFrame == animationFrame.field1527) { + normalizedFrame = 1.0F; + } else { + normalizedFrame = (currentFrame - animationFrame.initialX) / (animationFrame.field1527 - animationFrame.initialX); + } + + float calculatedValue; + if (animationFrame.isActive) { + calculatedValue = normalizedFrame; + } else { + class121.field1477[3] = animationFrame.controlY; + class121.field1477[2] = animationFrame.controlZ; + class121.field1477[1] = animationFrame.controlW; + class121.field1477[0] = animationFrame.startY - normalizedFrame; + class121.field1485[0] = 0.0F; + class121.field1485[1] = 0.0F; + class121.field1485[2] = 0.0F; + class121.field1485[3] = 0.0F; + class121.field1485[4] = 0.0F; + int var4 = solvePolynomialEquation(class121.field1477, 3, 0.0F, true, 1.0F, true, class121.field1485); + if (var4 == 1) { + calculatedValue = class121.field1485[0]; + } else { + calculatedValue = 0.0F; + } + } + + return animationFrame.adjustedX + calculatedValue * (animationFrame.adjustedW + (animationFrame.adjustedY * calculatedValue + animationFrame.adjustedZ) * calculatedValue); + } + } + + static float simpleCalculate(MayaAnimationFrame animationFrame, float currentFrame) { + if (animationFrame == null) { + return 0.0F; + } else { + float frameDifference = currentFrame - animationFrame.initialX; + return frameDifference * (animationFrame.controlZ + (animationFrame.startY * frameDifference + animationFrame.controlW) * frameDifference) + animationFrame.controlY; + } + } + + public static int solvePolynomialEquation(float[] coefficients, int degree, float lowerBound, boolean isLowerInclusive, float upperBound, boolean isUpperInclusive, float[] roots) { + float totalCoefficientSum = 0.0F; + + for(int i = 0; i < degree + 1; ++i) { + totalCoefficientSum += Math.abs(coefficients[i]); + } + + float tolerance = (Math.abs(lowerBound) + Math.abs(upperBound)) * (float)(degree + 1) * class121.field1479; + if (totalCoefficientSum <= tolerance) { + return -1; + } else { + float[] normalizedCoefficients = new float[degree + 1]; + + int rootCount; + for(rootCount = 0; rootCount < degree + 1; ++rootCount) { + normalizedCoefficients[rootCount] = 1.0F / totalCoefficientSum * coefficients[rootCount]; + } + + while(Math.abs(normalizedCoefficients[degree]) < tolerance) { + --degree; + } + + rootCount = 0; + if (degree == 0) { + return rootCount; + } else if (degree == 1) { + roots[0] = -normalizedCoefficients[0] / normalizedCoefficients[1]; + boolean isRootBeyondLower = isLowerInclusive ? lowerBound < roots[0] + tolerance : lowerBound < roots[0] - tolerance; + boolean isRootBeyondUpper = isUpperInclusive ? upperBound > roots[0] - tolerance : upperBound > tolerance + roots[0]; + rootCount = isRootBeyondLower && isRootBeyondUpper ? 1 : 0; + if (rootCount > 0) { + if (isLowerInclusive && roots[0] < lowerBound) { + roots[0] = lowerBound; + } else if (isUpperInclusive && roots[0] > upperBound) { + roots[0] = upperBound; + } + } + + return rootCount; + } else { + PolynomialSolver polynomialSolver = new PolynomialSolver(normalizedCoefficients, degree); + float[] derivativeRoots = new float[degree + 1]; + + for(int var13 = 1; var13 <= degree; ++var13) { + derivativeRoots[var13 - 1] = (float)var13 * normalizedCoefficients[var13]; + } + + float[] tempRoots = new float[degree + 1]; + int derivativeRootCount = solvePolynomialEquation(derivativeRoots, degree - 1, lowerBound, false, upperBound, false, tempRoots); + if (derivativeRootCount == -1) { + return 0; + } else { + boolean hasConverged = false; + float lastEvaluatedValue = 0.0F; + float nextEvaluatedValue = 0.0F; + float nextRoot = 0.0F; + + for(int i = 0; i <= derivativeRootCount; ++i) { + if (rootCount > degree) { + return rootCount; + } + + float root; + if (i == 0) { + root = lowerBound; + nextEvaluatedValue = evaluatePolynomial(normalizedCoefficients, degree, lowerBound); + if (Math.abs(nextEvaluatedValue) <= tolerance && isLowerInclusive) { + roots[rootCount++] = lowerBound; + } + } else { + root = nextRoot; + nextEvaluatedValue = lastEvaluatedValue; + } + + if (derivativeRootCount == i) { + nextRoot = upperBound; + hasConverged = false; + } else { + nextRoot = tempRoots[i]; + } + + lastEvaluatedValue = evaluatePolynomial(normalizedCoefficients, degree, nextRoot); + if (hasConverged) { + hasConverged = false; + } else if (Math.abs(lastEvaluatedValue) < tolerance) { + if (derivativeRootCount != i || isUpperInclusive) { + roots[rootCount++] = nextRoot; + hasConverged = true; + } + } else if (nextEvaluatedValue < 0.0F && lastEvaluatedValue > 0.0F || nextEvaluatedValue > 0.0F && lastEvaluatedValue < 0.0F) { + int index = rootCount++; + float startX = root; + float endX = nextRoot; + float startValue = evaluatePolynomial(polynomialSolver.coefficients, polynomialSolver.degree, root); + float refinedRoot; + if (Math.abs(startValue) < class121.field1479) { + refinedRoot = root; + } else { + float endValue = evaluatePolynomial(polynomialSolver.coefficients, polynomialSolver.degree, nextRoot); + if (Math.abs(endValue) < class121.field1479) { + refinedRoot = nextRoot; + } else { + float var28 = 0.0F; + float var29 = 0.0F; + float var30 = 0.0F; + float var35 = 0.0F; + boolean var36 = true; + boolean var37 = false; + + do { + var37 = false; + if (var36) { + var28 = startX; + var35 = startValue; + var29 = endX - startX; + var30 = var29; + var36 = false; + } + + if (Math.abs(var35) < Math.abs(endValue)) { + startX = endX; + endX = var28; + var28 = startX; + startValue = endValue; + endValue = var35; + var35 = startValue; + } + + float var38 = class121.field1480 * Math.abs(endX) + 0.0F; + float var39 = (var28 - endX) * 0.5F; + boolean var40 = Math.abs(var39) > var38 && 0.0F != endValue; + if (var40) { + if (!(Math.abs(var30) < var38) && !(Math.abs(startValue) <= Math.abs(endValue))) { + float var34 = endValue / startValue; + float var31; + float var32; + if (var28 == startX) { + var31 = var39 * 2.0F * var34; + var32 = 1.0F - var34; + } else { + var32 = startValue / var35; + float var33 = endValue / var35; + var31 = ((var32 - var33) * var39 * 2.0F * var32 - (endX - startX) * (var33 - 1.0F)) * var34; + var32 = (var34 - 1.0F) * (var33 - 1.0F) * (var32 - 1.0F); + } + + if ((double)var31 > 0.0) { + var32 = -var32; + } else { + var31 = -var31; + } + + var34 = var30; + var30 = var29; + if (2.0F * var31 < var32 * 3.0F * var39 - Math.abs(var38 * var32) && var31 < Math.abs(var32 * 0.5F * var34)) { + var29 = var31 / var32; + } else { + var29 = var39; + var30 = var39; + } + } else { + var29 = var39; + var30 = var39; + } + + startX = endX; + startValue = endValue; + if (Math.abs(var29) > var38) { + endX += var29; + } else if ((double)var39 > 0.0) { + endX += var38; + } else { + endX -= var38; + } + + endValue = evaluatePolynomial(polynomialSolver.coefficients, polynomialSolver.degree, endX); + if ((double)(endValue * (var35 / Math.abs(var35))) > 0.0) { + var36 = true; + var37 = true; + } else { + var37 = true; + } + } + } while(var37); + + refinedRoot = endX; + } + } + + roots[index] = refinedRoot; + if (rootCount > 1 && roots[rootCount - 2] >= roots[rootCount - 1] - tolerance) { + roots[rootCount - 2] = 0.5F * (roots[rootCount - 1] + roots[rootCount - 2]); + --rootCount; + } + } + } + + return rootCount; + } + } + } + } + + /** + * Evaluates a polynomial at a given point using Horner's method. + * @param coefficients An array of coefficients of the polynomial. + * @param degree The degree of the polynomial. + * @param x The point at which the polynomial is to be evaluated. + * @return The value of the polynomial at the given point. + */ + static float evaluatePolynomial(float[] coefficients, int degree, float x) { + float result = coefficients[degree]; + + for(int i = degree - 1; i >= 0; --i) { + result = result * x + coefficients[i]; + } + + return result; + } + + int read(Buffer buffer, int version) { + int numberOfFrames = buffer.readUnsignedShort(); + validate(buffer.readUnsignedByte()); + int animationStateCode = buffer.readUnsignedByte(); + AnimationState animationState = (AnimationState)class4.findEnumerated(AnimationState.method2852(), animationStateCode); + if (animationState == null) { + animationState = AnimationState.DEFAULT; + } + + this.initialAnimationState = animationState; + this.finalAnimationState = AnimationState.method2292(buffer.readUnsignedByte()); + this.isInitialised = buffer.readUnsignedByte() != 0; + this.keyFrames = new KeyFrame[numberOfFrames]; + KeyFrame previousFrame = null; + + for(int i = 0; i < numberOfFrames; ++i) { + KeyFrame frame = new KeyFrame(); + frame.read(buffer, version); + this.keyFrames[i] = frame; + if (previousFrame != null) { + previousFrame.next = frame; + } + + previousFrame = frame; + } + + return numberOfFrames; + } + + void initialiseKeyFrames() { + this.startFrame = this.keyFrames[0].frameNumber; + this.endFrame = this.keyFrames[this.getLastFrameIndex() - 1].frameNumber; + this.frameValues = new float[this.getFrameCount() + 1]; + + for(int frame = this.getFirstFrame(); frame <= this.getLastFrame(); ++frame) { + this.frameValues[frame - this.getFirstFrame()] = calculateFrameValue(this, (float)frame); + } + + this.keyFrames = null; + this.lowerBoundFrameValue = calculateFrameValue(this, (float)(this.getFirstFrame() - 1)); + this.upperBoundFrameValue = calculateFrameValue(this, (float)(this.getLastFrame() + 1)); + } + + public float evaluate(int frame) { + if (frame < this.getFirstFrame()) { + return this.lowerBoundFrameValue; + } else { + return frame > this.getLastFrame() ? this.upperBoundFrameValue : this.frameValues[frame - this.getFirstFrame()]; + } + } + + int getFirstFrame() { + return this.startFrame; + } + + int getLastFrame() { + return this.endFrame; + } + + int getFrameCount() { + return this.getLastFrame() - this.getFirstFrame(); + } + + int findFrameIndex(float frame) { + + if (this.currentKeyframeIndex < 0 || !((float)this.keyFrames[this.currentKeyframeIndex].frameNumber <= frame) || this.keyFrames[this.currentKeyframeIndex].next != null && !((float)this.keyFrames[this.currentKeyframeIndex].next.frameNumber > frame)) { + if (!(frame < (float)this.getFirstFrame()) && !(frame > (float)this.getLastFrame())) { + int count = this.getLastFrameIndex(); + int current = this.currentKeyframeIndex; + if (count > 0) { + int low = 0; + int high = count - 1; + + do { + int var6 = high + low >> 1; + if (frame < (float)this.keyFrames[var6].frameNumber) { + if (frame > (float)this.keyFrames[var6 - 1].frameNumber) { + current = var6 - 1; + break; + } + + high = var6 - 1; + } else { + if (!(frame > (float)this.keyFrames[var6].frameNumber)) { + current = var6; + break; + } + + if (frame < (float)this.keyFrames[var6 + 1].frameNumber) { + current = var6; + break; + } + + low = var6 + 1; + } + } while(low <= high); + } + + if (current != this.currentKeyframeIndex) { + this.currentKeyframeIndex = current; + this.needsUpdate = true; + } + + return this.currentKeyframeIndex; + } else { + return -1; + } + } else { + return this.currentKeyframeIndex; + } + } + + KeyFrame getKeyFrame(float frame) { + int index = this.findFrameIndex(frame); + return index >= 0 && index < this.keyFrames.length ? this.keyFrames[index] : null; + } + + int getLastFrameIndex() { + return this.keyFrames == null ? 0 : this.keyFrames.length; + } +} diff --git a/src/main/kotlin/jagex/MayaAnimationFrameData.java b/src/main/kotlin/jagex/MayaAnimationFrameData.java new file mode 100644 index 0000000..d7edcd5 --- /dev/null +++ b/src/main/kotlin/jagex/MayaAnimationFrameData.java @@ -0,0 +1,34 @@ +package jagex; + +import java.util.concurrent.Callable; + +public class MayaAnimationFrameData implements Callable { + + final MayaAnimationFrame animationFrame; + final MayaAnimationFrameType animationFrameType; + final MayaAnimationFrameFlag animationFrameFlag; + final int field1515; + final MayaAnimation animation; + + MayaAnimationFrameData(MayaAnimation animation, MayaAnimationFrame animationFrame, MayaAnimationFrameType animationFrameType, MayaAnimationFrameFlag animationFrameFlag, int var5) { + this.animation = animation; + this.animationFrame = animationFrame; + this.animationFrameType = animationFrameType; + this.animationFrameFlag = animationFrameFlag; + this.field1515 = var5; + } + + public Object call() { + this.animationFrame.initialiseKeyFrames(); + MayaAnimationFrame[][] var1; + if (this.animationFrameType == MayaAnimationFrameType.field1548) { + var1 = this.animation.secondaryFrames; + } else { + var1 = this.animation.primaryFrames; + } + + var1[this.field1515][this.animationFrameFlag.method3076()] = this.animationFrame; + return null; + } + +} diff --git a/src/main/kotlin/jagex/MayaAnimationFrameFlag.java b/src/main/kotlin/jagex/MayaAnimationFrameFlag.java new file mode 100644 index 0000000..3896e1b --- /dev/null +++ b/src/main/kotlin/jagex/MayaAnimationFrameFlag.java @@ -0,0 +1,44 @@ +package jagex; + +public class MayaAnimationFrameFlag implements MouseWheel { + + static final MayaAnimationFrameFlag NORMAL = new MayaAnimationFrameFlag(0, 0, -1); + static final MayaAnimationFrameFlag field1559 = new MayaAnimationFrameFlag(1, 1, 0); + static final MayaAnimationFrameFlag field1568 = new MayaAnimationFrameFlag(2, 2, 1); + static final MayaAnimationFrameFlag field1560 = new MayaAnimationFrameFlag(3, 3, 2); + static final MayaAnimationFrameFlag field1562 = new MayaAnimationFrameFlag(4, 4, 3); + static final MayaAnimationFrameFlag field1578 = new MayaAnimationFrameFlag(5, 5, 4); + static final MayaAnimationFrameFlag field1564 = new MayaAnimationFrameFlag(6, 6, 5); + static final MayaAnimationFrameFlag field1565 = new MayaAnimationFrameFlag(7, 7, 6); + static final MayaAnimationFrameFlag field1561 = new MayaAnimationFrameFlag(8, 8, 7); + static final MayaAnimationFrameFlag field1567 = new MayaAnimationFrameFlag(9, 9, 8); + static final MayaAnimationFrameFlag field1563 = new MayaAnimationFrameFlag(10, 10, 0); + static final MayaAnimationFrameFlag field1569 = new MayaAnimationFrameFlag(11, 11, 1); + static final MayaAnimationFrameFlag field1570 = new MayaAnimationFrameFlag(12, 12, 2); + static final MayaAnimationFrameFlag field1571 = new MayaAnimationFrameFlag(13, 13, 3); + static final MayaAnimationFrameFlag field1572 = new MayaAnimationFrameFlag(14, 14, 4); + static final MayaAnimationFrameFlag field1573 = new MayaAnimationFrameFlag(15, 15, 5); + static final MayaAnimationFrameFlag field1574 = new MayaAnimationFrameFlag(16, 16, 0); + + final int field1575; + final int field1576; + final int field1577; + + MayaAnimationFrameFlag(int var1, int var2, int var4) { + this.field1575 = var1; + this.field1576 = var2; + this.field1577 = var4; + } + + static MayaAnimationFrameFlag[] values() { + return new MayaAnimationFrameFlag[]{NORMAL, field1559, field1568, field1560, field1562, field1578, field1564, field1565, field1561, field1567, field1563, field1569, field1570, field1571, field1572, field1573, field1574}; + } + + public int rsOrdinal() { + return this.field1576; + } + + int method3076() { + return this.field1577; + } +} diff --git a/src/main/kotlin/jagex/MayaAnimationFrameType.java b/src/main/kotlin/jagex/MayaAnimationFrameType.java new file mode 100644 index 0000000..91d0fe2 --- /dev/null +++ b/src/main/kotlin/jagex/MayaAnimationFrameType.java @@ -0,0 +1,81 @@ +package jagex; + +import net.runelite.mapping.Export; +import net.runelite.mapping.ObfuscatedGetter; +import net.runelite.mapping.ObfuscatedName; +import net.runelite.mapping.ObfuscatedSignature; + +@ObfuscatedName("dk") +public class MayaAnimationFrameType implements MouseWheel { + @ObfuscatedName("f") + @ObfuscatedSignature( + descriptor = "Ldk;" + ) + static final MayaAnimationFrameType DEFAULT = new MayaAnimationFrameType(0, 0, (String)null, 0); + @ObfuscatedName("w") + @ObfuscatedSignature( + descriptor = "Ldk;" + ) + static final MayaAnimationFrameType field1548 = new MayaAnimationFrameType(1, 1, (String)null, 9); + @ObfuscatedName("v") + @ObfuscatedSignature( + descriptor = "Ldk;" + ) + static final MayaAnimationFrameType field1555 = new MayaAnimationFrameType(2, 2, (String)null, 3); + @ObfuscatedName("s") + @ObfuscatedSignature( + descriptor = "Ldk;" + ) + static final MayaAnimationFrameType field1550 = new MayaAnimationFrameType(3, 3, (String)null, 6); + @ObfuscatedName("z") + @ObfuscatedSignature( + descriptor = "Ldk;" + ) + static final MayaAnimationFrameType TRANSFORMATION = new MayaAnimationFrameType(4, 4, (String)null, 1); + @ObfuscatedName("j") + @ObfuscatedSignature( + descriptor = "Ldk;" + ) + static final MayaAnimationFrameType field1551 = new MayaAnimationFrameType(5, 5, (String)null, 3); + @ObfuscatedName("i") + @ObfuscatedGetter( + intValue = -162414941 + ) + final int field1547; + @ObfuscatedName("n") + @ObfuscatedGetter( + intValue = -1294570757 + ) + final int field1553; + @ObfuscatedName("l") + @ObfuscatedGetter( + intValue = -1207176119 + ) + final int field1554; + + MayaAnimationFrameType(int var1, int var2, String var3, int var4) { + this.field1547 = var1; + this.field1553 = var2; + this.field1554 = var4; + } + + @ObfuscatedName("f") + @ObfuscatedSignature( + descriptor = "(B)I", + garbageValue = "3" + ) + @Export("rsOrdinal") + public int rsOrdinal() { + return this.field1553; + } + + @ObfuscatedName("v") + @ObfuscatedSignature( + descriptor = "(B)I", + garbageValue = "20" + ) + int getMaxIndex() { + return this.field1554; + } + +} diff --git a/src/main/kotlin/jagex/MayaAnimationLoadFrameTask.java b/src/main/kotlin/jagex/MayaAnimationLoadFrameTask.java new file mode 100644 index 0000000..bf4bc60 --- /dev/null +++ b/src/main/kotlin/jagex/MayaAnimationLoadFrameTask.java @@ -0,0 +1,26 @@ +package jagex; + +import java.util.concurrent.Callable; + +class MayaAnimationLoadFrameTask implements Callable { + + final MayaAnimation animation; + final int val$workStart; + final int val$workEnd; + final MayaAnimationFrameData[] val$curveLoadJobs; + + MayaAnimationLoadFrameTask(MayaAnimation animation, int var2, int var3, MayaAnimationFrameData[] var4) { + this.animation = animation; + this.val$workStart = var2; + this.val$workEnd = var3; + this.val$curveLoadJobs = var4; + } + + public Object call() { + for(int var1 = this.val$workStart; var1 < this.val$workEnd; ++var1) { + this.val$curveLoadJobs[var1].call(); + } + + return null; + } +} diff --git a/src/main/kotlin/jagex/MayaAnimationLoadTask.java b/src/main/kotlin/jagex/MayaAnimationLoadTask.java new file mode 100644 index 0000000..4a1b783 --- /dev/null +++ b/src/main/kotlin/jagex/MayaAnimationLoadTask.java @@ -0,0 +1,15 @@ +package jagex; + +import java.util.concurrent.Callable; + +record MayaAnimationLoadTask( + MayaAnimation mayaAnimation, + Buffer mayaAnimationBuffer, + int mayaAnimationVersion +) implements Callable { + + public Object call() { + this.mayaAnimation.read(this.mayaAnimationBuffer, this.mayaAnimationVersion); + return null; + } +} diff --git a/src/main/kotlin/jagex/MayaAnimationSkeleton.java b/src/main/kotlin/jagex/MayaAnimationSkeleton.java new file mode 100644 index 0000000..e347dcb --- /dev/null +++ b/src/main/kotlin/jagex/MayaAnimationSkeleton.java @@ -0,0 +1,55 @@ +package jagex; + +public class MayaAnimationSkeleton { + + Bone[] bones; + int boneCount; + + MayaAnimationSkeleton(Buffer buffer, int var2) { + this.bones = new Bone[var2]; + this.boneCount = buffer.readUnsignedByte(); + + for (int var3 = 0; var3 < this.bones.length; ++var3) { + Bone frame = new Bone(this.boneCount, buffer, false); + this.bones[var3] = frame; + } + + this.initializeBones(); + } + + void initializeBones() { + for (Bone frame : bones) { + if (frame.index >= 0) { + frame.parentBone = this.bones[frame.index]; + } + } + } + + public int frameCount() { + return this.bones.length; + } + + public Bone getBone(int index) { + return index >= this.frameCount() ? null : this.bones[index]; + } + + public Bone[] getAllBones() { + return this.bones; + } + + public void applyAnimation(MayaAnimation animation, int frameIndex) { + this.applyAnimation(animation, frameIndex, (boolean[]) null, false); + } + + public void applyAnimation(MayaAnimation animation, int frameIndex, boolean[] affectedBones, boolean inverse) { + int animLength = animation.getDuration(); + int boneIndex = 0; + Bone[] frames = this.getAllBones(); + for (Bone frame : frames) { + if (affectedBones == null || inverse == affectedBones[boneIndex]) { + animation.apply(frameIndex, frame, boneIndex, animLength); + } + ++boneIndex; + } + } +} diff --git a/src/main/kotlin/jagex/MouseWheel.java b/src/main/kotlin/jagex/MouseWheel.java new file mode 100644 index 0000000..75b1afc --- /dev/null +++ b/src/main/kotlin/jagex/MouseWheel.java @@ -0,0 +1,6 @@ +package jagex; + +public interface MouseWheel { + + int rsOrdinal(); +} diff --git a/src/main/kotlin/jagex/PolynomialSolver.java b/src/main/kotlin/jagex/PolynomialSolver.java new file mode 100644 index 0000000..7d2d6d2 --- /dev/null +++ b/src/main/kotlin/jagex/PolynomialSolver.java @@ -0,0 +1,12 @@ +package jagex; + +public class PolynomialSolver { + + float[] coefficients; + int degree; + + PolynomialSolver(float[] coefficients, int degree) { + this.coefficients = coefficients; + this.degree = degree; + } +} diff --git a/src/main/kotlin/jagex/Quaternion.java b/src/main/kotlin/jagex/Quaternion.java new file mode 100644 index 0000000..8d49045 --- /dev/null +++ b/src/main/kotlin/jagex/Quaternion.java @@ -0,0 +1,84 @@ +package jagex; + +public final class Quaternion { + public static Quaternion[] pool = new Quaternion[0]; + public static int poolSize; + static int maxPoolSize; + float qW; + float qX; + float qY; + float qZ; + + static { + initializePool(100); + new Quaternion(); + } + + public Quaternion() { + this.reset(); + } + + static void initializePool(int size) { + maxPoolSize = size; + pool = new Quaternion[size]; + poolSize = 0; + } + + public void release() { + synchronized(pool) { + if (poolSize < maxPoolSize - 1) { + pool[++poolSize - 1] = this; + } + } + } + + void setValues(float x, float y, float z, float w) { + this.qX = x; + this.qY = y; + this.qZ = z; + this.qW = w; + } + + public void setFromAxisAngle(float axisX, float axisY, float axisZ, float angle) { + float sinHalfAngle = (float)Math.sin((double)(angle * 0.5F)); + float cosHalfAngle = (float)Math.cos((double)(0.5F * angle)); + this.qX = axisX * sinHalfAngle; + this.qY = axisY * sinHalfAngle; + this.qZ = sinHalfAngle * axisZ; + this.qW = cosHalfAngle; + } + + public final void reset() { + this.qZ = 0.0F; + this.qY = 0.0F; + this.qX = 0.0F; + this.qW = 1.0F; + } + + public final void multiply(Quaternion other) { + this.setValues(other.qY * this.qZ + this.qW * other.qX + other.qW * this.qX - this.qY * other.qZ, this.qX * other.qZ + this.qY * other.qW - other.qX * this.qZ + other.qY * this.qW, other.qW * this.qZ + other.qX * this.qY - this.qX * other.qY + other.qZ * this.qW, other.qW * this.qW - this.qX * other.qX - other.qY * this.qY - other.qZ * this.qZ); + } + + public boolean equals(Object other) { + if (!(other instanceof Quaternion)) { + return false; + } else { + Quaternion otherQuaternion = (Quaternion)other; + return this.qX == otherQuaternion.qX && otherQuaternion.qY == this.qY && otherQuaternion.qZ == this.qZ && otherQuaternion.qW == this.qW; + } + } + + public int hashCode() { + boolean var1 = true; + float result = 1.0F; + result = result * 31.0F + this.qX; + result = this.qY + 31.0F * result; + result = this.qZ + result * 31.0F; + result = 31.0F * result + this.qW; + return (int)result; + } + + public String toString() { + return this.qX + "," + this.qY + "," + this.qZ + "," + this.qW; + } +} diff --git a/src/main/kotlin/jagex/Skeleton.java b/src/main/kotlin/jagex/Skeleton.java new file mode 100644 index 0000000..f7d7664 --- /dev/null +++ b/src/main/kotlin/jagex/Skeleton.java @@ -0,0 +1,52 @@ +package jagex; + +public class Skeleton { + int id; + int count; + int[] transformTypes; + int[][] labels; + MayaAnimationSkeleton mayaAnimationSkeleton; + + public Skeleton(int var1, byte[] var2) { + this.id = var1; + Buffer buffer = new Buffer(var2); + this.count = buffer.readUnsignedByte(); + this.transformTypes = new int[this.count]; + this.labels = new int[this.count][]; + + int var4; + for(var4 = 0; var4 < this.count; ++var4) { + this.transformTypes[var4] = buffer.readUnsignedByte(); + } + + for(var4 = 0; var4 < this.count; ++var4) { + this.labels[var4] = new int[buffer.readUnsignedByte()]; + } + + for(var4 = 0; var4 < this.count; ++var4) { + for(int var5 = 0; var5 < this.labels[var4].length; ++var5) { + this.labels[var4][var5] = buffer.readUnsignedByte(); + } + } + + if (buffer.offset < buffer.array.length) { + var4 = buffer.readUnsignedShort(); + if (var4 > 0) { + this.mayaAnimationSkeleton = new MayaAnimationSkeleton(buffer, var4); + } + } + } + + public int getId() { + return this.id; + } + + public int getCount() { + return this.count; + } + + public MayaAnimationSkeleton getMayaAnimationSkeleton() { + return this.mayaAnimationSkeleton; + } + +} diff --git a/src/main/kotlin/jagex/TransformationMatrix.java b/src/main/kotlin/jagex/TransformationMatrix.java new file mode 100644 index 0000000..b015045 --- /dev/null +++ b/src/main/kotlin/jagex/TransformationMatrix.java @@ -0,0 +1,101 @@ +package jagex; + +public class TransformationMatrix { + + float scaleX; + float skewYX; + float skewZX; + float skewXY; + float scaleY; + float skewZY; + float skewXZ; + float skewYZ; + float scaleZ; + float translateX; + float translateY; + float translateZ; + + static { + new TransformationMatrix(); + } + + TransformationMatrix() { + this.reset(); + } + + void reset() { + this.translateZ = 0.0F; + this.translateY = 0.0F; + this.translateX = 0.0F; + this.skewYZ = 0.0F; + this.skewXZ = 0.0F; + this.skewZY = 0.0F; + this.skewXY = 0.0F; + this.skewZX = 0.0F; + this.skewYX = 0.0F; + this.scaleZ = 1.0F; + this.scaleY = 1.0F; + this.scaleX = 1.0F; + } + + void rotateX(float angle) { + float cosAngle = (float)Math.cos((double)angle); + float sinAngle = (float)Math.sin((double)angle); + float tempSkewYX = this.skewYX; + float tempScaleY = this.scaleY; + float tempSkewYZ = this.skewYZ; + float tempTranslateY = this.translateY; + this.skewYX = tempSkewYX * cosAngle - this.skewZX * sinAngle; + this.skewZX = tempSkewYX * sinAngle + cosAngle * this.skewZX; + this.scaleY = cosAngle * tempScaleY - sinAngle * this.skewZY; + this.skewZY = cosAngle * this.skewZY + sinAngle * tempScaleY; + this.skewYZ = cosAngle * tempSkewYZ - sinAngle * this.scaleZ; + this.scaleZ = this.scaleZ * cosAngle + tempSkewYZ * sinAngle; + this.translateY = tempTranslateY * cosAngle - sinAngle * this.translateZ; + this.translateZ = cosAngle * this.translateZ + sinAngle * tempTranslateY; + } + + void rotateY(float angle) { + float cosAngle = (float)Math.cos((double)angle); + float sinAngle = (float)Math.sin((double)angle); + float tempScaleX = this.scaleX; + float tempSkewXY = this.skewXY; + float tempSkewXZ = this.skewXZ; + float tempTranslateX = this.translateX; + this.scaleX = this.skewZX * sinAngle + tempScaleX * cosAngle; + this.skewZX = cosAngle * this.skewZX - sinAngle * tempScaleX; + this.skewXY = sinAngle * this.skewZY + tempSkewXY * cosAngle; + this.skewZY = this.skewZY * cosAngle - tempSkewXY * sinAngle; + this.skewXZ = sinAngle * this.scaleZ + tempSkewXZ * cosAngle; + this.scaleZ = this.scaleZ * cosAngle - tempSkewXZ * sinAngle; + this.translateX = cosAngle * tempTranslateX + this.translateZ * sinAngle; + this.translateZ = this.translateZ * cosAngle - sinAngle * tempTranslateX; + } + + void rotateZ(float angle) { + float cosAngle = (float)Math.cos((double)angle); + float sinAngle = (float)Math.sin((double)angle); + float tempScaleX = this.scaleX; + float tempSkewXY = this.skewXY; + float tempSkewXZ = this.skewXZ; + float tempTranslateX = this.translateX; + this.scaleX = tempScaleX * cosAngle - sinAngle * this.skewYX; + this.skewYX = cosAngle * this.skewYX + tempScaleX * sinAngle; + this.skewXY = tempSkewXY * cosAngle - sinAngle * this.scaleY; + this.scaleY = this.scaleY * cosAngle + sinAngle * tempSkewXY; + this.skewXZ = tempSkewXZ * cosAngle - sinAngle * this.skewYZ; + this.skewYZ = cosAngle * this.skewYZ + sinAngle * tempSkewXZ; + this.translateX = cosAngle * tempTranslateX - this.translateY * sinAngle; + this.translateY = sinAngle * tempTranslateX + this.translateY * cosAngle; + } + + void translate(float x, float y, float z) { + this.translateX += x; + this.translateY += y; + this.translateZ += z; + } + + public String toString() { + return this.scaleX + "," + this.skewXY + "," + this.skewXZ + "," + this.translateX + "\n" + this.skewYX + "," + this.scaleY + "," + this.skewYZ + "," + this.translateY + "\n" + this.skewZX + "," + this.skewZY + "," + this.scaleZ + "," + this.translateZ; + } +} diff --git a/src/main/kotlin/jagex/class121.java b/src/main/kotlin/jagex/class121.java new file mode 100644 index 0000000..3fd129a --- /dev/null +++ b/src/main/kotlin/jagex/class121.java @@ -0,0 +1,18 @@ +package jagex; + +public class class121 { + + public static final float field1479 = Math.ulp(1.0F); + public static final float field1480; + static float[] field1477; + static float[] field1485; + + static int field1483; + + static { + field1480 = 2.0F * field1479; + field1477 = new float[4]; + field1485 = new float[5]; + } + +} diff --git a/src/main/kotlin/jagex/class130.java b/src/main/kotlin/jagex/class130.java new file mode 100644 index 0000000..52f593b --- /dev/null +++ b/src/main/kotlin/jagex/class130.java @@ -0,0 +1,10 @@ +package jagex; + +import java.util.concurrent.ThreadFactory; + +final class class130 implements ThreadFactory { + + public Thread newThread(Runnable var1) { + return new Thread(var1, "OSRS Maya Anim Load"); + } +} diff --git a/src/main/kotlin/jagex/class134.java b/src/main/kotlin/jagex/class134.java new file mode 100644 index 0000000..f2688b3 --- /dev/null +++ b/src/main/kotlin/jagex/class134.java @@ -0,0 +1,26 @@ +package jagex; + +public enum class134 implements MouseWheel { + + field1621(0, 0), + field1607(1, 1), + field1608(2, 2), + field1609(3, 3), + field1610(4, 4), + field1617(5, 5), + field1612(6, 6), + field1611(7, 7), + field1614(8, 8); + + final int field1615; + final int field1616; + + class134(int var3, int var4) { + this.field1615 = var3; + this.field1616 = var4; + } + + public int rsOrdinal() { + return this.field1616; + } +} diff --git a/src/main/kotlin/jagex/class277.java b/src/main/kotlin/jagex/class277.java new file mode 100644 index 0000000..f79ab9b --- /dev/null +++ b/src/main/kotlin/jagex/class277.java @@ -0,0 +1,8 @@ +package jagex; + +import java.util.concurrent.ThreadPoolExecutor; + +public class class277 { + static ThreadPoolExecutor threadPoolExecutor; + +} diff --git a/src/main/kotlin/jagex/class4.java b/src/main/kotlin/jagex/class4.java new file mode 100644 index 0000000..e302ab4 --- /dev/null +++ b/src/main/kotlin/jagex/class4.java @@ -0,0 +1,13 @@ +package jagex; + +public class class4 { + + public static MouseWheel findEnumerated(MouseWheel[] var0, int var1) { + for (MouseWheel var4 : var0) { + if (var1 == var4.rsOrdinal()) { + return var4; + } + } + return null; + } +} diff --git a/src/main/kotlin/jagex/class415.java b/src/main/kotlin/jagex/class415.java new file mode 100644 index 0000000..32c0fa2 --- /dev/null +++ b/src/main/kotlin/jagex/class415.java @@ -0,0 +1,32 @@ +package jagex; + +import net.runelite.mapping.ObfuscatedName; +import net.runelite.mapping.ObfuscatedSignature; + +public class class415 { + float field4632; + float field4631; + float field4630; + + static { + new class415(0.0F, 0.0F, 0.0F); + new class415(1.0F, 1.0F, 1.0F); + new class415(1.0F, 0.0F, 0.0F); + new class415(0.0F, 1.0F, 0.0F); + new class415(0.0F, 0.0F, 1.0F); + } + + class415(float var1, float var2, float var3) { + this.field4632 = var1; + this.field4631 = var2; + this.field4630 = var3; + } + + final float method7872() { + return (float)Math.sqrt((double)(this.field4630 * this.field4630 + this.field4631 * this.field4631 + this.field4632 * this.field4632)); + } + + public String toString() { + return this.field4632 + ", " + this.field4631 + ", " + this.field4630; + } +} diff --git a/src/main/kotlin/jagex/class461.java b/src/main/kotlin/jagex/class461.java new file mode 100644 index 0000000..dbf30bf --- /dev/null +++ b/src/main/kotlin/jagex/class461.java @@ -0,0 +1,6 @@ +package jagex; + +public class class461 { + static int threadPoolExecutorThreadCount; + +} diff --git a/src/main/kotlin/net/runelite/mapping/Export.java b/src/main/kotlin/net/runelite/mapping/Export.java new file mode 100644 index 0000000..062e342 --- /dev/null +++ b/src/main/kotlin/net/runelite/mapping/Export.java @@ -0,0 +1,6 @@ +package net.runelite.mapping; + +public @interface Export +{ + String value(); +} diff --git a/src/main/kotlin/net/runelite/mapping/ObfuscatedGetter.java b/src/main/kotlin/net/runelite/mapping/ObfuscatedGetter.java new file mode 100644 index 0000000..e3da978 --- /dev/null +++ b/src/main/kotlin/net/runelite/mapping/ObfuscatedGetter.java @@ -0,0 +1,8 @@ +package net.runelite.mapping; + +public @interface ObfuscatedGetter +{ + int intValue() default 0; + + long longValue() default 0L; +} diff --git a/src/main/kotlin/net/runelite/mapping/ObfuscatedName.java b/src/main/kotlin/net/runelite/mapping/ObfuscatedName.java new file mode 100644 index 0000000..ae55907 --- /dev/null +++ b/src/main/kotlin/net/runelite/mapping/ObfuscatedName.java @@ -0,0 +1,6 @@ +package net.runelite.mapping; + +public @interface ObfuscatedName +{ + String value(); +} diff --git a/src/main/kotlin/net/runelite/mapping/ObfuscatedSignature.java b/src/main/kotlin/net/runelite/mapping/ObfuscatedSignature.java new file mode 100644 index 0000000..b977f02 --- /dev/null +++ b/src/main/kotlin/net/runelite/mapping/ObfuscatedSignature.java @@ -0,0 +1,8 @@ +package net.runelite.mapping; + +public @interface ObfuscatedSignature +{ + String descriptor(); + + String garbageValue() default ""; // valid garbage value for last parameter. can't be an Object because Java. +} diff --git a/src/main/kotlin/stan/qodat/Properties.kt b/src/main/kotlin/stan/qodat/Properties.kt index 2787d7d..2901d23 100644 --- a/src/main/kotlin/stan/qodat/Properties.kt +++ b/src/main/kotlin/stan/qodat/Properties.kt @@ -8,6 +8,7 @@ import javafx.scene.shape.DrawMode import qodat.cache.Cache import stan.qodat.scene.controller.EntityViewController.SortType import stan.qodat.scene.runescape.animation.Animation +import stan.qodat.scene.runescape.animation.AnimationLegacy import stan.qodat.scene.runescape.entity.Entity import stan.qodat.util.PropertiesManager import tornadofx.booleanProperty diff --git a/src/main/kotlin/stan/qodat/cache/CacheAssetLoader.kt b/src/main/kotlin/stan/qodat/cache/CacheAssetLoader.kt index 0a2b871..172438b 100644 --- a/src/main/kotlin/stan/qodat/cache/CacheAssetLoader.kt +++ b/src/main/kotlin/stan/qodat/cache/CacheAssetLoader.kt @@ -4,10 +4,13 @@ import javafx.application.Platform import javafx.concurrent.Task import qodat.cache.Cache import qodat.cache.definition.AnimatedEntityDefinition +import qodat.cache.definition.AnimationMayaDefinition import qodat.cache.definition.EntityDefinition import stan.qodat.Properties import stan.qodat.cache.impl.oldschool.OldschoolCacheRuneLite import stan.qodat.scene.runescape.animation.Animation +import stan.qodat.scene.runescape.animation.AnimationLegacy +import stan.qodat.scene.runescape.animation.AnimationMaya import stan.qodat.scene.runescape.entity.* import stan.qodat.task.BackgroundTasks import stan.qodat.util.createNpcAnimsJsonDir @@ -78,10 +81,17 @@ class CacheAssetLoader( val animations = ArrayList() for ((i, definition) in animationDefinitions.withIndex()) { try { - if (definition.frameHashes.isNotEmpty()) - animations += Animation("$i", definition, cache).apply { + if (definition is AnimationMayaDefinition) { + animations += AnimationMaya("$i", definition, cache).apply { this.idProperty.set(i) } + } else { + if (definition.frameHashes.isNotEmpty()) { + animations += AnimationLegacy("$i", definition, cache).apply { + this.idProperty.set(i) + } + } + } updateProgress((100.0 * i.div(animationDefinitions.size)), 100.0) updateMessage("Loading animation (${i + 1} / ${animationDefinitions.size})") } catch (e: Exception) { diff --git a/src/main/kotlin/stan/qodat/cache/impl/legacy/LegacyDefinitions.kt b/src/main/kotlin/stan/qodat/cache/impl/legacy/LegacyDefinitions.kt index 467d45b..e4b467e 100644 --- a/src/main/kotlin/stan/qodat/cache/impl/legacy/LegacyDefinitions.kt +++ b/src/main/kotlin/stan/qodat/cache/impl/legacy/LegacyDefinitions.kt @@ -70,7 +70,7 @@ data class LegacyAnimationFrameDefinition( override val transformationDeltaY: IntArray, override val transformationDeltaZ: IntArray, override val transformationGroup: LegacyAnimationSkeletonDefinition -) : AnimationFrameDefinition +) : AnimationFrameLegacyDefinition @Serializable data class LegacyModelDefinition( diff --git a/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt b/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt index e73fa9e..ad5eb32 100644 --- a/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt +++ b/src/main/kotlin/stan/qodat/cache/impl/oldschool/OldschoolCacheRuneLite.kt @@ -2,9 +2,13 @@ package stan.qodat.cache.impl.oldschool import com.google.gson.GsonBuilder import com.google.gson.reflect.TypeToken -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.runBlocking import net.runelite.cache.* import net.runelite.cache.definitions.FramemapDefinition +import net.runelite.cache.definitions.SequenceDefinition import net.runelite.cache.definitions.loaders.FrameLoader import net.runelite.cache.definitions.loaders.FramemapLoader import net.runelite.cache.definitions.loaders.SequenceLoader @@ -31,7 +35,7 @@ import java.util.concurrent.ConcurrentHashMap */ object OldschoolCacheRuneLite : Cache("LIVE") { - internal var store = Store(Properties.osrsCachePath.get().toFile()) + var store = Store(Properties.osrsCachePath.get().toFile()) lateinit var npcManager: NpcManager lateinit var itemManager: ItemManager @@ -42,7 +46,7 @@ object OldschoolCacheRuneLite : Cache("LIVE") { lateinit var frameIndex: Index lateinit var framemapIndex: Index - lateinit var frames: HashMap> + lateinit var frames: HashMap> lateinit var frameMaps: HashMap> private lateinit var animations : Array @@ -64,7 +68,7 @@ object OldschoolCacheRuneLite : Cache("LIVE") { store.load() frameIndex = store.getIndex(IndexType.ANIMATIONS) framemapIndex = store.getIndex(IndexType.SKELETONS) - frames = HashMap>() + frames = HashMap>() frameMaps = HashMap>() npcManager = NpcManager(store) npcManager.load() @@ -123,17 +127,21 @@ object OldschoolCacheRuneLite : Cache("LIVE") { npcManager.npcs .filter { it.models != null && it.models.isNotEmpty() } .map { npc -> - GlobalScope.async(Dispatchers.IO) { + async(Dispatchers.IO) { object : NPCDefinition { override fun getOptionalId() = OptionalInt.of(npc.id) override val name = npc.name.ifBlank { "null" } override val modelIds = npc.models.map { it.toString() }.toTypedArray() override val animationIds = try { animIdsCache.getOrPut(npc.standingAnimation) { - npcAnimsDir + val data = npcAnimsDir .resolve("${npc.id}.json") .bufferedReader() .use {gson.fromJson(it, intArrayType).map { it.toString() }.toTypedArray() } + if (data.isEmpty()) { + emptyArray() + } else + data } } catch (ignored: Exception) { System.err.println("Failed to load anim data for npc ${npc.name} ${npc.standingAnimation}") @@ -213,7 +221,21 @@ object OldschoolCacheRuneLite : Cache("LIVE") { val sequence = SequenceLoader().apply { configureForRevision(seqArchive.revision) }.load(it.fileId, it.contents) - object : AnimationDefinition { + if (sequence.animMayaID >= 0) + object : AnimationMayaDefinition { + override val id: String = it.fileId.toString() + override val frameHashes: IntArray = sequence.frameIDs?: IntArray(0) + override val frameLengths: IntArray = sequence.frameLenghts?: IntArray(0) + override val loopOffset: Int = sequence.frameStep + override val leftHandItem: Int = sequence.leftHandItem + override val rightHandItem: Int = sequence.rightHandItem + override val animMayaID: Int = sequence.animMayaID + override val animMayaFrameSounds: Map = sequence.animMayaFrameSounds?: emptyMap() + override val animMayaStart: Int = sequence.animMayaStart + override val animMayaEnd: Int = sequence.animMayaEnd + override val animMayaMasks: BooleanArray = sequence.animMayaMasks?: BooleanArray(0) + } + else object : AnimationDefinition { override val id: String = it.fileId.toString() override val frameHashes: IntArray = sequence.frameIDs?: IntArray(0) override val frameLengths: IntArray = sequence.frameLenghts?: IntArray(0) @@ -222,6 +244,7 @@ object OldschoolCacheRuneLite : Cache("LIVE") { override val rightHandItem: Int = sequence.rightHandItem } } catch (e: Exception) { + e.printStackTrace() val sequence = SequenceLoader206().load(it.fileId, it.contents) object : AnimationDefinition { override val id: String = it.fileId.toString() @@ -237,7 +260,7 @@ object OldschoolCacheRuneLite : Cache("LIVE") { return animations } - override fun getFrameDefinition(frameHash: Int): AnimationFrameDefinition? { + override fun getFrameDefinition(frameHash: Int): AnimationFrameLegacyDefinition? { val storage = store.storage val hexString = Integer.toHexString(frameHash) @@ -265,7 +288,7 @@ object OldschoolCacheRuneLite : Cache("LIVE") { } } val frame = FrameLoader().load(frameMapDefinition, file.fileId, frameContents) - file.fileId to object : AnimationFrameDefinition { + file.fileId to object : AnimationFrameLegacyDefinition { override val transformationCount: Int = frame.translatorCount override val transformationGroupAccessIndices: IntArray = frame.indexFrameIds override val transformationDeltaX: IntArray = frame.translator_x diff --git a/src/main/kotlin/stan/qodat/cache/impl/qodat/QodatCache.kt b/src/main/kotlin/stan/qodat/cache/impl/qodat/QodatCache.kt index 7de498a..5911716 100644 --- a/src/main/kotlin/stan/qodat/cache/impl/qodat/QodatCache.kt +++ b/src/main/kotlin/stan/qodat/cache/impl/qodat/QodatCache.kt @@ -11,7 +11,8 @@ import qodat.cache.definition.* import qodat.cache.models.RSModelLoader import stan.qodat.Properties import stan.qodat.cache.impl.oldschool.OldschoolCacheRuneLite -import stan.qodat.scene.runescape.animation.Animation +import stan.qodat.scene.runescape.animation.AnimationFrameLegacy +import stan.qodat.scene.runescape.animation.AnimationLegacy import stan.qodat.scene.runescape.entity.NPC import stan.qodat.scene.runescape.model.Model import java.io.File @@ -102,7 +103,7 @@ object QodatCache : Cache("qodat") { } encodeModel(file, getQodatModelDefinition(any)) return EncodeResult(file) - } else if (any is Animation) { + } else if (any is AnimationLegacy) { val animationSaveDir = Properties.defaultExportsPath.get().resolve("animation").resolve("json").toFile().apply { if (!parentFile.exists()) parentFile.mkdirs() @@ -125,7 +126,7 @@ object QodatCache : Cache("qodat") { if (!exists()) mkdir() } - val frameList = any.getFrameList() + val frameList = any.getFrameList().filterIsInstance() val frameArchiveId = any.exportFrameArchiveId.get() // animationFrameSaveDir.listFiles() // ?.mapNotNull { it.nameWithoutExtension.toIntOrNull() } @@ -265,7 +266,7 @@ object QodatCache : Cache("qodat") { return OldschoolCacheRuneLite.getAnimationSkeletonDefinition(frameHash) } - override fun getFrameDefinition(frameHash: Int): AnimationFrameDefinition? { + override fun getFrameDefinition(frameHash: Int): AnimationFrameLegacyDefinition? { return OldschoolCacheRuneLite.getFrameDefinition(frameHash) } diff --git a/src/main/kotlin/stan/qodat/cache/impl/qodat/QodatDefinitions.kt b/src/main/kotlin/stan/qodat/cache/impl/qodat/QodatDefinitions.kt index daef188..16ac5a5 100644 --- a/src/main/kotlin/stan/qodat/cache/impl/qodat/QodatDefinitions.kt +++ b/src/main/kotlin/stan/qodat/cache/impl/qodat/QodatDefinitions.kt @@ -58,7 +58,7 @@ data class QodatAnimationFrameDefinition( override val transformationDeltaY: IntArray, override val transformationDeltaZ: IntArray, override val transformationGroup: QodatAnimationTransformationGroup -) : AnimationFrameDefinition +) : AnimationFrameLegacyDefinition @Serializable data class QodatModelDefinition( diff --git a/src/main/kotlin/stan/qodat/scene/control/export/ExportMenu.kt b/src/main/kotlin/stan/qodat/scene/control/export/ExportMenu.kt index 9c10d0f..bc1cb80 100644 --- a/src/main/kotlin/stan/qodat/scene/control/export/ExportMenu.kt +++ b/src/main/kotlin/stan/qodat/scene/control/export/ExportMenu.kt @@ -9,6 +9,7 @@ import stan.qodat.javafx.onChange import stan.qodat.scene.control.export.wavefront.WaveFrontFormat import stan.qodat.scene.runescape.animation.Animation import stan.qodat.scene.runescape.animation.AnimationFrame +import stan.qodat.scene.runescape.animation.AnimationLegacy import stan.qodat.util.onInvalidation import stan.qodat.util.setAndBind @@ -41,7 +42,7 @@ class ExportMenu : Menu("Export") { exportableProperty.set(exportable) } - fun setAnimation(animation: Animation) { + fun setAnimation(animation: AnimationLegacy) { animationProperty.set(animation) } @@ -118,4 +119,4 @@ class ExportMenu : Menu("Export") { }) }) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/control/export/gif/AnimationToGifTask.kt b/src/main/kotlin/stan/qodat/scene/control/export/gif/AnimationToGifTask.kt index a1817c6..6fd6636 100644 --- a/src/main/kotlin/stan/qodat/scene/control/export/gif/AnimationToGifTask.kt +++ b/src/main/kotlin/stan/qodat/scene/control/export/gif/AnimationToGifTask.kt @@ -7,9 +7,8 @@ import javafx.scene.image.WritableImage import javafx.util.Duration import kotlinx.coroutines.* import kotlinx.coroutines.javafx.JavaFx -import stan.qodat.javafx.JavaFXExecutor import stan.qodat.scene.control.export.gif.encoder.* -import stan.qodat.scene.runescape.animation.Animation +import stan.qodat.scene.runescape.animation.AnimationLegacy import stan.qodat.scene.runescape.animation.AnimationPlayer import java.io.FileOutputStream import java.nio.file.Path @@ -20,7 +19,7 @@ class AnimationToGifTask( private val exportPath: Path, private val scene: SubScene, private val animationPlayer: AnimationPlayer, - private val animation: Animation + private val animation: AnimationLegacy ) : Task() { override fun call(): Path { @@ -96,4 +95,4 @@ class AnimationToGifTask( out.close() return path.toPath() } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/control/export/gif/GifFormat.kt b/src/main/kotlin/stan/qodat/scene/control/export/gif/GifFormat.kt index f9fc803..a08db9a 100644 --- a/src/main/kotlin/stan/qodat/scene/control/export/gif/GifFormat.kt +++ b/src/main/kotlin/stan/qodat/scene/control/export/gif/GifFormat.kt @@ -5,11 +5,11 @@ import javafx.beans.binding.ObjectBinding import javafx.beans.property.ObjectProperty import stan.qodat.Properties import stan.qodat.scene.control.export.ExportFormat -import stan.qodat.scene.runescape.animation.Animation +import stan.qodat.scene.runescape.animation.AnimationLegacy import java.io.File import java.nio.file.Path -class GifFormat : ExportFormat { +class GifFormat : ExportFormat { override val defaultSaveDestinationProperty: ObjectBinding = Bindings.createObjectBinding( @@ -20,11 +20,11 @@ class GifFormat : ExportFormat { override val lastSaveDestinationProperty: ObjectProperty = Properties.lastGIFExportPath - override fun export(context: Animation, destination: Path) { + override fun export(context: AnimationLegacy, destination: Path) { TODO("Not yet implemented") } - override fun chooseSaveDestination(context: Animation): File? { + override fun chooseSaveDestination(context: AnimationLegacy): File? { TODO("Not yet implemented") } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/control/export/mp4/AnimationToMp4Task.kt b/src/main/kotlin/stan/qodat/scene/control/export/mp4/AnimationToMp4Task.kt index 795fce4..3085e6f 100644 --- a/src/main/kotlin/stan/qodat/scene/control/export/mp4/AnimationToMp4Task.kt +++ b/src/main/kotlin/stan/qodat/scene/control/export/mp4/AnimationToMp4Task.kt @@ -14,7 +14,7 @@ import org.jcodec.common.io.SeekableByteChannel import org.jcodec.common.model.Rational import org.jcodec.scale.AWTUtil import stan.qodat.javafx.JavaFXExecutor -import stan.qodat.scene.runescape.animation.Animation +import stan.qodat.scene.runescape.animation.AnimationLegacy import stan.qodat.scene.runescape.animation.AnimationPlayer import java.nio.file.Path import java.util.concurrent.Semaphore @@ -24,7 +24,7 @@ class AnimationToMp4Task( private val exportPath: Path, private val scene: SubScene, private val animationPlayer: AnimationPlayer, - private val animation: Animation + private val animation: AnimationLegacy ) : Task() { override fun call() { @@ -112,4 +112,4 @@ class AnimationToMp4Task( updateMessage("Generated MP4 at $file") } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/control/tree/AnimationFrameTreeItem.kt b/src/main/kotlin/stan/qodat/scene/control/tree/AnimationFrameTreeItem.kt index 5e363f8..e2708b5 100644 --- a/src/main/kotlin/stan/qodat/scene/control/tree/AnimationFrameTreeItem.kt +++ b/src/main/kotlin/stan/qodat/scene/control/tree/AnimationFrameTreeItem.kt @@ -19,10 +19,7 @@ import stan.qodat.cache.impl.oldschool.OldschoolCacheRuneLite import stan.qodat.javafx.hBox import stan.qodat.javafx.onSelected import stan.qodat.scene.SubScene3D -import stan.qodat.scene.runescape.animation.Animation -import stan.qodat.scene.runescape.animation.AnimationFrame -import stan.qodat.scene.runescape.animation.Transformation -import stan.qodat.scene.runescape.animation.TransformationType +import stan.qodat.scene.runescape.animation.* import stan.qodat.scene.runescape.entity.AnimatedEntity import stan.qodat.util.FrameTimeUtil import stan.qodat.util.onInvalidation @@ -67,26 +64,28 @@ class AnimationFrameTreeItem( } } - if (frame.definition != null) { - val fileIdText = Text().apply { - font = Font.font("Menlo", 11.0) - fill = Color.web("#A4B8C8") - } - val frameIdText = Text().apply { - font = Font.font("Menlo", FontWeight.EXTRA_BOLD, 11.0) - fill = Color.web("#A4B8C8") - } - updateFileAndFrameIdTexts(frame.idProperty.get(), fileIdText, frameIdText) - frame.idProperty.onInvalidation { - val hash = get() - updateFileAndFrameIdTexts(hash, fileIdText, frameIdText) - } - children += fileIdText - children += frameIdText - children += Text().apply { - fill = Color.web("#A4B8C8") - font = Font.font("Menlo", FontWeight.EXTRA_LIGHT, FontPosture.ITALIC, 11.0) - textProperty().setAndBind(frame.idProperty.asString()) + if (frame is AnimationFrameLegacy) { + if (frame.definition != null) { + val fileIdText = Text().apply { + font = Font.font("Menlo", 11.0) + fill = Color.web("#A4B8C8") + } + val frameIdText = Text().apply { + font = Font.font("Menlo", FontWeight.EXTRA_BOLD, 11.0) + fill = Color.web("#A4B8C8") + } + updateFileAndFrameIdTexts(frame.idProperty.get(), fileIdText, frameIdText) + frame.idProperty.onInvalidation { + val hash = get() + updateFileAndFrameIdTexts(hash, fileIdText, frameIdText) + } + children += fileIdText + children += frameIdText + children += Text().apply { + fill = Color.web("#A4B8C8") + font = Font.font("Menlo", FontWeight.EXTRA_LIGHT, FontPosture.ITALIC, 11.0) + textProperty().setAndBind(frame.idProperty.asString()) + } } } @@ -216,7 +215,8 @@ class AnimationFrameTreeItem( setOnAction { val index = animation.getFrameList().indexOf(frame) shiftFrameIndices(index) - animation.getFrameList().add(index + 1, frame.clone("frame[${index + 1}]")) + if (frame is AnimationFrameLegacy) + animation.getFrameList().add(index + 1, frame.clone("frame[${index + 1}]")) } } @@ -233,7 +233,8 @@ class AnimationFrameTreeItem( if (nextFrameIndex != index) { val nextFrame = frames[nextFrameIndex] - if (frame.transformationCountProperty.get() == nextFrame.transformationCountProperty.get()) { + if (frame is AnimationFrameLegacy && nextFrame is AnimationFrameLegacy) + if (frame.transformationCountProperty.get() == nextFrame.transformationCountProperty.get()) { val transforms = mutableListOf() for (i in 0 until frame.transformationCountProperty.get()) { val initialTransform = frame.transformationList.get(i) @@ -291,4 +292,4 @@ class AnimationFrameTreeItem( return selectionMesh } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/control/tree/AnimationTreeItem.kt b/src/main/kotlin/stan/qodat/scene/control/tree/AnimationTreeItem.kt index 2ff56c8..0f74d86 100644 --- a/src/main/kotlin/stan/qodat/scene/control/tree/AnimationTreeItem.kt +++ b/src/main/kotlin/stan/qodat/scene/control/tree/AnimationTreeItem.kt @@ -11,9 +11,7 @@ import stan.qodat.javafx.hBox import stan.qodat.javafx.onChange import stan.qodat.javafx.onExpanded import stan.qodat.scene.control.TreeItemListContextMenu -import stan.qodat.scene.runescape.animation.Animation -import stan.qodat.scene.runescape.animation.AnimationFrame -import stan.qodat.scene.runescape.animation.Transformation +import stan.qodat.scene.runescape.animation.* import stan.qodat.scene.runescape.entity.AnimatedEntity import stan.qodat.util.setAndBind @@ -46,16 +44,18 @@ class AnimationTreeItem( textProperty().setAndBind(animation.labelProperty) contextMenu = createContextMenu() } - children += Button("Pack").apply { - setOnAction { - if (animation.exportFrameArchiveId.get() == 0){ - val dialog = TextInputDialog() - dialog.showAndWait().ifPresent { - animation.exportFrameArchiveId.set(it.toIntOrNull()?:0) + if (animation is AnimationLegacy) { + children += Button("Pack").apply { + setOnAction { + if (animation.exportFrameArchiveId.get() == 0) { + val dialog = TextInputDialog() + dialog.showAndWait().ifPresent { + animation.exportFrameArchiveId.set(it.toIntOrNull() ?: 0) + } } + if (animation.exportFrameArchiveId.get() != 0) + QodatCache.encode(animation) } - if (animation.exportFrameArchiveId.get() != 0) - QodatCache.encode(animation) } } children += Button("Load").apply { @@ -96,6 +96,8 @@ class AnimationTreeItem( for (treeItem in children) { if (treeItem is AnimationFrameTreeItem) { val frame = treeItem.frame + if (frame !is AnimationFrameLegacy) + continue val transforms = frame.transformationList ?: continue transforms.onChange { treeItem.resetTransformTreeItems(transforms, entity, frame, treeView) } treeItem.resetTransformTreeItems(transforms, entity, frame, treeView) @@ -138,4 +140,4 @@ class AnimationTreeItem( companion object { val transformsContextMenuMap = HashMap>() } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/control/tree/EntityTreeItem.kt b/src/main/kotlin/stan/qodat/scene/control/tree/EntityTreeItem.kt index c29128b..f266989 100644 --- a/src/main/kotlin/stan/qodat/scene/control/tree/EntityTreeItem.kt +++ b/src/main/kotlin/stan/qodat/scene/control/tree/EntityTreeItem.kt @@ -10,7 +10,7 @@ import stan.qodat.Properties import stan.qodat.javafx.* import stan.qodat.scene.control.LockButton import stan.qodat.scene.control.export.ExportMenu -import stan.qodat.scene.runescape.animation.Animation +import stan.qodat.scene.runescape.animation.AnimationLegacy import stan.qodat.scene.runescape.entity.AnimatedEntity import stan.qodat.scene.runescape.entity.Entity import stan.qodat.util.setAndBind @@ -98,7 +98,7 @@ class EntityTreeItem( contextmenu { item("Add empty") { setOnAction { - val newAnimation = Animation("10000").apply { + val newAnimation = AnimationLegacy("10000").apply { idProperty.set(10_000) } val newAnimationTreeItem = AnimationTreeItem(newAnimation, entity, treeView, diff --git a/src/main/kotlin/stan/qodat/scene/control/tree/TransformTreeItem.kt b/src/main/kotlin/stan/qodat/scene/control/tree/TransformTreeItem.kt index 27acc1f..93f6ad7 100644 --- a/src/main/kotlin/stan/qodat/scene/control/tree/TransformTreeItem.kt +++ b/src/main/kotlin/stan/qodat/scene/control/tree/TransformTreeItem.kt @@ -17,6 +17,7 @@ import javafx.scene.shape.Sphere import stan.qodat.javafx.* import stan.qodat.scene.control.TreeItemListContextMenu import stan.qodat.scene.runescape.animation.AnimationFrame +import stan.qodat.scene.runescape.animation.AnimationFrameLegacy import stan.qodat.scene.runescape.animation.Transformation import stan.qodat.scene.runescape.animation.TransformationType import stan.qodat.scene.runescape.entity.Entity @@ -40,18 +41,20 @@ open class TransformTreeItem( hBox(isGraphic = true) { checkBox(transform.enabledProperty, biDirectional = true) label(transform.labelProperty) { - contextMenu = AnimationTreeItem.transformsContextMenuMap.getOrPut(frame) { - TreeItemListContextMenu( - list = frame.transformationList, - rootItem = transformsItem, - selectionModel = selectionModel, - itemCreator = { type, transform -> - if (type == TreeItemListContextMenu.CreateActionType.DUPLICATE) - transform.clone() - else - Transformation("New") - } - ) + if (frame is AnimationFrameLegacy) { + contextMenu = AnimationTreeItem.transformsContextMenuMap.getOrPut(frame) { + TreeItemListContextMenu( + list = frame.transformationList, + rootItem = transformsItem, + selectionModel = selectionModel, + itemCreator = { type, transform -> + if (type == TreeItemListContextMenu.CreateActionType.DUPLICATE) + transform.clone() + else + Transformation("New") + } + ) + } } } vBox { @@ -181,4 +184,4 @@ open class TransformTreeItem( sphere.translateY = (minY + height.toDouble().div(2)) sphere.translateZ = (minZ + depth.toDouble().div(2)) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/controller/AnimationController.kt b/src/main/kotlin/stan/qodat/scene/controller/AnimationController.kt index 527423f..c400401 100644 --- a/src/main/kotlin/stan/qodat/scene/controller/AnimationController.kt +++ b/src/main/kotlin/stan/qodat/scene/controller/AnimationController.kt @@ -14,6 +14,7 @@ import qodat.cache.definition.AnimatedEntityDefinition import stan.qodat.Properties import stan.qodat.javafx.onChange import stan.qodat.scene.runescape.animation.Animation +import stan.qodat.scene.runescape.animation.AnimationLegacy import stan.qodat.util.configureSearchFilter import stan.qodat.util.setAndBind import java.net.URL @@ -33,18 +34,18 @@ class AnimationController : Initializable, (AnimatedEntityDefinition) -> Array /** - * A [TextField] in which users can filter the [animations][Animation] contained in the [animationsListView]. + * A [TextField] in which users can filter the [animations][AnimationLegacy] contained in the [animationsListView]. * This is used to set the predicate of [filteredAnimations]. */ @FXML lateinit var searchTextField: TextField /** - * A [FilteredList] of [animations][Animation]. This list is backed by [animations]. + * A [FilteredList] of [animations][AnimationLegacy]. This list is backed by [animations]. */ lateinit var filteredAnimations: FilteredList /** - * An [ObservableList] of all the [animations][Animation] present in the loaded [Cache]. + * An [ObservableList] of all the [animations][AnimationLegacy] present in the loaded [Cache]. */ val animations: ObservableList = FXCollections.observableArrayList() @@ -85,4 +86,4 @@ class AnimationController : Initializable, (AnimatedEntityDefinition) -> Array, Searchable, ViewNodeProvider, Encoder { - - private lateinit var frames: ObservableList - private lateinit var skeletons: ObservableMap +abstract class Animation( + label: String, + open val definition: AnimationDefinition? = null, + val cache: Cache? = null +) : Transformer, Searchable, ViewNodeProvider { private lateinit var viewBox: HBox - private var currentFrameIndex = 0 - val treeItemProperty = SimpleObjectProperty() - val exportFrameArchiveId = SimpleIntegerProperty() val labelProperty = SimpleStringProperty(label) val nameProperty = SimpleStringProperty(label) val idProperty = SimpleIntegerProperty() val loopOffsetProperty = SimpleIntegerProperty(definition?.loopOffset ?: -1) val leftHandItemProperty = SimpleIntegerProperty(definition?.leftHandItem ?: -1) val rightHandItemProperty = SimpleIntegerProperty(definition?.rightHandItem ?: -1) + abstract override fun getFrameList(): ObservableList - fun getSkeletons(): ObservableMap { - if (!this::skeletons.isInitialized) { - try { - val skeletonsMap: Map = definition - ?.frameHashes - ?.map { getCacheSafe().getAnimationSkeletonDefinition(it) } - ?.distinctBy { it.id } - ?.associate { it.id to AnimationSkeleton("${it.id}", it) } - ?: emptyMap() - skeletons = FXCollections.observableMap(skeletonsMap) - } catch (e: Exception) { - Qodat.logException("Could not get animation {${getName()}}'s skeletons", e) - return FXCollections.emptyObservableMap() - } - } - return skeletons - } - - override fun getFrameList(): ObservableList { - if (!this::frames.isInitialized) { - try { - val framesArray = if (definition == null) emptyArray() else Array(definition.frameHashes.size) { idx -> - val frameDefinition = getCacheSafe().getFrameDefinition(definition.frameHashes[idx])!! - AnimationFrame( - name = "frame[$idx]", - definition = frameDefinition, - duration = definition.frameLengths[idx] - ).apply { - idProperty.set(this@Animation.definition.frameHashes[idx]) - } - } - frames = FXCollections.observableArrayList(*framesArray) - } catch (e: Exception) { - Qodat.logException("Could not get animation {${getName()}}'s frames", e) - return FXCollections.emptyObservableList() - } - } - return frames - } - - private fun getCacheSafe() = requireNotNull(cache) + protected fun getCacheSafe() = requireNotNull(cache) { "Cache must not be null if loading from cache definition!" } override fun setFrame(frameIndex: Int) { @@ -120,6 +67,8 @@ class Animation( override fun getName() = nameProperty.get() + abstract fun copy(): AnimationLegacy + override fun getViewNode(): Node { if (!this::viewBox.isInitialized) { viewBox = LabeledHBox(labelProperty, labelPrefix = "animation").apply { @@ -134,26 +83,10 @@ class Animation( } menu("export") { menuItem("GIF") { - BackgroundTasks.submit( - addProgressIndicator = true, - AnimationToGifTask( - exportPath = Properties.defaultExportsPath.get(), - scene = SubScene3D.subSceneProperty.get(), - animationPlayer = SubScene3D.contextProperty.get().animationPlayer, - animation = this@Animation - ) - ) + exportAsGif() } menuItem("mp4") { - BackgroundTasks.submit( - addProgressIndicator = true, - AnimationToMp4Task( - exportPath = Properties.defaultExportsPath.get(), - scene = SubScene3D.subSceneProperty.get(), - animationPlayer = SubScene3D.contextProperty.get().animationPlayer, - animation = this@Animation - ) - ) + exportAsMp4() } } } @@ -162,7 +95,9 @@ class Animation( return viewBox } - fun copy() = Animation(labelProperty.get(), definition, cache) + abstract fun exportAsMp4() + + abstract fun exportAsGif() companion object { fun createCellFactory() = Callback, ListCell> { @@ -174,5 +109,4 @@ class Animation( } } } - } diff --git a/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationExporter.kt b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationExporter.kt index d5e022b..6266f4c 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationExporter.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationExporter.kt @@ -13,13 +13,16 @@ import java.io.DataOutputStream class AnimationExporter { - fun encode(animation: Animation){ + fun encode(animation: AnimationLegacy){ val out = ByteArrayOutputStream() val os = DataOutputStream(out) val frames = animation.getFrameList() val firstFrame = frames.first() + if (firstFrame !is AnimationFrameLegacy) + error("Only legacy frames are supported") + val group = firstFrame.definition!!.transformationGroup os.write(encodeFrameMap(group)) @@ -62,7 +65,7 @@ class AnimationExporter { // storage.save(cache.store) } - private fun packFrames(animation: Animation): Int { + private fun packFrames(animation: AnimationLegacy): Int { val store = OldschoolCacheRuneLite.store val frameIndex = store.getIndex(IndexType.ANIMATIONS) @@ -108,7 +111,7 @@ class AnimationExporter { return archiveId } - fun encodeSequence(animation: Animation) : ByteArray { + fun encodeSequence(animation: AnimationLegacy) : ByteArray { val out = ByteArrayOutputStream() val os = DataOutputStream(out) @@ -116,6 +119,10 @@ class AnimationExporter { os.writeShort(animation.getFrameList().size) for (frame in animation.getFrameList()) { + + if (frame !is AnimationFrameLegacy) + error("Only legacy frames are supported") + os.writeShort(frame.getLength().toInt()) } @@ -147,6 +154,10 @@ class AnimationExporter { } private fun encodeFrame(animationFrame: AnimationFrame): ByteArray { + + if (animationFrame !is AnimationFrameLegacy) + error("Only legacy frames are supported") + val out = ByteArrayOutputStream() val os = DataOutputStream(out) diff --git a/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrame.kt b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrame.kt index b17e9d2..bb20487 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrame.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrame.kt @@ -4,18 +4,14 @@ import javafx.beans.property.SimpleBooleanProperty import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleStringProperty -import javafx.collections.FXCollections import javafx.scene.Node import javafx.scene.layout.HBox import javafx.util.Duration -import qodat.cache.definition.AnimationFrameDefinition -import qodat.cache.definition.AnimationTransformationGroup import stan.qodat.scene.control.LabeledHBox import stan.qodat.scene.provider.ViewNodeProvider import stan.qodat.scene.transform.TransformationGroup import stan.qodat.util.FrameTimeUtil import stan.qodat.util.Searchable -import stan.qodat.util.onInvalidation /** * TODO: add documentation @@ -23,47 +19,25 @@ import stan.qodat.util.onInvalidation * @author Stan van der Bend (https://www.rune-server.ee/members/StanDev/) * @since 28/01/2021 */ -class AnimationFrame( +sealed class AnimationFrame( name: String, - val definition: AnimationFrameDefinition?, duration: Int ) : TransformationGroup, Searchable, ViewNodeProvider { private lateinit var viewBox : HBox val labelProperty = SimpleStringProperty(name) + val durationProperty = SimpleObjectProperty(FrameTimeUtil.frame(duration)) val idProperty = SimpleIntegerProperty() - val transformationCountProperty = SimpleIntegerProperty(definition?.transformationCount?:0) - val transformationList = FXCollections.observableArrayList() - val durationProperty = SimpleObjectProperty(FrameTimeUtil.frame(duration)) val enabledProperty = SimpleBooleanProperty(true) - fun getLength() = FrameTimeUtil.toFrame(durationProperty.get()) - - init { - if (definition != null) { - val transformations = Array(transformationCountProperty.get()) { - val groupIndex = definition.transformationGroupAccessIndices[it] - Transformation( - "transform[$it]", - definition.transformationGroup.targetVertexGroupsIndices[groupIndex], - definition.transformationGroup.transformationTypes[groupIndex], - definition.transformationDeltaX[it], - definition.transformationDeltaY[it], - definition.transformationDeltaZ[it] - ).apply { - idProperty.set(it) - groupIndexProperty.set(groupIndex) - } - } - transformationList.addAll(transformations) - } - transformationList.onInvalidation { - transformationCountProperty.set(transformationList.size) - } + internal fun getFileId(hexString: String): Int { + return Integer.parseInt(hexString.substring(0, hexString.length - 4), 16) } - fun getTransformationCount() = transformationCountProperty.get() + internal fun getFrameId(hexString: String): Int { + return Integer.parseInt(hexString.substring(hexString.length - 4), 16) + } override fun durationProperty(): SimpleObjectProperty = durationProperty @@ -75,39 +49,10 @@ class AnimationFrame( return viewBox } + abstract fun getLength(): Long + override fun getName() = labelProperty.get() override fun toString() = getName() - - fun clone(name: String) = AnimationFrame( - name = name, - definition = object : AnimationFrameDefinition { - override val transformationCount: Int = transformationCountProperty.get() - override val transformationGroupAccessIndices: IntArray = - transformationList.map { it.groupIndexProperty.get() }.toIntArray() - override val transformationDeltaX: IntArray = - transformationList.map { it.getDeltaX() }.toIntArray() - override val transformationDeltaY: IntArray = - transformationList.map { it.getDeltaY() }.toIntArray() - override val transformationDeltaZ: IntArray = - transformationList.map { it.getDeltaZ() }.toIntArray() - override val transformationGroup: AnimationTransformationGroup = - definition!!.transformationGroup - }, - duration = getLength().toInt() - ).apply { - val hex = Integer.toHexString(this@AnimationFrame.idProperty.get()) - val fileId = getFileId(hex) - val frameId = getFrameId(hex) + 1 - this.idProperty.set(((fileId and 0xFFFF) shl 16) or (frameId and 0xFFFF)) - } - - internal fun getFileId(hexString: String): Int { - return Integer.parseInt(hexString.substring(0, hexString.length - 4), 16) - } - - internal fun getFrameId(hexString: String): Int { - return Integer.parseInt(hexString.substring(hexString.length - 4), 16) - } - } + diff --git a/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrameLegacy.kt b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrameLegacy.kt new file mode 100644 index 0000000..7ecbde4 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrameLegacy.kt @@ -0,0 +1,68 @@ +package stan.qodat.scene.runescape.animation + +import javafx.beans.property.SimpleIntegerProperty +import javafx.collections.FXCollections +import qodat.cache.definition.AnimationFrameLegacyDefinition +import qodat.cache.definition.AnimationTransformationGroup +import stan.qodat.util.FrameTimeUtil +import stan.qodat.util.onInvalidation + +class AnimationFrameLegacy( + name: String, + val definition: AnimationFrameLegacyDefinition?, + duration: Int +) : AnimationFrame(name, duration) { + + val transformationCountProperty = SimpleIntegerProperty(definition?.transformationCount?:0) + val transformationList = FXCollections.observableArrayList() + + init { + + if (definition != null) { + val transformations = Array(transformationCountProperty.get()) { + val groupIndex = definition.transformationGroupAccessIndices[it] + Transformation( + "transform[$it]", + definition.transformationGroup.targetVertexGroupsIndices[groupIndex], + definition.transformationGroup.transformationTypes[groupIndex], + definition.transformationDeltaX[it], + definition.transformationDeltaY[it], + definition.transformationDeltaZ[it] + ).apply { + idProperty.set(it) + groupIndexProperty.set(groupIndex) + } + } + transformationList.addAll(transformations) + } + transformationList.onInvalidation { + transformationCountProperty.set(transformationList.size) + } + } + override fun getLength() = FrameTimeUtil.toFrame(durationProperty.get()) + + fun getTransformationCount() = transformationCountProperty.get() + fun clone(name: String) = AnimationFrameLegacy( + name = name, + definition = object : AnimationFrameLegacyDefinition { + override val transformationCount: Int = transformationCountProperty.get() + override val transformationGroupAccessIndices: IntArray = + transformationList.map { it.groupIndexProperty.get() }.toIntArray() + override val transformationDeltaX: IntArray = + transformationList.map { it.getDeltaX() }.toIntArray() + override val transformationDeltaY: IntArray = + transformationList.map { it.getDeltaY() }.toIntArray() + override val transformationDeltaZ: IntArray = + transformationList.map { it.getDeltaZ() }.toIntArray() + override val transformationGroup: AnimationTransformationGroup = + definition!!.transformationGroup + }, + duration = getLength().toInt() + ).apply { + val hex = Integer.toHexString(this@AnimationFrameLegacy.idProperty.get()) + val fileId = getFileId(hex) + val frameId = getFrameId(hex) + 1 + this.idProperty.set(((fileId and 0xFFFF) shl 16) or (frameId and 0xFFFF)) + } + +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrameMaya.kt b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrameMaya.kt new file mode 100644 index 0000000..8d62d80 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationFrameMaya.kt @@ -0,0 +1,13 @@ +package stan.qodat.scene.runescape.animation + +import jagex.MayaAnimation + +class AnimationFrameMaya( + name: String, + private val duration: Int, + val index: Int, + val animation: MayaAnimation, +) : AnimationFrame(name, duration) +{ + override fun getLength() = duration.toLong() +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationLegacy.kt b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationLegacy.kt new file mode 100644 index 0000000..4655253 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationLegacy.kt @@ -0,0 +1,98 @@ +package stan.qodat.scene.runescape.animation + +import javafx.beans.property.SimpleIntegerProperty +import javafx.collections.FXCollections +import javafx.collections.ObservableList +import javafx.collections.ObservableMap +import qodat.cache.Cache +import qodat.cache.Encoder +import qodat.cache.definition.AnimationDefinition +import stan.qodat.Properties +import stan.qodat.Qodat +import stan.qodat.scene.SubScene3D +import stan.qodat.scene.control.export.gif.AnimationToGifTask +import stan.qodat.scene.control.export.mp4.AnimationToMp4Task +import stan.qodat.task.BackgroundTasks + + +class AnimationLegacy( + label: String, + definition: AnimationDefinition? = null, + cache: Cache? = null +) : Encoder, Animation(label, definition, cache) { + + private lateinit var frames: ObservableList + private lateinit var skeletons: ObservableMap + + val exportFrameArchiveId = SimpleIntegerProperty() + + fun getSkeletons(): ObservableMap { + if (!this::skeletons.isInitialized) { + try { + val skeletonsMap: Map = definition + ?.frameHashes + ?.map { getCacheSafe().getAnimationSkeletonDefinition(it) } + ?.distinctBy { it.id } + ?.associate { it.id to AnimationSkeleton("${it.id}", it) } + ?: emptyMap() + skeletons = FXCollections.observableMap(skeletonsMap) + } catch (e: Exception) { + Qodat.logException("Could not get animation {${getName()}}'s skeletons", e) + return FXCollections.emptyObservableMap() + } + } + return skeletons + } + + override fun getFrameList(): ObservableList { + if (!this::frames.isInitialized) { + try { + val framesArray = if (definition == null) + emptyArray() + else { + Array(definition.frameHashes.size) { idx -> + val frameDefinition = getCacheSafe().getFrameDefinition(definition.frameHashes[idx])!! + AnimationFrameLegacy( + name = "frame[$idx]", + definition = frameDefinition, + duration = definition.frameLengths[idx] + ).apply { + idProperty.set(this@AnimationLegacy.definition.frameHashes[idx]) + } + } + } + frames = FXCollections.observableArrayList(*framesArray) + } catch (e: Exception) { + Qodat.logException("Could not get animation {${getName()}}'s frames", e) + return FXCollections.emptyObservableList() + } + } + return frames + } + + override fun copy() = AnimationLegacy(labelProperty.get(), definition, cache) + + override fun exportAsMp4() { + BackgroundTasks.submit( + addProgressIndicator = true, + AnimationToMp4Task( + exportPath = Properties.defaultExportsPath.get(), + scene = SubScene3D.subSceneProperty.get(), + animationPlayer = SubScene3D.contextProperty.get().animationPlayer, + animation = this + ) + ) + } + + override fun exportAsGif() { + BackgroundTasks.submit( + addProgressIndicator = true, + AnimationToGifTask( + exportPath = Properties.defaultExportsPath.get(), + scene = SubScene3D.subSceneProperty.get(), + animationPlayer = SubScene3D.contextProperty.get().animationPlayer, + animation = this + ) + ) + } +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationMaya.kt b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationMaya.kt new file mode 100644 index 0000000..f06bbf7 --- /dev/null +++ b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationMaya.kt @@ -0,0 +1,58 @@ +package stan.qodat.scene.runescape.animation + +import jagex.MayaAnimation +import javafx.collections.FXCollections +import javafx.collections.ObservableList +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout +import net.runelite.cache.IndexType +import net.runelite.cache.fs.Index +import qodat.cache.Cache +import qodat.cache.definition.AnimationMayaDefinition +import stan.qodat.cache.impl.oldschool.OldschoolCacheRuneLite +import stan.qodat.util.runCatchingWithDialog + +class AnimationMaya(label: String, override val definition: AnimationMayaDefinition, cache: Cache? = null) : + Animation(label, definition, cache) { + + private val frames = FXCollections.observableArrayList() + + override fun getFrameList(): ObservableList { + if (frames.isEmpty()) { + val animationsArchive: Index = OldschoolCacheRuneLite.store.getIndex(IndexType.ANIMATIONS) + val framesArchive = OldschoolCacheRuneLite.store.getIndex(IndexType.SKELETONS) + val mayaAnimation = MayaAnimation.loadMayaAnimation( + animationsArchive, + framesArchive, + definition.animMayaID, + false + ) + runCatchingWithDialog("Loading Maya Animation") { + runBlocking { + withTimeout(5000) { + while (!mayaAnimation.isAnimationLoaded) { + Thread.sleep(100) + } + } + } + } + val animMayaDuration = definition.animMayaEnd - definition.animMayaStart + repeat(animMayaDuration) { index -> + val frame = AnimationFrameMaya( + name = "frame[$index]", + duration = 1, // TODO: figure out duration + index = index, + animation = mayaAnimation, + ) + frames.add(frame) + } + } + return frames + } + + override fun copy(): AnimationLegacy = + AnimationLegacy(labelProperty.get(), definition, cache) + + override fun exportAsMp4() = TODO() + override fun exportAsGif() = TODO() +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationPlayer.kt b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationPlayer.kt index e6af857..3b71a93 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationPlayer.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/animation/AnimationPlayer.kt @@ -8,4 +8,4 @@ import stan.qodat.scene.transform.TransformationTimer * @author Stan van der Bend (https://www.rune-server.ee/members/StanDev/) * @since 28/01/2021 */ -class AnimationPlayer : TransformationTimer() \ No newline at end of file +class AnimationPlayer : TransformationTimer() diff --git a/src/main/kotlin/stan/qodat/scene/runescape/model/ModelSkeleton.kt b/src/main/kotlin/stan/qodat/scene/runescape/model/ModelSkeleton.kt index e2dac7a..9f5a5c7 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/model/ModelSkeleton.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/model/ModelSkeleton.kt @@ -1,11 +1,14 @@ package stan.qodat.scene.runescape.model import fxyz3d.geometry.Point3F +import jagex.BoneTransform import javafx.geometry.Point3D import qodat.cache.definition.ModelDefinition -import qodat.cache.models.FaceNormal +import qodat.cache.models.RS2Model import qodat.cache.models.VertexNormal import stan.qodat.scene.runescape.animation.AnimationFrame +import stan.qodat.scene.runescape.animation.AnimationFrameLegacy +import stan.qodat.scene.runescape.animation.AnimationFrameMaya import stan.qodat.scene.runescape.animation.TransformationType import stan.qodat.scene.transform.Transformable import stan.qodat.util.COSINE @@ -55,8 +58,16 @@ open class ModelSkeleton(internal val modelDefinition: ModelDefinition) copyOriginalVertexValues() - resetPointOffset() + if (frame is AnimationFrameLegacy) { + animateLegacyFrame(frame) + } else if (frame is AnimationFrameMaya) { + animationMayaFrame(frame) + } + } + + private fun animateLegacyFrame(frame: AnimationFrameLegacy) { + resetPointOffset() for (transformation in frame.transformationList) { if (!transformation.enabledProperty.get()) @@ -77,15 +88,20 @@ open class ModelSkeleton(internal val modelDefinition: ModelDefinition) when (type) { TransformationType.SET_OFFSET -> offset(vertexGroupIndices, deltaX, deltaY, deltaZ) + TransformationType.TRANSLATE -> translate(vertexGroupIndices, deltaX, deltaY, deltaZ) + TransformationType.ROTATE -> rotate(vertexGroupIndices, deltaX, deltaY, deltaZ) + TransformationType.SCALE -> scale(vertexGroupIndices, deltaX, deltaY, deltaZ) + TransformationType.TRANSPARENCY -> { // TODO: Implement } + TransformationType.UNDEFINED -> { // TODO: Throw undefined exception? } @@ -93,11 +109,66 @@ open class ModelSkeleton(internal val modelDefinition: ModelDefinition) } } + val field2674 = BoneTransform() + var field2739: BoneTransform = BoneTransform() + var field2703: BoneTransform = BoneTransform() + + private fun animationMayaFrame(frame: AnimationFrameMaya) { + val anim = frame.animation + val animLength = anim.duration + val animSkeleton = anim.skeleton.mayaAnimationSkeleton + + anim.skeleton + if (modelDefinition !is RS2Model) + error("ModelDefinition is not an RS2Model") + + animSkeleton.applyAnimation(anim, frame.index) + + val duration = anim.duration + for (vertex in 0 until getVertexCount()) { + val boneIndices = modelDefinition.animayaGroups[vertex] + if (boneIndices != null && boneIndices.isNotEmpty()) { + val scales = modelDefinition.animayaScales[vertex] + field2703.zeroMatrix() + for ((index, boneIndex) in boneIndices.withIndex()) { + val bone = animSkeleton.getBone(boneIndex) + if (bone != null) { + field2674.setUniformScale(scales[index] / 255.0F) + field2739.copy(bone.getTransform(duration)) + field2739.combine(field2674) + field2703.addTransform(field2739) + } + } + applyMayaTransform(vertex, field2703) + } + } + + if (anim.hasTransformations()) { +// this.method4707(var1, var2) + } + } + + private fun applyMayaTransform(vertex: Int, transform: BoneTransform) { + val x = getX(vertex) + val y = -getY(vertex) + val z = -getZ(vertex) + val d = 1.0F + if (!this::originalVertexXValues.isInitialized) { + originalVertexXValues = vertexPositionsX.copyOf() + originalVertexYValues = vertexPositionsY.copyOf() + originalVertexZValues = vertexPositionsZ.copyOf() + } + setX(vertex, (transform.matrix[0] * x + transform.matrix[4] * y + transform.matrix[8] * z + transform.matrix[12] * d).toInt()) + setY(vertex, -(transform.matrix[1] * x + transform.matrix[5] * y + transform.matrix[9] * z + transform.matrix[13] * d).toInt()) + setZ(vertex, -(transform.matrix[2] * x + transform.matrix[6] * y + transform.matrix[10] * z + transform.matrix[14] * d).toInt()) +// println("\tv[$vertex] -> ${transform.matrix.contentToString()}") + } + private fun scale( - targetVertexGroupIndices: IntArray, - deltaX: Int, - deltaY: Int, - deltaZ: Int + targetVertexGroupIndices: IntArray, + deltaX: Int, + deltaY: Int, + deltaZ: Int, ) { for (vertexGroupIndex in targetVertexGroupIndices) { if (vertexGroupIndex < vertexGroups.size) { @@ -117,10 +188,10 @@ open class ModelSkeleton(internal val modelDefinition: ModelDefinition) } private fun rotate( - targetVertexGroupIndices: IntArray, - deltaX: Int, - deltaY: Int, - deltaZ: Int + targetVertexGroupIndices: IntArray, + deltaX: Int, + deltaY: Int, + deltaZ: Int, ) { val rotationX = convertRotationValue(deltaX) val rotationY = convertRotationValue(deltaY) @@ -181,10 +252,10 @@ open class ModelSkeleton(internal val modelDefinition: ModelDefinition) private fun convertRotationValue(value: Int) = (value and 255).times(8) private fun translate( - targetVertexGroupIndices: IntArray, - deltaX: Int, - deltaY: Int, - deltaZ: Int + targetVertexGroupIndices: IntArray, + deltaX: Int, + deltaY: Int, + deltaZ: Int, ) { for (vertexGroupIndex in targetVertexGroupIndices) { if (vertexGroupIndex < vertexGroups.size) { @@ -198,10 +269,10 @@ open class ModelSkeleton(internal val modelDefinition: ModelDefinition) } private fun offset( - targetVertexGroupIndices: IntArray, - deltaX: Int, - deltaY: Int, - deltaZ: Int + targetVertexGroupIndices: IntArray, + deltaX: Int, + deltaY: Int, + deltaZ: Int, ) { resetPointOffset() var iteratedVertexCount = 0 @@ -389,4 +460,4 @@ open class ModelSkeleton(internal val modelDefinition: ModelDefinition) } return normals } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/util/osrs_anim_parser.kt b/src/main/kotlin/stan/qodat/util/osrs_anim_parser.kt index bac175b..576942b 100644 --- a/src/main/kotlin/stan/qodat/util/osrs_anim_parser.kt +++ b/src/main/kotlin/stan/qodat/util/osrs_anim_parser.kt @@ -1,6 +1,7 @@ package stan.qodat.util import com.google.gson.GsonBuilder +import jagex.Buffer import javafx.application.Platform import javafx.concurrent.Task import net.runelite.cache.ConfigType @@ -8,6 +9,7 @@ import net.runelite.cache.IndexType import net.runelite.cache.NpcManager import net.runelite.cache.ObjectManager import net.runelite.cache.definitions.loaders.SequenceLoader +import net.runelite.cache.fs.Archive import net.runelite.cache.fs.ArchiveFiles import net.runelite.cache.fs.Store import stan.qodat.Properties @@ -28,47 +30,9 @@ private val gson = GsonBuilder().setPrettyPrinting().create() fun createNpcAnimsJsonDir( store: Store, - npcManager: NpcManager -) = object : Task() { - override fun call(): Void? { - val map = ConcurrentHashMap() - val storage = store.storage - val index = store.getIndex(IndexType.CONFIGS) - val seqArchive = index.getArchive(ConfigType.SEQUENCE.id) - val archiveData = storage.loadArchive(seqArchive) - val files = seqArchive.getFiles(archiveData) - val frameIndex = store.getIndex(IndexType.ANIMATIONS) - val animationFiles = files.files - val animations: Map> = animationFiles.parallelStream().map { file -> - val loader = SequenceLoader() - loader.configureForRevision(seqArchive.revision) - val anim = loader.load(file.fileId, file.contents) - Platform.runLater { - val progress = (100.0 * anim.id.toFloat().div(animationFiles.size)) - updateProgress(progress, 100.0) - updateMessage("Parsed animation (${anim.id + 1} / ${animationFiles.size}})") - } - val frames: Set = anim.frameIDs?.map { - val frameArchiveId = it shr 16 - val frameArchiveFileId = it and 65535 - - val frameArchive = requireNotNull(frameIndex.getArchive(frameArchiveId)) - { "Frame group null for $frameArchiveId file $frameArchiveFileId" } - val frameArchiveFiles = map.getOrPut(frameArchiveId) { - frameArchive.getFiles(storage.loadArchive(frameArchive))!! - } - val frameFile = frameArchiveFiles.findFile(frameArchiveFileId)!! - val frameContents = frameFile.contents - - val frameMapArchiveId = frameContents[0].toInt() and 0xff shl 8 or (frameContents[1].toInt() and 0xff) - frameMapArchiveId - }?.toSet() ?: emptySet() - (anim.id to frames) - }.collect(Collectors.toList()).toMap() - - updateMessage("Loaded all animation mappings!") - - + npcManager: NpcManager, +) = object : LoadAnimationTask(store) { + override fun matchAnimationsToSkeletons(skeletonIdsByAnimationId: Map>) { val total = npcManager.npcs.size val counter = AtomicInteger(0) npcManager.npcs.parallelStream().forEach { npc -> @@ -82,22 +46,22 @@ fun createNpcAnimsJsonDir( npc.rotate180Animation ).filter { it > 0 } try { + if (animationRef.isNotEmpty()) { val referenceFrames = animationRef.flatMap { - requireNotNull(animations[it]) { + requireNotNull(skeletonIdsByAnimationId[it]) { "Animation $it was null for npc ${npc.name}" } }.toSet() - val matches = animations.filter { entry -> + val matches = skeletonIdsByAnimationId.filter { entry -> entry.value.any { referenceFrames.any { reference -> reference == it } } } - try { val file = Properties.osrsCachePath.get().resolve("npc_anims/${npc.id}.json").toFile() val writer = FileWriter(file) @@ -118,68 +82,23 @@ fun createNpcAnimsJsonDir( e.printStackTrace() } } - return null } } fun createObjectAnimsJsonDir( store: Store, objectManager: ObjectManager, -) = object : Task() { - override fun call(): Void? { - val map = ConcurrentHashMap() - val storage = store.storage - val index = store.getIndex(IndexType.CONFIGS) - val seqArchive = index.getArchive(ConfigType.SEQUENCE.id) - val archiveData = storage.loadArchive(seqArchive) - val files = seqArchive.getFiles(archiveData) - val frameIndex = store.getIndex(IndexType.ANIMATIONS) - val animationFiles = files.files - val animations = animationFiles.parallelStream().map { file -> - val loader = SequenceLoader() - loader.configureForRevision(seqArchive.revision) - val anim = loader.load(file.fileId, file.contents) - Platform.runLater { - val progress = (100.0 * anim.id.toFloat().div(animationFiles.size)) - updateProgress(progress, 100.0) - updateMessage("Parsed animation (${anim.id + 1} / ${animationFiles.size}})") - } - val frames: Set = anim.frameIDs?.map { - val frameArchiveId = it shr 16 - val frameArchiveFileId = it and 65535 - - val frameArchive = frameIndex.getArchive(frameArchiveId)!! - val frameArchiveFiles = map.getOrPut(frameArchiveId) { - frameArchive.getFiles(storage.loadArchive(frameArchive))!! - } - val frameFile = frameArchiveFiles.findFile(frameArchiveFileId)!! - val frameContents = frameFile.contents - - val frameMapArchiveId = frameContents[0].toInt() and 0xff shl 8 or (frameContents[1].toInt() and 0xff) - frameMapArchiveId - }?.toSet() ?: emptySet() - (anim.id to frames) - }.collect(Collectors.toList()).toMap() - - Platform.runLater { - updateMessage("Loaded all animation mappings!") - } - +) = object : LoadAnimationTask(store) { + override fun matchAnimationsToSkeletons(skeletonIdsByAnimationId: Map>) { val total = objectManager.objects.size val counter = AtomicInteger(0) objectManager.objects.parallelStream().forEach { objectDefinition -> val animationRef = objectDefinition.animationID if (animationRef > 0) { - - val referenceFrames = animations[animationRef]!! - - val matches = animations.filter { entry -> - entry.value.any { - referenceFrames.any { reference -> - reference == it - } - } + val referenceFrames = skeletonIdsByAnimationId[animationRef]!! + val matches = skeletonIdsByAnimationId.filter { entry -> + entry.value.any { referenceFrames.any { reference -> reference == it } } } try { val file = @@ -199,6 +118,67 @@ fun createObjectAnimsJsonDir( } } } + } +} + + + +abstract class LoadAnimationTask( + private val store: Store, +) : Task() { + + private val map = ConcurrentHashMap() + private val storage = store.storage + private val index by lazy { store.getIndex(IndexType.CONFIGS) } + private val seqArchive by lazy { index.getArchive(ConfigType.SEQUENCE.id) } + private val archiveData by lazy { storage.loadArchive(seqArchive) } + private val files by lazy { seqArchive.getFiles(archiveData) } + private val animIndex by lazy { store.getIndex(IndexType.ANIMATIONS) } + private val animationFiles by lazy { files.files } + + override fun call(): Void? { + val skeletonIdsByAnimationId = associateAnimationBySkeletonIds() + updateMessage("Loaded all animation mappings!") + matchAnimationsToSkeletons(skeletonIdsByAnimationId) return null } + + abstract fun matchAnimationsToSkeletons(skeletonIdsByAnimationId: Map>) + + private fun associateAnimationBySkeletonIds() = animationFiles.parallelStream().map { file -> + val loader = SequenceLoader() + loader.configureForRevision(seqArchive.revision) + val anim = loader.load(file.fileId, file.contents) + Platform.runLater { + val progress = (100.0 * anim.id.toFloat().div(animationFiles.size)) + updateProgress(progress, 100.0) + updateMessage("Parsed animation (${anim.id + 1} / ${animationFiles.size}})") + } + val frameGroupIds: Set = if (anim.animMayaID >= 0) { + val animArchive: Archive = animIndex.getArchive(anim.animMayaID shr 16 and '\uffff'.code) + val animData = store.storage.loadArchive(animArchive) + val animFiles = animArchive.getFiles(animData) + val animFile = animFiles.findFile(anim.animMayaID and '\uffff'.code) + val data = animFile.contents + val buffer = Buffer(data) + val version = buffer.readUnsignedByte() + val frameGroupId = buffer.readUnsignedShort() + setOf(frameGroupId) + } else anim.frameIDs?.map { + val frameArchiveId = it shr 16 + val frameArchiveFileId = it and 65535 + + val frameArchive = requireNotNull(animIndex.getArchive(frameArchiveId)) + { "Frame group null for $frameArchiveId file $frameArchiveFileId" } + val frameArchiveFiles = map.getOrPut(frameArchiveId) { + frameArchive.getFiles(storage.loadArchive(frameArchive))!! + } + val frameFile = frameArchiveFiles.findFile(frameArchiveFileId)!! + val frameContents = frameFile.contents + + val frameMapArchiveId = frameContents[0].toInt() and 0xff shl 8 or (frameContents[1].toInt() and 0xff) + frameMapArchiveId + }?.toSet() ?: emptySet() + (anim.id to frameGroupIds) + }.collect(Collectors.toList()).toMap() }