[
  {
    "path": ".github/ISSUE_TEMPLATE/bugs.md",
    "content": "---\nname: Bug report\nabout: Something isn't working right\ntitle: '[BUG] '\nlabels: bug\nassignees: ''\n\n---\n\n**Description:**\n- Give a detailed explanation of the issue\n\n**Steps to Reproduce:**\n- Steps to reproduce this behaviour\n\n**Technical Information:**\n - Minecraft: \n - Fabric API: \n - Industrial Revolution: \n - Modpack: \n\n**Logs:**\n- Use a paste service, do not paste the logs directly here.\n\n**Details:**\nExtra information that might be helpful like screenshots or technical information about your computer.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/requests.md",
    "content": "---\nname: Request\nabout: I have a really cool idea!\ntitle: '[REQUEST] '\nlabels: enhancement\nassignees: ''\n\n---\n\n**Description:**\n- Describe your suggestion with as many details as possible.\n\n**Reason:**\n- Why do you think your suggestion should be added?\n"
  },
  {
    "path": ".gitignore",
    "content": "# gradle\n\n.gradle/\nbuild/\nout/\n\n# idea\n\n.idea/\n*.iml\n*.ipr\n*.iws\n\n# fabric\n\nrun/"
  },
  {
    "path": "LICENSE.txt",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><img src=\"https://i.imgur.com/1ZwHaAt.png\"></p>\n<h3 align=\"center\">Industrial Revolution</h3>\n<p align=\"center\">An Industrial mod made for Fabric.</p>\n<p align=\"center\">\n  <a title=\"Fabric API\" href=\"https://github.com/FabricMC/fabric\">\n    <img src=\"https://i.imgur.com/Ol1Tcf8.png\" width=\"151\" height=\"50\" />\n  </a>\n  <a title=\"Fabric Language Kotlin\" href=\"https://github.com/FabricMC/fabric-language-kotlin\" target=\"_blank\" rel=\"noopener noreferrer\">\n    <img src=\"https://i.imgur.com/c1DH9VL.png\" width=\"171\" height=\"50\" />\n  </a>\n</p>\n<p align=\"center\">\n  <a href=\"https://opensource.org/licenses/Apache-2.0\"><img src=\"https://img.shields.io/badge/License-Apache%202.0-brightgreen.svg\"></a>\n    <a href=\"https://www.curseforge.com/minecraft/mc-mods/industrial-revolution\"><img src=\"http://cf.way2muchnoise.eu/versions/391708_latest.svg\"></a>\n  <a href=\"https://www.curseforge.com/minecraft/mc-mods/industrial-revolution\"><img src=\"http://cf.way2muchnoise.eu/391708.svg\"></a>\n</p>\n\n## Features\nFor a complete list of features please check the mods [official page](https://www.curseforge.com/minecraft/mc-mods/industrial-revolution)\n\n## License\nDistributed under the Apache License, Version 2.0. See `LICENSE` for more information.\n\n## Build\nIf you want to build this yourself, please clone the repository and execute `gradlew build` in the projects folder. \n\nArtifacts will be generated at `/build/libs`\n"
  },
  {
    "path": "build.gradle",
    "content": "plugins {\n    id \"fabric-loom\"\n    id \"maven-publish\"\n    id \"org.jetbrains.kotlin.jvm\"\n}\n\nsourceCompatibility = JavaVersion.VERSION_16\ntargetCompatibility = JavaVersion.VERSION_16\n\narchivesBaseName = project.archives_base_name\nversion = project.mod_version\ngroup = project.maven_group\n\nrepositories {\n    maven {\n        name = \"Modmuss50\"\n        url = \"https://maven.modmuss50.me/\"\n        content {\n            includeGroup \"RebornCore\"\n            includeGroup \"TechReborn\"\n            includeGroup \"teamreborn\"\n        }\n    }\n    maven {\n        url \"https://maven.shedaniel.me/\"\n        content {\n            includeGroup \"dev.architectury\"\n            includeGroup \"me.shedaniel\"\n            includeGroup \"me.shedaniel.cloth\"\n            includeGroup \"me.shedaniel.cloth.api\"\n        }\n    }\n    maven {\n        url = \"https://maven.fabricmc.net/\"\n        content {\n            includeGroup \"net.fabricmc\"\n        }\n    }\n    maven {\n        name = \"BuildCraft\"\n        url = \"https://mod-buildcraft.com/maven\"\n        content {\n            includeGroup \"alexiil.mc.lib\"\n        }\n    }\n    maven {\n        name = \"CottonMC\"\n        url = \"https://server.bbkr.space/artifactory/libs-release\"\n        content {\n            includeGroup \"io.github.cottonmc\"\n        }\n    }\n    maven {\n        url = \"https://maven.terraformersmc.com/releases/\"\n        content {\n            includeGroup \"com.terraformersmc\"\n        }\n    }\n    maven {\n        name = \"Patchouli\"\n        url = \"https://maven.blamejared.com\"\n        content {\n            includeGroup \"vazkii.patchouli\"\n        }\n    }\n    maven {\n        url = \"https://jitpack.io\"\n        content {\n            includeGroup \"com.github.Technici4n\"\n            includeGroup \"com.github.Draylar\"\n            includeGroup \"com.github.Draylar.omega-config\"\n            includeGroup \"com.github.emilyploszaj\"\n        }\n    }\n    maven {\n        url \"https://oskarstrom.net/maven\"\n        content {\n            includeGroup \"net.oskarstrom\"\n        }\n    }\n    maven {\n        name = \"Cafeteria Development\"\n        url = 'https://maven.cafeteria.dev'\n        content {\n            includeGroup 'dev.cafeteria'\n            includeGroup 'me.luligabi'\n            includeGroup 'net.adriantodt.fabricmc'\n        }\n    }\n    maven {\n        name = 'Ladysnake Mods'\n        url = 'https://ladysnake.jfrog.io/artifactory/mods'\n        content {\n            includeGroup 'io.github.ladysnake'\n            includeGroupByRegex 'io\\\\.github\\\\.onyxstudios.*'\n        }\n    }\n}\n\ndependencies {\n\n    dependencies.ext.lib = { dep, optional = false ->\n        modImplementation(dep) {\n            exclude group: \"net.fabricmc.fabric-api\"\n            exclude group: \"com.jamieswhiteshirt\"\n            exclude group: \"io.github.prospector\"\n        }\n        if (!optional) {\n            include(dep) {\n                exclude group: \"net.fabricmc.fabric-api\"\n                exclude group: \"com.jamieswhiteshirt\"\n                exclude group: \"io.github.prospector\"\n            }\n        }\n    }\n\n    minecraft \"com.mojang:minecraft:${project.minecraft_version}\"\n    mappings \"net.fabricmc:yarn:${project.yarn_mappings}:v2\"\n    modImplementation \"net.fabricmc:fabric-loader:${project.loader_version}\"\n\n    modImplementation \"net.fabricmc.fabric-api:fabric-api:${project.fabric_version}\"\n\n    modImplementation \"net.fabricmc:fabric-language-kotlin:${project.fabric_kotlin_version}\"\n\n    dependencies.ext.lib(\"teamreborn:energy:${project.tr_energy_version}\")\n    dependencies.ext.lib(\"io.github.cottonmc:LibGui:${project.libgui_version}\")\n    dependencies.ext.lib(\"vazkii.patchouli:Patchouli:${project.patchouli_version}\")\n    dependencies.ext.lib(\"alexiil.mc.lib:libblockattributes-core:${project.lba_version}\")\n    dependencies.ext.lib(\"alexiil.mc.lib:libblockattributes-fluids:${project.lba_version}\")\n    dependencies.ext.lib(\"alexiil.mc.lib:libblockattributes-items:${project.lba_version}\")\n    dependencies.ext.lib(\"com.github.Draylar:magna:${project.magna_version}\")\n    dependencies.ext.lib(\"com.github.emilyploszaj:step-height-entity-attribute:v1.0.1\")\n    dependencies.ext.lib(\"dev.cafeteria:fake-player-api:${project.fakeplayerapi_version}\")\n    dependencies.ext.lib(\"me.luligabi:NoIndium:1.0\")\n    dependencies.ext.lib(\"me.shedaniel:RoughlyEnoughItems-fabric:${project.rei_version}\", true)\n\n    modRuntimeOnly(\"com.terraformersmc:modmenu:${project.modmenu_version}\") {\n        exclude group: \"net.fabricmc.fabric-api\", module: \"fabric-api\"\n    }\n\n    // DashLoader Stuff\n    modCompileOnly(\"net.oskarstrom:DashLoader:2.0-dev12\")\n    dependencies.ext.lib(\"io.activej:activej-serializer:${project.activej_version}\", true)\n    modRuntimeOnly(\"org.yaml:snakeyaml:1.27\")\n}\n\nloom {\n    accessWidenerPath = file(\"src/main/resources/indrev.accesswidener\")\n}\n\nprocessResources {\n    inputs.property \"version\", project.version\n\n    filesMatching(\"fabric.mod.json\") {\n        expand \"version\": project.version\n    }\n}\n\n// ensure that the encoding is set to UTF-8, no matter what the system default is\n// this fixes some edge cases with special characters not displaying correctly\n// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html\ntasks.withType(JavaCompile) {\n    it.options.encoding = \"UTF-8\"\n    it.options.release = 16\n}\n\n\njava {\n\t// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the \"build\" task\n\t// if it is present.\n\t// If you remove this line, sources will not be generated.\n\twithSourcesJar()\n}\n\njar {\n    from(\"LICENSE\") {\n\t\trename { \"${it}_${project.archivesBaseName}\"}\n\t}\n\n    exclude(\"me/steven/indrev/datagen\")\n}\n\n// configure the maven publication\npublishing {\n    publications {\n        mavenJava(MavenPublication) {\n            // add all the jars that should be included when publishing to maven\n            artifact(remapJar) {\n                builtBy remapJar\n            }\n            artifact(sourcesJar) {\n                builtBy remapSourcesJar\n            }\n        }\n    }\n\n    // select the repositories you want to publish to\n    repositories {\n        maven {\n            url \"https://maven.cafeteria.dev/releases\"\n            credentials {\n                username = project.property(\"mcdUsername\")\n                password = project.property(\"mcdPassword\")\n            }\n            authentication {\n                basic(BasicAuthentication)\n            }\n        }\n    }\n}\n\ncompileKotlin.kotlinOptions.jvmTarget = \"16\"\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.3.1-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "kotlin.code.style=official\norg.gradle.jvmargs=-Xmx2G\n# Fabric Properties\nminecraft_version=1.18.2\nyarn_mappings=1.18.2+build.3\nloader_version=0.13.3\n#Fabric api\nfabric_version=0.51.1+1.18.2\nloom_version=0.11-SNAPSHOT\n# Mod Properties\nmod_version=1.14.0-BETA\nmaven_group=me.steven\narchives_base_name=indrev\n# Kotlin\nkotlin_version=1.6.20\nfabric_kotlin_version=1.7.2+kotlin.1.6.20\n# LibGui\nlibgui_version=5.3.2+1.18.2\n# Mod Menu\nmodmenu_version=3.1.0\n# Roughly Enough Items\nrei_version=8.0.442\n# Patchouli\npatchouli_version=1.18.2-66-FABRIC\n# LBA\nlba_version=0.10.0\n# Magna\nmagna_version=1.7.0-1.18\n# TR Energy\ntr_energy_version=2.0.0-beta1\n# Fake Player API\nfakeplayerapi_version=0.3.0\n# ActiveJ\nactivej_version=4.3"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    \n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "settings.gradle",
    "content": "pluginManagement {\n    repositories {\n        jcenter()\n        maven {\n            name = 'Fabric'\n            url = 'https://maven.fabricmc.net/'\n        }\n        gradlePluginPortal()\n    }\n\n    plugins {\n        id 'fabric-loom' version loom_version\n        id \"org.jetbrains.kotlin.jvm\" version kotlin_version\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/AprilFools.java",
    "content": "package me.steven.indrev;\n\nimport net.fabricmc.api.EnvType;\nimport net.fabricmc.api.Environment;\nimport net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;\nimport net.fabricmc.loader.api.FabricLoader;\nimport net.minecraft.text.LiteralText;\nimport net.minecraft.util.Formatting;\nimport net.minecraft.util.registry.Registry;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.time.LocalDate;\n\n@Environment(EnvType.CLIENT)\npublic final class AprilFools {\n    private static final File CHECK =\n        new File(FabricLoader.getInstance().getGameDir().toString(), \".indrev_\" + LocalDate.now().getYear());\n\n    public static void init() {\n        if (isToday()) {\n            ItemTooltipCallback.EVENT.register((itemStack, tooltipContext, list) -> {\n                String itemNamespace = Registry.ITEM.getId(itemStack.getItem()).getNamespace();\n                if (itemNamespace.equals(IndustrialRevolution.MOD_ID) && list.size() > 1) {\n                    list.add(new LiteralText(\"\")); // break line\n                    list.add(new LiteralText(\"every good modpack uses forge...\").formatted(Formatting.ITALIC));\n                }\n            });\n\n            try { CHECK.createNewFile(); }\n            catch (IOException ex) { ex.printStackTrace(); }\n        }\n    }\n\n    public static boolean isToday() {\n        return false;\n        /*LocalDate now = LocalDate.now();\n        return now.getDayOfMonth() == 1 && now.getMonthValue() == 4 && !CHECK.exists();*/\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/FabricRecipeRemainder.java",
    "content": "package me.steven.indrev;\n\nimport net.minecraft.entity.player.PlayerEntity;\nimport net.minecraft.inventory.CraftingInventory;\nimport net.minecraft.item.ItemStack;\n\npublic interface FabricRecipeRemainder {\n    ItemStack getRemainder(ItemStack stack, CraftingInventory craftingInventory, PlayerEntity playerEntity);\n}"
  },
  {
    "path": "src/main/java/me/steven/indrev/IREnergyStorage.java",
    "content": "package me.steven.indrev;\n\nimport net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions;\nimport net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext;\nimport net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant;\nimport net.minecraft.util.math.Direction;\nimport org.jetbrains.annotations.Nullable;\nimport team.reborn.energy.api.EnergyStorage;\n\n\n/**\n * Copied from SimpleSidedEnergyContainer and modified to fit IR's needs\n */\npublic abstract class IREnergyStorage extends SnapshotParticipant<Long> {\n    private final SideStorage[] sideStorages = new SideStorage[7];\n\n    public IREnergyStorage() {\n        for (int i = 0; i < 7; ++i) {\n            sideStorages[i] = new SideStorage(i == 6 ? null : Direction.byId(i));\n        }\n    }\n\n    /**\n     * @return The current capacity of this storage.\n     */\n    public abstract long getCapacity();\n\n    /**\n     * @return The maximum amount of energy that can be inserted in a single operation from the passed side.\n     */\n    public abstract long getMaxInsert(@Nullable Direction side);\n\n    /**\n     * @return The maximum amount of energy that can be extracted in a single operation from the passed side.\n     */\n    public abstract long getMaxExtract(@Nullable Direction side);\n\n    /**\n     * @return An {@link EnergyStorage} implementation for the passed side.\n     */\n    public EnergyStorage getSideStorage(@Nullable Direction side) {\n        return sideStorages[side == null ? 6 : side.getId()];\n    }\n\n    public long getAmount() {\n        return 0;\n    }\n\n    public void setAmount(long v) {\n\n    }\n\n    @Override\n    protected Long createSnapshot() {\n        return getAmount();\n    }\n\n    @Override\n    protected void readSnapshot(Long snapshot) {\n        setAmount(snapshot);\n    }\n\n    private class SideStorage implements EnergyStorage {\n        private final Direction side;\n\n        private SideStorage(Direction side) {\n            this.side = side;\n        }\n\n        @Override\n        public boolean supportsInsertion() {\n            return getMaxInsert(side) > 0;\n        }\n\n        @Override\n        public long insert(long maxAmount, TransactionContext transaction) {\n            StoragePreconditions.notNegative(maxAmount);\n\n            long inserted = Math.min(getMaxInsert(side), Math.min(maxAmount, getCapacity() - getAmount()));\n\n            if (inserted > 0) {\n                updateSnapshots(transaction);\n\n                setAmount(getAmount() + inserted);\n                return inserted;\n            }\n\n            return 0;\n        }\n\n        @Override\n        public boolean supportsExtraction() {\n            return getMaxExtract(side) > 0;\n        }\n\n        @Override\n        public long extract(long maxAmount, TransactionContext transaction) {\n            StoragePreconditions.notNegative(maxAmount);\n\n            long extracted = Math.min(getMaxExtract(side), Math.min(maxAmount, getAmount()));\n\n            if (extracted > 0) {\n                updateSnapshots(transaction);\n                setAmount(getAmount() - extracted);\n                return extracted;\n            }\n\n            return 0;\n        }\n\n        @Override\n        public long getAmount() {\n            return IREnergyStorage.this.getAmount();\n        }\n\n        @Override\n        public long getCapacity() {\n            return IREnergyStorage.this.getCapacity();\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/WCustomTabPanel.java",
    "content": "/*\nOriginal code by Juuxel, part of https://github.com/CottonMC/LibGui\n\nMIT License\n\nCopyright (c) 2018 The Cotton Project\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\npackage me.steven.indrev;\n\nimport io.github.cottonmc.cotton.gui.client.BackgroundPainter;\nimport io.github.cottonmc.cotton.gui.client.LibGui;\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing;\nimport io.github.cottonmc.cotton.gui.widget.*;\nimport io.github.cottonmc.cotton.gui.widget.data.Axis;\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment;\nimport io.github.cottonmc.cotton.gui.widget.data.InputResult;\nimport io.github.cottonmc.cotton.gui.widget.icon.Icon;\nimport net.fabricmc.api.EnvType;\nimport net.fabricmc.api.Environment;\nimport net.minecraft.client.MinecraftClient;\nimport net.minecraft.client.font.TextRenderer;\nimport net.minecraft.client.sound.PositionedSoundInstance;\nimport net.minecraft.client.util.math.MatrixStack;\nimport net.minecraft.sound.SoundEvents;\nimport net.minecraft.text.Text;\nimport net.minecraft.util.Identifier;\nimport org.jetbrains.annotations.Nullable;\n\nimport java.util.*;\nimport java.util.function.Consumer;\n\npublic class WCustomTabPanel extends WPanel {\n    private static final int TAB_PADDING = 4;\n    private static final int TAB_WIDTH = 28;\n    private static final int TAB_HEIGHT = 25;\n    private static final int PANEL_PADDING = 8; // The padding of BackgroundPainter.VANILLA\n    private static final int ICON_SIZE = 16;\n    private final WBox tabRibbon = new WBox(Axis.HORIZONTAL).setSpacing(1);\n    private final List<WTab> tabWidgets = new ArrayList<>();\n    private final WCardPanel mainPanel = new WCardPanel();\n\n    /**\n     * Constructs a new tab panel.\n     */\n    public WCustomTabPanel() {\n        add(tabRibbon, 0, 0);\n        add(mainPanel, PANEL_PADDING, TAB_HEIGHT + PANEL_PADDING);\n    }\n\n    public void add(WWidget widget, int x, int y) {\n        children.add(widget);\n        widget.setParent(this);\n        widget.setLocation(x, y);\n        expandToFit(widget);\n    }\n\n    @Override\n    public WPanel setBackgroundPainter(BackgroundPainter painter) {\n        return super.setBackgroundPainter(\n                BackgroundPainter.createLightDarkVariants(\n                        BackgroundPainter.createNinePatch(new Identifier(\"libgui\", \"textures/widget/panel_light.png\")).setPadding(0).setTopPadding(-25),\n                        BackgroundPainter.createNinePatch(new Identifier(\"libgui\", \"textures/widget/panel_dark.png\")).setPadding(8).setTopPadding(-25)\n                ));\n    }\n\n    // dont bother it's just bad code ik\n    public WPanel setForceBackgroundPainter(BackgroundPainter painter) {\n        return super.setBackgroundPainter(painter);\n    }\n\n    /**\n     * Adds a tab to this panel.\n     *\n     * @param tab the added tab\n     */\n    public void add(WCustomTabPanel.Tab tab) {\n        WTab tabWidget = new WTab(tab);\n\n        if (tabWidgets.isEmpty()) {\n            tabWidget.selected = true;\n        }\n\n        tabWidgets.add(tabWidget);\n        tabRibbon.add(tabWidget, TAB_WIDTH, TAB_HEIGHT + TAB_PADDING);\n        mainPanel.add(tab.getWidget());\n    }\n\n    /**\n     * Configures and adds a tab to this panel.\n     *\n     * @param widget       the contained widget\n     * @param configurator the tab configurator\n     */\n    public void add(WWidget widget, Consumer<WCustomTabPanel.Tab.Builder> configurator) {\n        WCustomTabPanel.Tab.Builder builder = new WCustomTabPanel.Tab.Builder(widget);\n        configurator.accept(builder);\n        add(builder.build());\n    }\n\n    @Override\n    public void setSize(int x, int y) {\n        super.setSize(x, y);\n        tabRibbon.setSize(x, TAB_HEIGHT);\n    }\n\n    @Environment(EnvType.CLIENT)\n    @Override\n    public void addPainters() {\n        super.addPainters();\n    }\n\n    /**\n     * The data of a tab.\n     */\n    public static class Tab {\n        private final Text title;\n        private final Icon icon;\n        private final WWidget widget;\n        private final Consumer<TooltipBuilder> tooltip;\n\n        /**\n         * Constructs a tab.\n         *\n         * @param title   the tab title\n         * @param icon    the tab icon\n         * @param widget  the widget contained in the tab\n         * @param tooltip the tab tooltip\n         * @throws IllegalArgumentException if both the title and the icon are null\n         * @throws NullPointerException     if either the widget or the tooltip is null\n         */\n        public Tab(Text title, Icon icon, WWidget widget, Consumer<TooltipBuilder> tooltip) {\n            if (title == null && icon == null) {\n                throw new IllegalArgumentException(\"A tab must have a title or an icon\");\n            }\n\n            this.title = title;\n            this.icon = icon;\n            this.widget = Objects.requireNonNull(widget, \"widget\");\n            this.tooltip = tooltip;\n        }\n\n        /**\n         * Gets the title of this tab.\n         *\n         * @return the title, or null if there's no title\n         */\n        public Text getTitle() {\n            return title;\n        }\n\n        /**\n         * Gets the icon of this tab.\n         *\n         * @return the icon, or null if there's no title\n         */\n        public Icon getIcon() {\n            return icon;\n        }\n\n        /**\n         * Gets the contained widget of this tab.\n         *\n         * @return the contained widget\n         */\n        public WWidget getWidget() {\n            return widget;\n        }\n\n        /**\n         * Adds this widget's tooltip to the {@code tooltip} builder.\n         *\n         * @param tooltip the tooltip builder\n         */\n        public void addTooltip(TooltipBuilder tooltip) {\n            if (this.tooltip != null)\n                this.tooltip.accept(tooltip);\n        }\n\n        /**\n         * A builder for tab data.\n         */\n        public static final class Builder {\n            @Nullable\n            private Text title;\n            @Nullable\n            private Icon icon;\n            private final WWidget widget;\n            private final List<Text> tooltip = new ArrayList<>();\n\n            /**\n             * Constructs a new tab data builder.\n             *\n             * @param widget the contained widget\n             * @throws NullPointerException if the widget is null\n             */\n            public Builder(WWidget widget) {\n                this.widget = Objects.requireNonNull(widget, \"widget\");\n            }\n\n            /**\n             * Sets the tab title.\n             *\n             * @param title the new title\n             * @return this builder\n             * @throws NullPointerException if the title is null\n             */\n            public Builder title(Text title) {\n                this.title = Objects.requireNonNull(title, \"title\");\n                return this;\n            }\n\n            /**\n             * Sets the tab icon.\n             *\n             * @param icon the new icon\n             * @return this builder\n             * @throws NullPointerException if the icon is null\n             */\n            public Builder icon(Icon icon) {\n                this.icon = Objects.requireNonNull(icon, \"icon\");\n                return this;\n            }\n\n            /**\n             * Adds lines to the tab's tooltip.\n             *\n             * @param lines the added lines\n             * @return this builder\n             * @throws NullPointerException if the line array is null\n             */\n            public Builder tooltip(Text... lines) {\n                Objects.requireNonNull(lines, \"lines\");\n                Collections.addAll(tooltip, lines);\n\n                return this;\n            }\n\n            /**\n             * Adds lines to the tab's tooltip.\n             *\n             * @param lines the added lines\n             * @return this builder\n             * @throws NullPointerException if the line collection is null\n             */\n            public Builder tooltip(Collection<? extends Text> lines) {\n                Objects.requireNonNull(lines, \"lines\");\n                tooltip.addAll(lines);\n                return this;\n            }\n\n            /**\n             * Builds a tab from this builder.\n             *\n             * @return the built tab\n             */\n            public Tab build() {\n                Consumer<TooltipBuilder> tooltip = null;\n\n                if (!this.tooltip.isEmpty()) {\n                    //noinspection Convert2Lambda\n                    tooltip = new Consumer<TooltipBuilder>() {\n                        @Environment(EnvType.CLIENT)\n                        @Override\n                        public void accept(TooltipBuilder builder) {\n                            builder.add(WCustomTabPanel.Tab.Builder.this.tooltip.toArray(new Text[0]));\n                        }\n                    };\n                }\n\n                return new Tab(title, icon, widget, tooltip);\n            }\n        }\n    }\n\n    private final class WTab extends WWidget {\n        private final WCustomTabPanel.Tab data;\n        boolean selected = false;\n\n        WTab(WCustomTabPanel.Tab data) {\n            this.data = data;\n        }\n\n        @Override\n        public boolean canFocus() {\n            return true;\n        }\n\n        @Environment(EnvType.CLIENT)\n        @Override\n        public InputResult onClick(int x, int y, int button) {\n            super.onClick(x, y, button);\n\n            MinecraftClient.getInstance().getSoundManager().play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0F));\n\n            for (WTab tab : tabWidgets) {\n                tab.selected = (tab == this);\n            }\n\n            mainPanel.setSelectedCard(data.getWidget());\n            WCustomTabPanel.this.layout();\n            return InputResult.PROCESSED;\n        }\n\n        @Environment(EnvType.CLIENT)\n        @Override\n        public void onKeyPressed(int ch, int key, int modifiers) {\n            if (isActivationKey(ch)) {\n                onClick(0, 0, 0);\n            }\n        }\n\n        @Environment(EnvType.CLIENT)\n        @Override\n        public void paint(MatrixStack matrices, int x, int y, int mouseX, int mouseY) {\n            TextRenderer renderer = MinecraftClient.getInstance().textRenderer;\n            Text title = data.getTitle();\n            Icon icon = data.getIcon();\n\n            if (title != null) {\n                int width = TAB_WIDTH + renderer.getWidth(title);\n                if (icon == null) width = Math.max(TAB_WIDTH, width - ICON_SIZE);\n\n                if (this.width != width) {\n                    setSize(width, this.height);\n                    getParent().layout();\n                }\n            }\n\n            (selected ? WCustomTabPanel.Painters.SELECTED_TAB : WCustomTabPanel.Painters.UNSELECTED_TAB).paintBackground(matrices, x, y, this);\n            if (isFocused()) {\n                (selected ? WCustomTabPanel.Painters.SELECTED_TAB_FOCUS_BORDER : Painters.UNSELECTED_TAB_FOCUS_BORDER).paintBackground(matrices, x, y, this);\n            }\n\n            int iconX = 6;\n\n            if (title != null) {\n                int titleX = (icon != null) ? iconX + ICON_SIZE + 1 : 0;\n                int titleY = (height - TAB_PADDING - renderer.fontHeight) / 2 + 1;\n                int width = (icon != null) ? this.width - iconX - ICON_SIZE : this.width;\n                HorizontalAlignment align = (icon != null) ? HorizontalAlignment.LEFT : HorizontalAlignment.CENTER;\n\n                int color;\n                if (LibGui.isDarkMode()) {\n                    color = WLabel.DEFAULT_DARKMODE_TEXT_COLOR;\n                } else {\n                    color = selected ? WLabel.DEFAULT_TEXT_COLOR : 0xEEEEEE;\n                }\n\n                ScreenDrawing.drawString(matrices, title.asOrderedText(), align, x + titleX, y + titleY, width, color);\n            }\n\n            if (icon != null) {\n                icon.paint(matrices, x + iconX, (y + (height - TAB_PADDING - ICON_SIZE) / 2) + 1, ICON_SIZE);\n            }\n        }\n\n        @Override\n        public void addTooltip(TooltipBuilder tooltip) {\n            data.addTooltip(tooltip);\n        }\n    }\n\n    /**\n     * Internal background painter instances for tabs.\n     */\n    @Environment(EnvType.CLIENT)\n    final static class Painters {\n        static final BackgroundPainter SELECTED_TAB = BackgroundPainter.createLightDarkVariants(\n                BackgroundPainter.createNinePatch(new Identifier(\"indrev\", \"textures/gui/selected_light.png\")).setTopPadding(2),\n                BackgroundPainter.createNinePatch(new Identifier(\"libgui\", \"textures/widget/tab/selected_dark.png\")).setTopPadding(2)\n        );\n\n        static final BackgroundPainter UNSELECTED_TAB = BackgroundPainter.createLightDarkVariants(\n                BackgroundPainter.createNinePatch(new Identifier(\"indrev\", \"textures/gui/unselected_light.png\")),\n                BackgroundPainter.createNinePatch(new Identifier(\"libgui\", \"textures/widget/tab/unselected_dark.png\"))\n        );\n\n        static final BackgroundPainter SELECTED_TAB_FOCUS_BORDER = BackgroundPainter.createNinePatch(new Identifier(\"libgui\", \"textures/widget/tab/focus.png\")).setTopPadding(2);\n        static final BackgroundPainter UNSELECTED_TAB_FOCUS_BORDER = BackgroundPainter.createNinePatch(new Identifier(\"libgui\", \"textures/widget/tab/focus.png\"));\n    }\n}\n\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/aprilfools/AprilFoolsMixinConfigPlugin.java",
    "content": "package me.steven.indrev.mixin.aprilfools;\n\nimport me.steven.indrev.AprilFools;\nimport net.fabricmc.api.EnvType;\nimport net.fabricmc.loader.api.FabricLoader;\nimport org.objectweb.asm.tree.ClassNode;\nimport org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;\nimport org.spongepowered.asm.mixin.extensibility.IMixinInfo;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\npublic class AprilFoolsMixinConfigPlugin implements IMixinConfigPlugin {\n    private static final ArrayList<String> mixins = new ArrayList<>();\n    static {\n        mixins.add(\"aprilfools.MixinTranslatableText\");\n    }\n\n    @Override\n    public void onLoad(String mixinPackage) { }\n\n    @Override\n    public String getRefMapperConfig() {\n        return null;\n    }\n\n    @Override\n    public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {\n        return true;\n    }\n\n    @Override\n    public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) { }\n\n    @Override\n    public List<String> getMixins() {\n        return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT && AprilFools.isToday() ? mixins : null;\n    }\n\n    @Override\n    public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { }\n\n    @Override\n    public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/aprilfools/MixinTranslatableText.java",
    "content": "package me.steven.indrev.mixin.aprilfools;\n\nimport me.steven.indrev.IndustrialRevolution;\nimport net.minecraft.text.TranslatableText;\nimport org.spongepowered.asm.mixin.Final;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Shadow;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.ModifyVariable;\n\n@Mixin(TranslatableText.class)\npublic class MixinTranslatableText {\n    @Shadow @Final private String key;\n\n    @ModifyVariable(name = \"string\", at = @At(\"STORE\"), method = \"updateTranslations\")\n    private String owo(String original) {\n        if (this.key.contains(IndustrialRevolution.MOD_ID)) {\n            if (!original.contains(\".\")) {\n                String[] words = original.split(\" \");\n                for (int i = 0; i < words.length; i++) {\n                    if (words[i].endsWith(\"ger\") || words[i].endsWith(\"der\"))\n                        words[i] = words[i].substring(0, words[i].length() - 2) + \"ah\";\n                    else if (words[i].endsWith(\"er\") || words[i].endsWith(\"re\"))\n                        words[i] = words[i].substring(0, words[i].length() - 2) + 'y';\n                    else if (words[i].endsWith(\"xe\"))\n                        words[i] = words[i].substring(0, words[i].length() - 2) + \"xie\";\n                    else if (words[i].endsWith(\"et\"))\n                        words[i] = words[i].substring(0, words[i].length() - 2) + \"ie\";\n\n                    words[i] = words[i].replaceFirst(\"est\", \"ess\"); // Chest -> Chess\n                    words[i] = words[i].replaceFirst(\"O\", \"Ow\").replaceFirst(\"o\", \"ow\");\n                }\n\n                return String.join(\" \", words);\n            }\n        }\n\n        return original;\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/client/MixinBuiltChunk.java",
    "content": "package me.steven.indrev.mixin.client;\n\nimport me.steven.indrev.blockentities.GlobalStateController;\nimport net.minecraft.client.MinecraftClient;\nimport net.minecraft.client.render.chunk.ChunkBuilder;\nimport net.minecraft.util.math.BlockPos;\nimport net.minecraft.util.math.ChunkPos;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Shadow;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfo;\n\n@Mixin(ChunkBuilder.BuiltChunk.class)\npublic abstract class MixinBuiltChunk {\n    @Shadow public abstract BlockPos getOrigin();\n\n    @Inject(method = \"rebuild\", at = @At(\"INVOKE\"))\n    private void indrev_removeBuiltChunk(CallbackInfo ci) {\n        long chunkPos = ChunkPos.toLong(getOrigin().getX() >> 4, getOrigin().getZ() >> 4);\n        MinecraftClient.getInstance().execute(() -> GlobalStateController.INSTANCE.getChunksToUpdate().remove(chunkPos));\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/client/MixinClientPlayerInteractionManager.java",
    "content": "package me.steven.indrev.mixin.client;\n\nimport me.steven.indrev.registry.IRItemRegistry;\nimport net.minecraft.client.network.ClientPlayerInteractionManager;\nimport net.minecraft.item.ItemStack;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Redirect;\n\n@Mixin(ClientPlayerInteractionManager.class)\npublic class MixinClientPlayerInteractionManager {\n    @Redirect(method = \"isCurrentlyBreaking\", at = @At(value = \"INVOKE\", target = \"Lnet/minecraft/item/ItemStack;areNbtEqual(Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemStack;)Z\"))\n    private boolean indrev_fixGamerAxe(ItemStack left, ItemStack right) {\n        if (left.getItem() == IRItemRegistry.INSTANCE.getGAMER_AXE_ITEM() && left.getItem() == right.getItem())\n            return true;\n        return ItemStack.areNbtEqual(left, right);\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/client/MixinGameRenderer.java",
    "content": "package me.steven.indrev.mixin.client;\n\nimport me.steven.indrev.api.IRPlayerEntityExtension;\nimport me.steven.indrev.tools.modular.ArmorModule;\nimport net.minecraft.client.render.GameRenderer;\nimport net.minecraft.entity.LivingEntity;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;\n\n@Mixin(value = GameRenderer.class, priority = 999)\npublic class MixinGameRenderer {\n    @Inject(method = \"getNightVisionStrength\", at = @At(\"INVOKE\"), cancellable = true)\n    private static void indrev_nightVisionStrength(LivingEntity livingEntity, float f, CallbackInfoReturnable<Float> cir) {\n        if (livingEntity instanceof IRPlayerEntityExtension && ((IRPlayerEntityExtension) livingEntity).isApplied(ArmorModule.NIGHT_VISION)) {\n            cir.setReturnValue(1f);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/client/MixinItemRenderer.java",
    "content": "package me.steven.indrev.mixin.client;\n\nimport com.mojang.blaze3d.systems.RenderSystem;\nimport me.steven.indrev.items.armor.IRModularArmorItem;\nimport net.minecraft.client.font.TextRenderer;\nimport net.minecraft.client.render.BufferBuilder;\nimport net.minecraft.client.render.Tessellator;\nimport net.minecraft.client.render.item.ItemRenderer;\nimport net.minecraft.item.ItemStack;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Shadow;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfo;\n\n@Mixin(ItemRenderer.class)\npublic abstract class MixinItemRenderer {\n\n    @Shadow protected abstract void renderGuiQuad(BufferBuilder buffer, int x, int y, int width, int height, int red, int green, int blue, int alpha);\n\n    @Inject(\n            method = \"renderGuiItemOverlay(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V\",\n            at = @At(\n                    value = \"INVOKE\",\n                    target = \"Lnet/minecraft/item/ItemStack;isItemBarVisible()Z\",\n                    shift = At.Shift.BEFORE\n            )\n    )\n    private void indrev_renderModularArmorFluidTank(TextRenderer renderer, ItemStack stack, int x, int y, String countLabel, CallbackInfo ci) {\n        if (stack.getItem() instanceof IRModularArmorItem armor && armor.isFluidItemBarVisible(stack)) {\n            RenderSystem.disableDepthTest();\n            RenderSystem.disableTexture();\n            RenderSystem.disableBlend();\n            Tessellator tessellator = Tessellator.getInstance();\n            BufferBuilder bufferBuilder = tessellator.getBuffer();\n            int i = armor.getFluidItemBarStep(stack);\n            int j = armor.getFluidItemBarColor(stack);\n            this.renderGuiQuad(bufferBuilder, x + 2, y + 11, 13, 2, 0, 0, 0, 255);\n            this.renderGuiQuad(bufferBuilder, x + 2, y + 11, i, 1, j >> 16 & 255, j >> 8 & 255, j & 255, 255);\n            RenderSystem.enableBlend();\n            RenderSystem.enableTexture();\n            RenderSystem.enableDepthTest();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/client/MixinLivingEntityClient.java",
    "content": "package me.steven.indrev.mixin.client;\n\nimport me.steven.indrev.api.IRPlayerEntityExtension;\nimport me.steven.indrev.tools.modular.ArmorModule;\nimport net.minecraft.entity.LivingEntity;\nimport net.minecraft.entity.effect.StatusEffect;\nimport net.minecraft.entity.effect.StatusEffects;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;\n\n@Mixin(LivingEntity.class)\npublic abstract class MixinLivingEntityClient {\n    @Inject(method = \"hasStatusEffect\", at = @At(\"INVOKE\"), cancellable = true)\n    private void indrev_nightVision(StatusEffect effect, CallbackInfoReturnable<Boolean> cir) {\n        if (\n                effect.equals(StatusEffects.NIGHT_VISION)\n                && this instanceof IRPlayerEntityExtension\n                && ((IRPlayerEntityExtension) this).isApplied(ArmorModule.NIGHT_VISION)\n        ) {\n            cir.setReturnValue(true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/client/MixinMinecraftClient.java",
    "content": "package me.steven.indrev.mixin.client;\n\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler;\nimport me.steven.indrev.packets.client.GuiPropertySyncPacket;\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;\nimport net.fabricmc.fabric.api.networking.v1.PacketByteBufs;\nimport net.minecraft.client.MinecraftClient;\nimport net.minecraft.client.gui.screen.Screen;\nimport net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider;\nimport net.minecraft.network.PacketByteBuf;\nimport net.minecraft.screen.ScreenHandler;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfo;\n\n@Mixin(MinecraftClient.class)\npublic class MixinMinecraftClient {\n    @Inject(method = \"setScreen\", at = @At(\"RETURN\"))\n    private void indrev_requestProperties(Screen screen, CallbackInfo ci) {\n        if (screen instanceof ScreenHandlerProvider<?> provider) {\n            ScreenHandler handler = provider.getScreenHandler();\n            if (handler instanceof IRGuiScreenHandler) {\n                PacketByteBuf buf = PacketByteBufs.create();\n                buf.writeInt(handler.syncId);\n                ClientPlayNetworking.send(GuiPropertySyncPacket.INSTANCE.getC2S_REQUEST_PROPERTIES(), buf);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/client/MixinWItemSlot.java",
    "content": "package me.steven.indrev.mixin.client;\n\nimport io.github.cottonmc.cotton.gui.client.BackgroundPainter;\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot;\nimport org.jetbrains.annotations.Nullable;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Shadow;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfo;\n\n@Mixin(value = WItemSlot.class, remap = false)\npublic class MixinWItemSlot {\n    @Shadow @Nullable\n    private BackgroundPainter backgroundPainter;\n\n    @Inject(method = \"addPainters\", at = @At(\"HEAD\"), cancellable = true, remap = false)\n    private void indrev_dontOverridePainters(CallbackInfo ci) {\n        if (backgroundPainter != null) ci.cancel();\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/common/MixinAbstractCookingRecipe.java",
    "content": "package me.steven.indrev.mixin.common;\n\nimport alexiil.mc.lib.attributes.fluid.volume.FluidVolume;\nimport me.steven.indrev.components.CraftingComponent;\nimport me.steven.indrev.recipes.machines.IRRecipe;\nimport me.steven.indrev.recipes.machines.entries.InputEntry;\nimport me.steven.indrev.recipes.machines.entries.OutputEntry;\nimport me.steven.indrev.utils.IRFluidTank;\nimport net.minecraft.item.ItemStack;\nimport net.minecraft.recipe.AbstractCookingRecipe;\nimport net.minecraft.recipe.Ingredient;\nimport net.minecraft.recipe.Recipe;\nimport net.minecraft.recipe.RecipeType;\nimport net.minecraft.util.Identifier;\nimport net.minecraft.util.collection.DefaultedList;\nimport org.jetbrains.annotations.NotNull;\nimport org.spongepowered.asm.mixin.Final;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Shadow;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfo;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Random;\n\n@Mixin(AbstractCookingRecipe.class)\npublic abstract class MixinAbstractCookingRecipe implements IRRecipe {\n\n    @Shadow @Final protected Identifier id;\n\n    @Shadow @Final protected int cookTime;\n\n    @Shadow @Final protected ItemStack output;\n\n    @Shadow @Final protected Ingredient input;\n\n    private InputEntry[] indrev_inputEntries;\n\n    private OutputEntry[] indrev_outputEntries;\n\n    @Inject(method = \"<init>\", at = @At(\"TAIL\"))\n    private void a(RecipeType<?> type, Identifier id, String group, Ingredient input, ItemStack output, float experience, int cookTime, CallbackInfo ci) {\n        this.indrev_inputEntries = new InputEntry[] { new InputEntry(input, 1) };\n        this.indrev_outputEntries = new OutputEntry[] { new OutputEntry(output, 1d) };\n    }\n\n    @NotNull\n    @Override\n    public Identifier getIdentifier() {\n        return id;\n    }\n\n    @NotNull\n    @Override\n    public InputEntry @NotNull [] getInput() {\n        return indrev_inputEntries;\n    }\n\n    @NotNull\n    @Override\n    public OutputEntry @NotNull [] getOutputs() {\n        return indrev_outputEntries;\n    }\n\n    @Override\n    public int getTicks() {\n        return cookTime;\n    }\n\n    @Override\n    public boolean matches(@NotNull List<ItemStack> inv, @NotNull List<IRFluidTank> fluidVolume) {\n        return this.input.test(inv.get(0));\n    }\n\n    @NotNull\n    @Override\n    public List<ItemStack> craft(Random random) {\n        return Collections.singletonList(output.copy());\n    }\n\n    @NotNull\n    @Override\n    public Identifier getId() {\n        return id;\n    }\n\n    @Override\n    public boolean isIgnoredInRecipeBook() {\n        return false;\n    }\n\n    @NotNull\n    @Override\n    public DefaultedList<Ingredient> getIngredients() {\n        DefaultedList<Ingredient> defaultedList = DefaultedList.of();\n        defaultedList.add(this.input);\n        return defaultedList;\n    }\n\n    @Override\n    public boolean isEmpty() {\n        DefaultedList<Ingredient> defaultedList = this.getIngredients();\n        return defaultedList.isEmpty() || defaultedList.stream().anyMatch((ingredient) -> ingredient.getMatchingStacks().length == 0);\n    }\n\n    @Override\n    public boolean canStart(@NotNull CraftingComponent<?> component) {\n        return component.fits(output);\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/common/MixinEnchantmentHelper.java",
    "content": "package me.steven.indrev.mixin.common;\n\nimport me.steven.indrev.api.CustomEnchantmentProvider;\nimport me.steven.indrev.api.IRPlayerEntityExtension;\nimport me.steven.indrev.tools.modular.ArmorModule;\nimport net.minecraft.enchantment.Enchantment;\nimport net.minecraft.enchantment.EnchantmentHelper;\nimport net.minecraft.entity.EquipmentSlot;\nimport net.minecraft.entity.LivingEntity;\nimport net.minecraft.entity.player.PlayerEntity;\nimport net.minecraft.item.ItemStack;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;\n\n@Mixin(EnchantmentHelper.class)\npublic class MixinEnchantmentHelper {\n    @Inject(method = \"getLevel\", at = @At(\"HEAD\"), cancellable = true)\n    private static void indrev_customEnchantProvider(Enchantment enchantment, ItemStack stack, CallbackInfoReturnable<Integer> cir) {\n        if (stack.getItem() instanceof CustomEnchantmentProvider) {\n            int level = ((CustomEnchantmentProvider) stack.getItem()).getLevel(enchantment, stack);\n            if (level > -1)\n                cir.setReturnValue(level);\n        }\n    }\n\n    @Inject(method = \"hasAquaAffinity\", at = @At(\"HEAD\"), cancellable = true)\n    private static void indrev_waterAffinityChest(LivingEntity entity, CallbackInfoReturnable<Boolean> cir) {\n        //specifically checks for water affinity on chestplate\n        if (entity instanceof PlayerEntity player\n                && ArmorModule.WATER_AFFINITY.getLevel(player.getInventory().getArmorStack(EquipmentSlot.CHEST.getEntitySlotId())) > 0\n                && entity instanceof IRPlayerEntityExtension ext\n                && ext.isApplied(ArmorModule.WATER_AFFINITY)\n        )\n            cir.setReturnValue(true);\n    }\n\n    @Inject(method = \"getDepthStrider\", at = @At(\"HEAD\"), cancellable = true)\n    private static void indrev_waterAffinityLegs(LivingEntity entity, CallbackInfoReturnable<Integer> cir) {\n        //specifically checks for water affinity on leggings\n        if (entity instanceof PlayerEntity player\n                && ArmorModule.WATER_AFFINITY.getLevel(player.getInventory().getArmorStack(EquipmentSlot.LEGS.getEntitySlotId())) > 0\n                && entity instanceof IRPlayerEntityExtension ext\n                && ext.isApplied(ArmorModule.WATER_AFFINITY)\n        )\n            cir.setReturnValue(3);\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/common/MixinEntity.java",
    "content": "package me.steven.indrev.mixin.common;\n\nimport me.steven.indrev.api.IREntityExtension;\nimport me.steven.indrev.api.IRPlayerEntityExtension;\nimport me.steven.indrev.inventories.IRInventory;\nimport me.steven.indrev.tools.modular.ArmorModule;\nimport net.minecraft.entity.Entity;\nimport net.minecraft.entity.ItemEntity;\nimport net.minecraft.item.ItemStack;\nimport net.minecraft.world.World;\nimport org.jetbrains.annotations.Nullable;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Shadow;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfo;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;\n\n@Mixin(Entity.class)\npublic abstract class MixinEntity implements IREntityExtension {\n    @Shadow public World world;\n\n    @Shadow public abstract double getX();\n\n    @Shadow public abstract double getY();\n\n    @Shadow public abstract double getZ();\n\n    private IRInventory machineInv = null;\n\n    @Inject(method = \"setAir\", at = @At(\"INVOKE\"), cancellable = true)\n    private void indrev_breathingModule(CallbackInfo ci) {\n        if (this instanceof IRPlayerEntityExtension && ((IRPlayerEntityExtension) this).isApplied(ArmorModule.BREATHING)) {\n            ci.cancel();\n        }\n    }\n\n    @Inject(method = \"getJumpVelocityMultiplier\", at = @At(value = \"RETURN\"), cancellable = true)\n    private void indrev_jumpBoostModule(CallbackInfoReturnable<Float> cir) {\n        if (this instanceof IRPlayerEntityExtension && ((IRPlayerEntityExtension) this).isApplied(ArmorModule.JUMP_BOOST)) {\n            cir.setReturnValue((float) ((IRPlayerEntityExtension) this).getAppliedLevel(ArmorModule.JUMP_BOOST));\n        }\n    }\n\n    @Inject(method = \"dropStack(Lnet/minecraft/item/ItemStack;F)Lnet/minecraft/entity/ItemEntity;\", at = @At(\"HEAD\"), cancellable = true)\n    private void indrev_onDropItem(ItemStack stack, float yOffset, CallbackInfoReturnable<ItemEntity> cir) {\n        if (!world.isClient && machineInv != null) {\n            if (!machineInv.output(stack)) {\n                ItemEntity itemEntity = new ItemEntity(world, getX(), getY() + yOffset, getZ(), stack);\n                itemEntity.setToDefaultPickupDelay();\n                this.world.spawnEntity(itemEntity);\n            }\n            cir.cancel();\n        }\n    }\n\n    @Nullable\n    @Override\n    public IRInventory getMachineInv() {\n        return machineInv;\n    }\n\n    @Override\n    public void setMachineInv(IRInventory machineInv) {\n        this.machineInv = machineInv;\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/common/MixinItemPredicate.java",
    "content": "package me.steven.indrev.mixin.common;\n\nimport me.steven.indrev.api.CustomEnchantmentProvider;\nimport net.minecraft.item.ItemStack;\nimport net.minecraft.predicate.item.EnchantmentPredicate;\nimport net.minecraft.predicate.item.ItemPredicate;\nimport org.spongepowered.asm.mixin.Final;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Shadow;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.Slice;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;\n\n@Mixin(ItemPredicate.class)\npublic class MixinItemPredicate {\n    @Shadow @Final private EnchantmentPredicate[] enchantments;\n\n    @Inject(\n            method = \"test\",\n            slice = @Slice(\n                    from = @At(value = \"INVOKE\", target = \"Lnet/minecraft/predicate/item/EnchantmentPredicate;test(Ljava/util/Map;)Z\")\n            ),\n            at = @At(value = \"RETURN\"),\n            cancellable = true)\n    private void indrev_customEnchantProvider(ItemStack stack, CallbackInfoReturnable<Boolean> cir) {\n        if (stack.getItem() instanceof CustomEnchantmentProvider && enchantments.length > 0) {\n            for (EnchantmentPredicate predicate : enchantments) {\n                if (predicate.levels != null && predicate.enchantment != null) {\n                    int level = ((CustomEnchantmentProvider) stack.getItem()).getLevel(predicate.enchantment, stack);\n                    if (level > -1 && predicate.levels.test(level)) cir.setReturnValue(true);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/common/MixinItemStack.java",
    "content": "package me.steven.indrev.mixin.common;\n\nimport com.google.common.collect.Multimap;\nimport me.steven.indrev.api.AttributeModifierProvider;\nimport me.steven.indrev.items.energy.IREnergyItem;\nimport net.minecraft.entity.EquipmentSlot;\nimport net.minecraft.entity.attribute.EntityAttribute;\nimport net.minecraft.entity.attribute.EntityAttributeModifier;\nimport net.minecraft.item.Item;\nimport net.minecraft.item.ItemStack;\nimport net.minecraft.server.network.ServerPlayerEntity;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Shadow;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;\n\nimport java.util.Random;\n\n@Mixin(ItemStack.class)\npublic abstract class MixinItemStack {\n\n    @Shadow public abstract Item getItem();\n\n    @Inject(method = \"getAttributeModifiers\", at = @At(\"TAIL\"), cancellable = true)\n    private void indrev_modifiableAttributeModifiers(EquipmentSlot equipmentSlot, CallbackInfoReturnable<Multimap<EntityAttribute, EntityAttributeModifier>> cir) {\n        ItemStack stack = (ItemStack) (Object) this;\n        if (stack.getItem() instanceof AttributeModifierProvider) {\n            cir.setReturnValue(((AttributeModifierProvider) stack.getItem()).getAttributeModifiers(stack, equipmentSlot));\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/common/MixinLivingEntity.java",
    "content": "package me.steven.indrev.mixin.common;\n\nimport net.minecraft.entity.Entity;\nimport net.minecraft.entity.EntityType;\nimport net.minecraft.entity.LivingEntity;\nimport net.minecraft.world.World;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfo;\n\n@Mixin(LivingEntity.class)\npublic abstract class MixinLivingEntity extends Entity {\n\n    public MixinLivingEntity(EntityType<?> type, World world) {\n        super(type, world);\n    }\n\n    /*\n    @Inject(method = \"knockDownwards\", at = @At(\"HEAD\"), cancellable = true)\n    private void indrev_waterAffinityDownwards(CallbackInfo ci) {\n        this.setVelocity(this.getVelocity().add(0.0D, -0.03999999910593033D * 2, 0.0D));\n        ci.cancel();\n    }\n\n    @Inject(method = \"swimUpward\", at = @At(\"HEAD\"), cancellable = true)\n    private void indrev_waterAffinityUpwards(CallbackInfo ci) {\n        this.setVelocity(this.getVelocity().add(0.0D, 0.03999999910593033D * 2, 0.0D));\n        ci.cancel();\n    }*/\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/common/MixinPiglinBrain.java",
    "content": "package me.steven.indrev.mixin.common;\n\nimport me.steven.indrev.api.IRPlayerEntityExtension;\nimport me.steven.indrev.tools.modular.ArmorModule;\nimport net.minecraft.entity.LivingEntity;\nimport net.minecraft.entity.mob.PiglinBrain;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;\n\n@Mixin(PiglinBrain.class)\npublic class MixinPiglinBrain {\n    @Inject(method = \"wearsGoldArmor\", at = @At(\"RETURN\"), cancellable = true)\n    private static void indrev_hasPiglinTrickerModule(LivingEntity entity, CallbackInfoReturnable<Boolean> cir) {\n        if (entity instanceof IRPlayerEntityExtension && ((IRPlayerEntityExtension) entity).isApplied(ArmorModule.PIGLIN_TRICKER)) {\n            cir.setReturnValue(true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/common/MixinPlayerEntity.java",
    "content": "package me.steven.indrev.mixin.common;\n\nimport it.unimi.dsi.fastutil.objects.Object2IntMap;\nimport it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;\nimport me.steven.indrev.api.IRPlayerEntityExtension;\nimport me.steven.indrev.items.energy.IREnergyItem;\nimport me.steven.indrev.items.energy.IRGamerAxeItem;\nimport me.steven.indrev.tools.modular.ArmorModule;\nimport me.steven.indrev.utils.EnergyutilsKt;\nimport net.minecraft.block.BlockState;\nimport net.minecraft.entity.EntityType;\nimport net.minecraft.entity.LivingEntity;\nimport net.minecraft.entity.player.PlayerEntity;\nimport net.minecraft.entity.player.PlayerInventory;\nimport net.minecraft.item.Item;\nimport net.minecraft.item.ItemStack;\nimport net.minecraft.nbt.NbtCompound;\nimport net.minecraft.world.World;\nimport org.jetbrains.annotations.NotNull;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfo;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;\nimport team.reborn.energy.api.EnergyStorage;\n\nimport java.util.Iterator;\nimport java.util.Map;\n\n@Mixin(PlayerEntity.class)\npublic abstract class MixinPlayerEntity extends LivingEntity implements IRPlayerEntityExtension {\n\n    private boolean indrev_regenerating = false;\n    private double indrev_shield = 0.0;\n    private final Object2IntMap<ArmorModule> appliedEffects = new Object2IntOpenHashMap<>();\n\n    protected MixinPlayerEntity(EntityType<? extends LivingEntity> entityType, World world) {\n        super(entityType, world);\n    }\n\n    @Inject(method = \"getBlockBreakingSpeed\", at = @At(\"HEAD\"), cancellable = true)\n    private void indrev_checkEnergyTool(BlockState block, CallbackInfoReturnable<Float> cir) {\n        PlayerEntity player = (PlayerEntity) (Object) this;\n        PlayerInventory inventory = player.getInventory();\n        ItemStack itemStack = inventory.main.get(inventory.selectedSlot);\n        Item item = itemStack.getItem();\n        EnergyStorage itemIo = EnergyutilsKt.energyOf(itemStack);\n        if (itemIo != null && item instanceof IREnergyItem) {\n            long amount = itemIo.getAmount();\n            if (item instanceof IRGamerAxeItem) {\n                NbtCompound tag = itemStack.getOrCreateNbt();\n                if (tag.contains(\"Active\") && !tag.getBoolean(\"Active\")) {\n                    cir.setReturnValue(0.2F);\n                    return;\n                }\n            }\n            if (amount < 1) cir.setReturnValue(0.2F);\n        }\n    }\n\n    @Inject(method = \"writeCustomDataToNbt\", at = @At(\"RETURN\"))\n    private void indrev_writeShieldToPlayerTag(NbtCompound tag, CallbackInfo ci) {\n        tag.putDouble(\"indrev:shield\", indrev_shield);\n    }\n\n    @Inject(method = \"readCustomDataFromNbt\", at = @At(\"RETURN\"))\n    private void indrev_readShieldToPlayerTag(NbtCompound tag, CallbackInfo ci) {\n        indrev_shield = tag.getDouble(\"indrev:shield\");\n    }\n\n    @Override\n    public double getShieldDurability() {\n        return indrev_shield;\n    }\n\n    @Override\n    public void setShieldDurability(double shieldDurability) {\n        this.indrev_shield = shieldDurability;\n    }\n\n    @Override\n    public double getMaxShieldDurability() {\n        Iterator<ItemStack> iterator = getArmorItems().iterator();\n        double shield = 0.0;\n        while (iterator.hasNext()) {\n            ItemStack next = iterator.next();\n            shield += ArmorModule.PROTECTION.getLevel(next) * 25;\n        }\n        return shield;\n    }\n\n\n    @Override\n    public @NotNull Map<ArmorModule, Integer> getAppliedModules() {\n        return appliedEffects;\n    }\n\n    @Override\n    public boolean isApplied(@NotNull ArmorModule module) {\n        return appliedEffects.containsKey(module);\n    }\n\n    @Override\n    public void applyModule(@NotNull ArmorModule module, int level) {\n        appliedEffects.put(module, level);\n    }\n\n    @Override\n    public int getAppliedLevel(@NotNull ArmorModule module) {\n        return appliedEffects.getOrDefault(module, 0);\n    }\n\n    @Override\n    public void setRegenerating(boolean isRegenerating) {\n        this.indrev_regenerating = isRegenerating;\n    }\n\n    @Override\n    public boolean isRegenerating() {\n        return indrev_regenerating;\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/common/MixinPlayerInventory.java",
    "content": "package me.steven.indrev.mixin.common;\n\nimport me.steven.indrev.items.armor.JetpackHandler;\nimport net.minecraft.entity.EquipmentSlot;\nimport net.minecraft.entity.player.PlayerEntity;\nimport net.minecraft.entity.player.PlayerInventory;\nimport net.minecraft.item.ItemStack;\nimport org.spongepowered.asm.mixin.Final;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Shadow;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfo;\n\n@Mixin(PlayerInventory.class)\npublic abstract class MixinPlayerInventory {\n    @Shadow public abstract ItemStack getArmorStack(int slot);\n\n    @Shadow @Final public PlayerEntity player;\n\n    @Inject(method = \"updateItems\", at = @At(\"TAIL\"))\n    private void indrev_tickJetpack(CallbackInfo ci) {\n        ItemStack armorStack = getArmorStack(EquipmentSlot.CHEST.getEntitySlotId());\n        if (armorStack.getItem() instanceof JetpackHandler handler && handler.isUsable(armorStack)) {\n            handler.tickJetpack(armorStack, player);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/common/MixinServerPlayerEntity.java",
    "content": "package me.steven.indrev.mixin.common;\n\nimport com.mojang.authlib.GameProfile;\nimport io.netty.buffer.Unpooled;\nimport it.unimi.dsi.fastutil.objects.Object2IntMap;\nimport it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;\nimport me.steven.indrev.api.IRServerPlayerEntityExtension;\nimport me.steven.indrev.items.armor.IRModularArmorItem;\nimport me.steven.indrev.items.energy.IRPortableChargerItem;\nimport me.steven.indrev.packets.client.SyncAppliedModulesPacket;\nimport me.steven.indrev.tools.modular.ArmorModule;\nimport me.steven.indrev.utils.AccessorextensionsKt;\nimport me.steven.indrev.utils.EnergyutilsKt;\nimport me.steven.indrev.utils.HelperextensionsKt;\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;\nimport net.minecraft.entity.ExperienceOrbEntity;\nimport net.minecraft.entity.ItemEntity;\nimport net.minecraft.entity.damage.DamageSource;\nimport net.minecraft.entity.player.HungerManager;\nimport net.minecraft.entity.player.PlayerEntity;\nimport net.minecraft.entity.player.PlayerInventory;\nimport net.minecraft.item.FoodComponent;\nimport net.minecraft.item.ItemStack;\nimport net.minecraft.network.PacketByteBuf;\nimport net.minecraft.server.network.ServerPlayerEntity;\nimport net.minecraft.server.world.ServerWorld;\nimport net.minecraft.sound.SoundCategory;\nimport net.minecraft.sound.SoundEvent;\nimport net.minecraft.sound.SoundEvents;\nimport net.minecraft.util.math.BlockPos;\nimport net.minecraft.util.math.Box;\nimport net.minecraft.util.math.Vec3d;\nimport net.minecraft.util.math.Vec3i;\nimport net.minecraft.world.World;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Shadow;\nimport org.spongepowered.asm.mixin.injection.At;\nimport org.spongepowered.asm.mixin.injection.Inject;\nimport org.spongepowered.asm.mixin.injection.ModifyVariable;\nimport org.spongepowered.asm.mixin.injection.callback.CallbackInfo;\nimport team.reborn.energy.api.EnergyStorage;\n\nimport java.util.List;\nimport java.util.Map;\n\n@Mixin(ServerPlayerEntity.class)\npublic abstract class MixinServerPlayerEntity extends PlayerEntity implements IRServerPlayerEntityExtension {\n\n    @Shadow public abstract boolean isInvulnerableTo(DamageSource damageSource);\n\n    @Shadow public abstract void playSound(SoundEvent event, SoundCategory category, float volume, float pitch);\n\n    private int ticks = 0;\n    private int lastDamageTick = 0;\n    private float lastDmg = 0f;\n    private double lastShield = 0.0;\n    private final Object2IntMap<ArmorModule> oldAppliedModules = new Object2IntOpenHashMap<>();\n\n    public MixinServerPlayerEntity(World world, BlockPos pos, float yaw, GameProfile profile) {\n        super(world, pos, yaw, profile);\n    }\n\n    @Inject(method = \"tick\", at = @At(\"TAIL\"))\n    private void indrev_applyEffects(CallbackInfo ci) {\n        ticks++;\n\n        if (ticks % 15 == 0) {\n            applyArmorEffects();\n        }\n        indrev_tickMagnet();\n        setShieldDurability(Math.min(getShieldDurability(), getMaxShieldDurability()));\n    }\n\n    @ModifyVariable(method = \"damage(Lnet/minecraft/entity/damage/DamageSource;F)Z\", at = @At(\"HEAD\"), argsOnly = true)\n    private float indrev_absorbDamage(float amount, DamageSource source) {\n        final float initial = amount;\n        if (isInvulnerableTo(source)) return amount;\n        if (lastDamageTick + 10 > ticks) {\n            if (amount <= lastDmg)\n                return 0f;\n            amount = amount - lastDmg;\n        }\n        lastDamageTick = ticks;\n        lastDmg = initial;\n        if (shouldApplyToShield(source)) {\n            float leftover = (float) applyDamageToShield(amount);\n            if (amount > leftover)\n                world.playSoundFromEntity(null, this, SoundEvents.BLOCK_CHAIN_BREAK, SoundCategory.PLAYERS, 1f, 0.0001f);\n            return leftover;\n        } else\n            return amount;\n    }\n\n    @Inject(method = \"worldChanged\", at = @At(\"TAIL\"))\n    private void indrev_syncOnDimChange(ServerWorld origin, CallbackInfo ci) {\n        sync();\n        AccessorextensionsKt.getFluidNetworkState(origin).onDimChange(this);\n        AccessorextensionsKt.getItemNetworkState(origin).onDimChange(this);\n    }\n\n    private boolean shouldApplyToShield(DamageSource source) {\n        if (source.equals(DamageSource.FALL)) return isApplied(ArmorModule.FEATHER_FALLING);\n        else if (source.isFire()) return isApplied(ArmorModule.FIRE_RESISTANCE);\n        else return !source.equals(DamageSource.STARVE) && !source.equals(DamageSource.DROWN);\n    }\n\n    private void applyArmorEffects() {\n        ServerPlayerEntity player = (ServerPlayerEntity) (Object) this;\n        PlayerInventory inventory = player.getInventory();\n        getAppliedModules().clear();\n\n        for (int i = 0; i < inventory.armor.size(); i++) {\n            int cSlot = 36+i;\n            ItemStack itemStack = inventory.getStack(cSlot);\n            if (itemStack.getItem() instanceof IRModularArmorItem) {\n                List<ArmorModule> modules = ((IRModularArmorItem) itemStack.getItem()).getInstalled(itemStack);\n                for (ArmorModule module : modules) {\n                    int level = module.getLevel(itemStack);\n                    if (level <= 0) continue;\n                    switch (module) {\n                        case SPEED:\n                        case BREATHING:\n                        case JUMP_BOOST:\n                        case NIGHT_VISION:\n                        case FIRE_RESISTANCE:\n                        case PIGLIN_TRICKER:\n                        case FEATHER_FALLING:\n                        case WATER_AFFINITY:\n                            if (EnergyutilsKt.extract(inventory, cSlot, 20))\n                                applyModule(module, level);\n                            break;\n                        case AUTO_FEEDER:\n                            HungerManager hunger = player.getHungerManager();\n                            if (hunger.isNotFull()) {\n                                for (int slot = 0; slot <= inventory.size(); slot++) {\n                                    ItemStack stack = inventory.getStack(slot);\n                                    FoodComponent food = stack.getItem().getFoodComponent();\n                                    if (food != null && !food.isAlwaysEdible() && !HelperextensionsKt.hasNegativeEffects(food) && food.getHunger() <= 20 - hunger.getFoodLevel() && EnergyutilsKt.extract(inventory, cSlot, 30)) {\n                                        stack.finishUsing(world, player);\n                                        player.eatFood(world, stack);\n                                    }\n                                    if (!hungerManager.isNotFull()) break;\n                                }\n                            }\n                            break;\n                        case CHARGER:\n                                IRPortableChargerItem.Companion.chargeItemsInInv(cSlot, player.getInventory());\n                            break;\n                        case SOLAR_PANEL:\n                            if (world.isDay() && world.isSkyVisible(player.getBlockPos().up(2))) {\n                                for (int x = 0; x < 4; x++) {\n                                    EnergyutilsKt.insert(inventory, cSlot - x, 75L * level);\n                                }\n                            }\n                            break;\n                        case PROTECTION:\n                            if (ticks - 120 > lastDamageTick && getShieldDurability() < getMaxShieldDurability() && EnergyutilsKt.extract(inventory, cSlot, 30)) {\n                                regenerateShield();\n                            }\n                            break;\n                        default:\n                            break;\n                    }\n                }\n            }\n        }\n    }\n\n    private void indrev_tickMagnet() {\n        ServerPlayerEntity player = (ServerPlayerEntity) (Object) this;\n        PlayerInventory inventory = player.getInventory();\n        for (ItemStack itemStack : inventory.armor) {\n            if (itemStack.getItem() instanceof IRModularArmorItem) {\n                int level = ArmorModule.MAGNET.getLevel(itemStack);\n                if (level > 0) {\n                    Vec3i offset = new Vec3i(8, 8, 8);\n                    Box area = new Box(getBlockPos().subtract(offset), getBlockPos().add(offset));\n                    Vec3d blockCenter = HelperextensionsKt.toVec3d(getBlockPos()).add(0.5, 0.5, 0.5);\n                    world.getOtherEntities(this, area, (entity) -> entity instanceof ItemEntity || entity instanceof ExperienceOrbEntity).forEach(entity -> {\n                        if ((entity instanceof ItemEntity itemEntity && !itemEntity.cannotPickup())\n                                || (entity instanceof ExperienceOrbEntity xpEntity && xpEntity.age > 40)) {\n                            Vec3d v = entity.getPos().relativize(blockCenter).normalize().multiply(0.2);\n                            entity.addVelocity(v.x, v.y, v.z);\n                            //applyModule(ArmorModule.MAGNET, 1);\n                        }\n                    });\n                    return;\n                }\n            }\n        }\n        //getAppliedModules().remove(ArmorModule.MAGNET);\n    }\n\n    private void regenerateShield() {\n        setShieldDurability(Math.min(getShieldDurability() + 0.5, getMaxShieldDurability()));\n    }\n\n    private double applyDamageToShield(double damage) {\n        double absorbed = Math.min(damage, getShieldDurability());\n        setShieldDurability(getShieldDurability() - absorbed);\n        return damage - absorbed;\n    }\n\n    @Override\n    public boolean shouldSync() {\n        return !oldAppliedModules.equals(getAppliedModules()) || lastShield != getShieldDurability();\n    }\n\n    @Override\n    public void sync() {\n        lastShield = getShieldDurability();\n        oldAppliedModules.clear();\n        oldAppliedModules.putAll(getAppliedModules());\n        PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());\n        Map<ArmorModule, Integer> appliedModules = getAppliedModules();\n        buf.writeInt(appliedModules.size());\n        appliedModules.forEach((module, level) -> {\n            buf.writeInt(module.ordinal());\n            buf.writeInt(level);\n        });\n        buf.writeDouble(getShieldDurability());\n        buf.writeBoolean(ticks - 120 > lastDamageTick);\n        ServerPlayNetworking.send((ServerPlayerEntity) (Object) this, SyncAppliedModulesPacket.INSTANCE.getSYNC_MODULE_PACKET(), buf);\n    }\n\n    @Override\n    public boolean isRegenerating() {\n        return ticks - 120 > lastDamageTick;\n    }\n}"
  },
  {
    "path": "src/main/java/me/steven/indrev/mixin/common/MixinServerWorld.java",
    "content": "package me.steven.indrev.mixin.common;\n\nimport it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;\nimport me.steven.indrev.api.ServerWorldExtension;\nimport me.steven.indrev.networks.Network;\nimport me.steven.indrev.networks.NetworkState;\nimport me.steven.indrev.networks.ServoNetworkState;\nimport me.steven.indrev.networks.energy.EnergyNetwork;\nimport me.steven.indrev.networks.energy.EnergyNetworkState;\nimport me.steven.indrev.networks.fluid.FluidNetworkState;\nimport me.steven.indrev.networks.item.ItemNetworkState;\nimport net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache;\nimport net.minecraft.server.world.ServerWorld;\nimport net.minecraft.util.math.Direction;\nimport net.minecraft.world.PersistentStateManager;\nimport org.jetbrains.annotations.NotNull;\nimport org.spongepowered.asm.mixin.Mixin;\nimport org.spongepowered.asm.mixin.Shadow;\nimport team.reborn.energy.api.EnergyStorage;\n\n@Mixin(ServerWorld.class)\npublic abstract class MixinServerWorld implements ServerWorldExtension {\n\n    private final Long2ObjectOpenHashMap<BlockApiCache<EnergyStorage, Direction>> indrev_energyIoCache = new Long2ObjectOpenHashMap<>();\n\n    private ItemNetworkState indrev_itemNetworkState = null;\n    private FluidNetworkState indrev_fluidNetworkState = null;\n    private EnergyNetworkState indrev_energyNetworkState = null;\n\n    @Shadow\n    public abstract PersistentStateManager getPersistentStateManager();\n\n    @NotNull\n    @Override\n    public Long2ObjectOpenHashMap<BlockApiCache<EnergyStorage, Direction>> indrev_getEnergyCache() {\n        return indrev_energyIoCache;\n    }\n\n    @NotNull\n    @Override\n    public EnergyNetworkState indrev_getEnergyNetworkState() {\n        ServerWorld world = (ServerWorld) (Object) this;\n        if (indrev_energyNetworkState == null) {\n            indrev_energyNetworkState = getPersistentStateManager()\n                    .getOrCreate(\n                            nbt -> EnergyNetworkState.Companion.readNbt(nbt, () -> new EnergyNetworkState(world)),\n                            () -> new EnergyNetworkState(world),\n                            Network.Type.Companion.getENERGY().getKey());\n        }\n        return indrev_energyNetworkState;\n    }\n\n    @NotNull\n    @Override\n    public FluidNetworkState indrev_getFluidNetworkState() {\n        ServerWorld world = (ServerWorld) (Object) this;\n        if (indrev_fluidNetworkState == null) {\n            indrev_fluidNetworkState = getPersistentStateManager()\n                    .getOrCreate(\n                            nbt -> ServoNetworkState.Companion.readNbt(nbt, () -> new FluidNetworkState(world)),\n                            () -> new FluidNetworkState(world),\n                            Network.Type.Companion.getFLUID().getKey());\n        }\n        return indrev_fluidNetworkState;\n    }\n\n    @NotNull\n    @Override\n    public ItemNetworkState indrev_getItemNetworkState() {\n        ServerWorld world = (ServerWorld) (Object) this;\n        if (indrev_itemNetworkState == null) {\n            indrev_itemNetworkState = getPersistentStateManager()\n                    .getOrCreate(\n                            nbt -> ItemNetworkState.Companion.readNbt(nbt, () -> new ItemNetworkState(world)),\n                            () -> new ItemNetworkState(world),\n                            Network.Type.Companion.getITEM().getKey());\n        }\n        return indrev_itemNetworkState;\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/IndustrialRevolution.kt",
    "content": "package me.steven.indrev\n\nimport dev.cafeteria.fakeplayerapi.server.FakePlayerBuilder\nimport dev.cafeteria.fakeplayerapi.server.FakeServerPlayer\nimport me.steven.indrev.api.IRServerPlayerEntityExtension\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.datagen.DataGeneratorManager\nimport me.steven.indrev.events.common.IRLootTableCallback\nimport me.steven.indrev.gui.screenhandlers.COAL_GENERATOR_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.networks.NetworkEvents\nimport me.steven.indrev.packets.PacketRegistry\nimport me.steven.indrev.packets.client.SyncConfigPacket\nimport me.steven.indrev.recipes.SelfRemainderRecipe\nimport me.steven.indrev.recipes.machines.*\nimport me.steven.indrev.registry.*\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.api.ModInitializer\nimport net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents\nimport net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder\nimport net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents\nimport net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents\nimport net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents\nimport net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents\nimport net.fabricmc.fabric.api.particle.v1.FabricParticleTypes\nimport net.fabricmc.fabric.impl.datagen.FabricDataGenHelper\nimport net.fabricmc.loader.api.FabricLoader\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemGroup\nimport net.minecraft.item.ItemStack\nimport net.minecraft.sound.SoundCategory\nimport net.minecraft.sound.SoundEvent\nimport net.minecraft.tag.TagKey\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.registry.Registry\nimport org.apache.logging.log4j.LogManager\nimport org.apache.logging.log4j.Logger\n\nobject IndustrialRevolution : ModInitializer {\n    override fun onInitialize() {\n\n        //load the screenhandlers.kt class\n        COAL_GENERATOR_HANDLER\n\n        IRConfig\n        IRItemRegistry.registerAll()\n        IRBlockRegistry.registerAll()\n        IRFluidRegistry.registerAll()\n\n        Registry.register(Registry.SOUND_EVENT, LASER_SOUND_ID, LASER_SOUND_EVENT)\n        Registry.register(Registry.PARTICLE_TYPE, identifier(\"laser_particle\"), LASER_PARTICLE)\n\n        WorldGeneration.init()\n        WorldGeneration.addFeatures()\n\n        LootTableLoadingCallback.EVENT.register(IRLootTableCallback)\n\n        MachineRegistry\n\n        Registry.register(Registry.RECIPE_SERIALIZER, PulverizerRecipe.IDENTIFIER, PulverizerRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, PulverizerRecipe.IDENTIFIER, PulverizerRecipe.TYPE)\n        Registry.register(Registry.RECIPE_SERIALIZER, CompressorRecipe.IDENTIFIER, CompressorRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, CompressorRecipe.IDENTIFIER, CompressorRecipe.TYPE)\n        Registry.register(Registry.RECIPE_SERIALIZER, InfuserRecipe.IDENTIFIER, InfuserRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, InfuserRecipe.IDENTIFIER, InfuserRecipe.TYPE)\n        Registry.register(Registry.RECIPE_SERIALIZER, FluidInfuserRecipe.IDENTIFIER, FluidInfuserRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, FluidInfuserRecipe.IDENTIFIER, FluidInfuserRecipe.TYPE)\n        Registry.register(Registry.RECIPE_SERIALIZER, RecyclerRecipe.IDENTIFIER, RecyclerRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, RecyclerRecipe.IDENTIFIER, RecyclerRecipe.TYPE)\n        Registry.register(Registry.RECIPE_SERIALIZER, SmelterRecipe.IDENTIFIER, SmelterRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, SmelterRecipe.IDENTIFIER, SmelterRecipe.TYPE)\n        Registry.register(Registry.RECIPE_SERIALIZER, CondenserRecipe.IDENTIFIER, CondenserRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, CondenserRecipe.IDENTIFIER, CondenserRecipe.TYPE)\n        Registry.register(Registry.RECIPE_SERIALIZER, DistillerRecipe.IDENTIFIER, DistillerRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, DistillerRecipe.IDENTIFIER, DistillerRecipe.TYPE)\n        Registry.register(Registry.RECIPE_SERIALIZER, SawmillRecipe.IDENTIFIER, SawmillRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, SawmillRecipe.IDENTIFIER, SawmillRecipe.TYPE)\n        Registry.register(Registry.RECIPE_SERIALIZER, ModuleRecipe.IDENTIFIER, ModuleRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, ModuleRecipe.IDENTIFIER, ModuleRecipe.TYPE)\n        Registry.register(Registry.RECIPE_SERIALIZER, LaserRecipe.IDENTIFIER, LaserRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, LaserRecipe.IDENTIFIER, LaserRecipe.TYPE)\n        Registry.register(Registry.RECIPE_SERIALIZER, ElectrolysisRecipe.IDENTIFIER, ElectrolysisRecipe.SERIALIZER)\n        Registry.register(Registry.RECIPE_TYPE, ElectrolysisRecipe.IDENTIFIER, ElectrolysisRecipe.TYPE)\n\n        Registry.register(Registry.RECIPE_SERIALIZER, SelfRemainderRecipe.IDENTIFIER, SelfRemainderRecipe.SERIALIZER)\n\n        PacketRegistry.registerServer()\n\n        ServerTickEvents.END_WORLD_TICK.register(NetworkEvents)\n        ServerBlockEntityEvents.BLOCK_ENTITY_LOAD.register(NetworkEvents)\n        ServerLifecycleEvents.SERVER_STOPPING.register(NetworkEvents)\n\n        ServerPlayConnectionEvents.JOIN.register { handler, _, _ ->\n            val player = handler.player\n            SyncConfigPacket.sendConfig(player)\n            if (player is IRServerPlayerEntityExtension) {\n                (player as IRServerPlayerEntityExtension).sync()\n            }\n        }\n\n        ServerTickEvents.START_SERVER_TICK.register { server ->\n            server.playerManager.playerList.forEach { player ->\n                if (player is IRServerPlayerEntityExtension && player.shouldSync()) {\n                    player.sync()\n                }\n            }\n        }\n\n        ServerTickEvents.END_SERVER_TICK.register { server ->\n            server.playerManager.playerList.forEach { player ->\n                val currentScreenHandler = player.currentScreenHandler as? IRGuiScreenHandler ?: return@forEach\n                currentScreenHandler.syncProperties()\n            }\n        }\n\n        ServerLifecycleEvents.END_DATA_PACK_RELOAD.register { s, _, _ ->\n            s.recipeManager.recipes.keys.filterIsInstance<IRRecipeType<*>>().forEach { it.clearCache() }\n        }\n\n        if (FabricLoader.getInstance().getLaunchArguments(true).contains(\"-dataGen\")) {\n            FabricDataGenHelper.run()\n            ClientLifecycleEvents.CLIENT_STARTED.register(ClientLifecycleEvents.ClientStarted {\n                DataGeneratorManager(\"indrev\").generate()\n            })\n        }\n        LOGGER.info(\"Industrial Revolution has initialized.\")\n    }\n\n    val LOGGER: Logger = LogManager.getLogger(\"Industrial Revolution\")\n\n    const val MOD_ID = \"indrev\"\n\n    val MOD_GROUP: ItemGroup =\n        FabricItemGroupBuilder.build(identifier(\"indrev_group\")) { ItemStack { MachineRegistry.PULVERIZER_REGISTRY.block(Tier.MK4).asItem() } }\n\n    val COOLERS_TAG: TagKey<Item> = TagKey.of(Registry.ITEM_KEY, identifier(\"coolers\"))\n    val WRENCH_TAG: TagKey<Item> = TagKey.of(Registry.ITEM_KEY, Identifier(\"c:wrenches\"))\n    val SCREWDRIVER_TAG: TagKey<Item> = TagKey.of(Registry.ITEM_KEY, Identifier(\"c:screwdrivers\"))\n    val NIKOLITE_ORES: TagKey<Item> = TagKey.of(Registry.ITEM_KEY, Identifier(\"c:nikolite_ores\"))\n    val TIN_ORES: TagKey<Item> = TagKey.of(Registry.ITEM_KEY, Identifier(\"c:tin_ores\"))\n    val LEAD_ORES: TagKey<Item> = TagKey.of(Registry.ITEM_KEY, Identifier(\"c:lead_ores\"))\n    val SILVER_ORES: TagKey<Item> = TagKey.of(Registry.ITEM_KEY, Identifier(\"c:silver_ores\"))\n    val TUNGSTEN_ORES: TagKey<Item> = TagKey.of(Registry.ITEM_KEY, Identifier(\"c:tungsten_ores\"))\n    val ANCIENT_DEBRIS_ORES: TagKey<Item> = TagKey.of(Registry.ITEM_KEY, Identifier(\"c:ancient_debris_ores\"))\n\n    val LASER_SOUND_ID = identifier(\"laser\")\n    val LASER_SOUND_EVENT = SoundEvent(LASER_SOUND_ID)\n    val LASER_PARTICLE = FabricParticleTypes.simple()\n\n    val FAKE_PLAYER_BUILDER = FakePlayerBuilder(identifier(\"default_fake_player\")) { builder, server, world, profile ->\n        object : FakeServerPlayer(builder, server, world, profile) {\n            override fun isCreative(): Boolean = false\n            override fun isSpectator(): Boolean = false\n            override fun playSound(sound: SoundEvent?, volume: Float, pitch: Float) {}\n            override fun playSound(event: SoundEvent?, category: SoundCategory?, volume: Float, pitch: Float) {}\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/IndustrialRevolutionClient.kt",
    "content": "package me.steven.indrev\n\nimport it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap\nimport me.steven.indrev.api.OreDataCards\nimport me.steven.indrev.blockentities.GlobalStateController\nimport me.steven.indrev.blockentities.crafters.*\nimport me.steven.indrev.blockentities.miningrig.DrillBlockEntityRenderer\nimport me.steven.indrev.blockentities.farms.*\nimport me.steven.indrev.blockentities.generators.HeatGeneratorBlockEntityRenderer\nimport me.steven.indrev.blockentities.generators.SteamTurbineBlockEntity\nimport me.steven.indrev.blockentities.laser.CapsuleBlockEntityRenderer\nimport me.steven.indrev.blockentities.laser.LaserBlockEntityRenderer\nimport me.steven.indrev.blockentities.miningrig.MiningRigBlockEntityRenderer\nimport me.steven.indrev.blockentities.modularworkbench.ModularWorkbenchBlockEntityRenderer\nimport me.steven.indrev.blockentities.solarpowerplant.HeliostatBlockEntityRenderer\nimport me.steven.indrev.blockentities.storage.ChargePadBlockEntityRenderer\nimport me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntityRenderer\nimport me.steven.indrev.blockentities.storage.TankBlockEntityRenderer\nimport me.steven.indrev.components.multiblock.MultiblockBlockEntityRenderer\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.events.client.*\nimport me.steven.indrev.fluids.FluidType\nimport me.steven.indrev.gui.IRInventoryScreen\nimport me.steven.indrev.gui.screenhandlers.*\nimport me.steven.indrev.gui.screenhandlers.pipes.PipeFilterScreen\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.client.ClientNetworkState\nimport me.steven.indrev.packets.PacketRegistry\nimport me.steven.indrev.registry.*\nimport me.steven.indrev.events.client.IRWorldRenderer\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.api.ClientModInitializer\nimport net.fabricmc.fabric.api.`object`.builder.v1.client.model.FabricModelPredicateProviderRegistry\nimport net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap\nimport net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents\nimport net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback\nimport net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper\nimport net.fabricmc.fabric.api.client.model.ModelLoadingRegistry\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents\nimport net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry\nimport net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry\nimport net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback\nimport net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback\nimport net.fabricmc.fabric.api.client.rendering.v1.TooltipComponentCallback\nimport net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents\nimport net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry\nimport net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback\nimport net.minecraft.client.option.KeyBinding\nimport net.minecraft.client.particle.FlameParticle\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.util.InputUtil\nimport net.minecraft.item.ElytraItem\nimport net.minecraft.screen.PlayerScreenHandler\nimport org.lwjgl.glfw.GLFW\n\n@Suppress(\"UNCHECKED_CAST\")\nobject IndustrialRevolutionClient : ClientModInitializer {\n    override fun onInitializeClient() {\n        FluidType.WATER.registerReloadListener()\n        FluidType.LAVA.registerReloadListener()\n        FluidType.GAS.registerReloadListener()\n        arrayOf(\n            IRFluidRegistry.COOLANT_STILL,\n            IRFluidRegistry.SULFURIC_ACID_STILL,\n            IRFluidRegistry.TOXIC_MUD_STILL,\n            IRFluidRegistry.STEAM_STILL\n        ).forEach { it.registerRender(FluidType.WATER) }\n        arrayOf(\n            IRFluidRegistry.MOLTEN_NETHERITE_STILL,\n            IRFluidRegistry.MOLTEN_IRON_STILL,\n            IRFluidRegistry.MOLTEN_GOLD_STILL,\n            IRFluidRegistry.MOLTEN_COPPER_STILL,\n            IRFluidRegistry.MOLTEN_TIN_STILL,\n            IRFluidRegistry.MOLTEN_LEAD_STILL,\n            IRFluidRegistry.MOLTEN_SILVER_STILL\n        ).forEach { it.registerRender(FluidType.LAVA) }\n        arrayOf(\n            IRFluidRegistry.HYDROGEN_STILL,\n            IRFluidRegistry.OXYGEN_STILL,\n            IRFluidRegistry.METHANE_STILL,\n        ).forEach { it.registerRender(FluidType.GAS) }\n        arrayOf(\n            COAL_GENERATOR_HANDLER,\n            SOLAR_GENERATOR_HANDLER,\n            BIOMASS_GENERATOR_HANDLER,\n            HEAT_GENERATOR_HANDLER,\n            GAS_BURNING_GENERATOR_HANDLER,\n            BATTERY_HANDLER,\n            ELECTRIC_FURNACE_HANDLER,\n            PULVERIZER_HANDLER,\n            COMPRESSOR_HANDLER,\n            SOLID_INFUSER_HANDLER,\n            RECYCLER_HANDLER,\n            CHOPPER_HANDLER,\n            RANCHER_HANDLER,\n            MINING_RIG_HANDLER,\n            MODULAR_WORKBENCH_HANDLER,\n            FISHER_HANDLER,\n            SCREWDRIVER_HANDLER,\n            SMELTER_HANDLER,\n            CONDENSER_HANDLER,\n            FLUID_INFUSER_HANDLER,\n            FARMER_HANDLER,\n            SLAUGHTER_HANDLER,\n            SAWMILL_HANDLER,\n            ELECTRIC_FURNACE_FACTORY_HANDLER,\n            PULVERIZER_FACTORY_HANDLER,\n            COMPRESSOR_FACTORY_HANDLER,\n            SOLID_INFUSER_FACTORY_HANDLER,\n            CABINET_HANDLER,\n            DRILL_HANDLER,\n            LASER_HANDLER,\n            ELECTROLYTIC_SEPARATOR_HANDLER,\n            STEAM_TURBINE_HANDLER,\n            SOLAR_POWER_PLANT_TOWER_HANDLER,\n            DATA_CARD_WRITER_HANDLER,\n            PUMP_HANDLER\n        ).forEach { handler ->\n            ScreenRegistry.register(handler) { controller, inv, _ -> IRInventoryScreen(controller, inv.player) }\n        }\n        ScreenRegistry.register(PIPE_FILTER_HANDLER) { controller, inv, _ -> PipeFilterScreen(controller, inv.player) }\n\n        MachineRegistry.CHOPPER_REGISTRY.registerBlockEntityRenderer(::ChopperBlockEntityRenderer)\n        MachineRegistry.RANCHER_REGISTRY.registerBlockEntityRenderer(::AOEMachineBlockEntityRenderer)\n        MachineRegistry.FARMER_REGISTRY.registerBlockEntityRenderer(::AOEMachineBlockEntityRenderer)\n        MachineRegistry.SLAUGHTER_REGISTRY.registerBlockEntityRenderer(::AOEMachineBlockEntityRenderer)\n        MachineRegistry.MODULAR_WORKBENCH_REGISTRY.registerBlockEntityRenderer(::ModularWorkbenchBlockEntityRenderer)\n        MachineRegistry.CHARGE_PAD_REGISTRY.registerBlockEntityRenderer(::ChargePadBlockEntityRenderer)\n        MachineRegistry.CONDENSER_REGISTRY.registerBlockEntityRenderer(::CondenserBlockEntityRenderer)\n        MachineRegistry.FLUID_INFUSER_REGISTRY.registerBlockEntityRenderer(::FluidInfuserBlockEntityRenderer)\n        MachineRegistry.SOLID_INFUSER_FACTORY_REGISTRY.registerBlockEntityRenderer { MultiblockBlockEntityRenderer<SolidInfuserFactoryBlockEntity> { be -> be.multiblockComponent!! } }\n        MachineRegistry.COMPRESSOR_FACTORY_REGISTRY.registerBlockEntityRenderer { MultiblockBlockEntityRenderer<CompressorFactoryBlockEntity> { be -> be.multiblockComponent!! } }\n        MachineRegistry.PULVERIZER_FACTORY_REGISTRY.registerBlockEntityRenderer { MultiblockBlockEntityRenderer<PulverizerFactoryBlockEntity> { be -> be.multiblockComponent!! } }\n        MachineRegistry.ELECTRIC_FURNACE_FACTORY_REGISTRY.registerBlockEntityRenderer { MultiblockBlockEntityRenderer<ElectricFurnaceFactoryBlockEntity> { be -> be.multiblockComponent!! } }\n        MachineRegistry.MINING_RIG_REGISTRY.registerBlockEntityRenderer(::MiningRigBlockEntityRenderer)\n        MachineRegistry.PUMP_REGISTRY.registerBlockEntityRenderer(::PumpBlockEntityRenderer)\n        MachineRegistry.LAZULI_FLUX_CONTAINER_REGISTRY.registerBlockEntityRenderer(::LazuliFluxContainerBlockEntityRenderer)\n        MachineRegistry.HEAT_GENERATOR_REGISTRY.registerBlockEntityRenderer(::HeatGeneratorBlockEntityRenderer)\n        MachineRegistry.LASER_EMITTER_REGISTRY.registerBlockEntityRenderer(::LaserBlockEntityRenderer)\n        MachineRegistry.STEAM_TURBINE_REGISTRY.registerBlockEntityRenderer { MultiblockBlockEntityRenderer<SteamTurbineBlockEntity> { be -> be.multiblockComponent!! } }\n        BlockEntityRendererRegistry.register(IRBlockRegistry.TANK_BLOCK_ENTITY) { TankBlockEntityRenderer() }\n        BlockEntityRendererRegistry.register(IRBlockRegistry.DRILL_BLOCK_ENTITY_TYPE) { DrillBlockEntityRenderer() }\n        BlockEntityRendererRegistry.register(IRBlockRegistry.CAPSULE_BLOCK_ENTITY) { CapsuleBlockEntityRenderer() }\n        BlockEntityRendererRegistry.register(IRBlockRegistry.BIOMASS_COMPOSTER_BLOCK_ENTITY) { BiomassComposterBlockEntityRenderer() }\n        BlockEntityRendererRegistry.register(IRBlockRegistry.SOLAR_POWER_PLANT_TOWER_BLOCK_ENTITY) { MultiblockBlockEntityRenderer { be -> be.multiblockComponent } }\n        BlockEntityRendererRegistry.register(IRBlockRegistry.HELIOSTAT_BLOCK_ENTITY) { HeliostatBlockEntityRenderer() }\n\n        MachineRegistry.MODULAR_WORKBENCH_REGISTRY.setRenderLayer(RenderLayer.getTranslucent())\n        MachineRegistry.PUMP_REGISTRY.setRenderLayer(RenderLayer.getTranslucent())\n        MachineRegistry.HEAT_GENERATOR_REGISTRY.setRenderLayer(RenderLayer.getCutout())\n        BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.TANK_BLOCK, RenderLayer.getCutout())\n        BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.SULFUR_CRYSTAL_CLUSTER, RenderLayer.getTranslucent())\n        BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.DRILL_TOP, RenderLayer.getCutout())\n        BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.DRILL_MIDDLE, RenderLayer.getCutout())\n        BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.DRILL_BOTTOM, RenderLayer.getCutout())\n        BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.CAPSULE_BLOCK, RenderLayer.getCutout())\n\n        BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.CABLE_MK1, RenderLayer.getCutout())\n        BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.CABLE_MK2, RenderLayer.getCutout())\n        BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.CABLE_MK3, RenderLayer.getCutout())\n        BlockRenderLayerMap.INSTANCE.putBlock(IRBlockRegistry.CABLE_MK4, RenderLayer.getCutout())\n\n        ModelLoadingRegistry.INSTANCE.registerModelProvider(IRModelManagers)\n        ModelLoadingRegistry.INSTANCE.registerVariantProvider { IRModelManagers }\n\n        FabricModelPredicateProviderRegistry.register(\n            IRItemRegistry.GAMER_AXE_ITEM,\n            identifier(\"activate\")\n        ) { stack, _, _, _ -> stack?.orCreateNbt?.getFloat(\"Progress\") ?: 0f }\n\n        FabricModelPredicateProviderRegistry.register(IRItemRegistry.REINFORCED_ELYTRA, identifier(\"broken\")) { stack, _, _, _ ->\n            if (ElytraItem.isUsable(stack)) 0.0f else 1.0f\n        }\n\n        FabricModelPredicateProviderRegistry.register(IRItemRegistry.ORE_DATA_CARD, identifier(\"empty\")) { stack, _, _, _ ->\n            if (OreDataCards.readNbt(stack) == null) 0.0f else 1.0f\n        }\n\n        PacketRegistry.registerClient()\n\n        GlobalStateController.initClient()\n\n\n        HudRenderCallback.EVENT.register(IRHudRenderCallback)\n        WorldRenderEvents.BEFORE_BLOCK_OUTLINE.register(IRWorldRenderer)\n        WorldRenderEvents.BEFORE_ENTITIES.register(MatterProjectorPreviewRenderer)\n\n        ClientTickEvents.END_CLIENT_TICK.register(IRClientTickEvents)\n\n        ClientSpriteRegistryCallback.event(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE).register(\n            ClientSpriteRegistryCallback { _, registry ->\n                registry.register(identifier(\"block/lazuli_flux_container_lf_level\"))\n                registry.register(identifier(\"gui/hud_damaged\"))\n                registry.register(identifier(\"gui/hud_regenerating\"))\n                registry.register(identifier(\"gui/hud_warning\"))\n                registry.register(identifier(\"gui/hud_default\"))\n                registry.register(identifier(\"particle/laser_particle_1\"))\n                registry.register(identifier(\"particle/laser_particle_2\"))\n                registry.register(identifier(\"particle/laser_particle_3\"))\n            })\n\n        ParticleFactoryRegistry.getInstance().register(IndustrialRevolution.LASER_PARTICLE) { spriteProvider ->\n            FlameParticle.Factory(spriteProvider)\n        }\n\n        ClientPlayConnectionEvents.DISCONNECT.register { _, _ ->\n            IRConfig.readConfigs()\n        }\n\n        LivingEntityFeatureRendererRegistrationCallback.EVENT.register(IRLivingEntityFeatureRendererCallback)\n        TooltipComponentCallback.EVENT.register(IRTooltipComponentsCallback)\n        ItemTooltipCallback.EVENT.register(MiningRigInfoTooltipCallback)\n\n        AprilFools.init()\n    }\n\n    val MODULAR_CONTROLLER_KEYBINDING: KeyBinding = KeyBindingHelper.registerKeyBinding(\n        KeyBinding(\n            \"key.indrev.modular\",\n            InputUtil.Type.KEYSYM,\n            GLFW.GLFW_KEY_K,\n            \"category.indrev\"\n        )\n    )\n\n    val GAMER_AXE_TOGGLE_KEYBINDING: KeyBinding = KeyBindingHelper.registerKeyBinding(\n        KeyBinding(\n            \"key.indrev.gamer_axe_toggle\",\n            InputUtil.Type.KEYSYM,\n            GLFW.GLFW_KEY_G,\n            \"category.indrev\"\n        )\n    )\n\n    val CLIENT_NETWORK_STATE = Object2ObjectOpenHashMap<Network.Type<*>, ClientNetworkState<*>>()\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/AttributeModifierProvider.kt",
    "content": "package me.steven.indrev.api\n\nimport com.google.common.collect.Multimap\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.entity.attribute.EntityAttribute\nimport net.minecraft.entity.attribute.EntityAttributeModifier\nimport net.minecraft.item.ItemStack\n\ninterface AttributeModifierProvider {\n    fun getAttributeModifiers(itemStack: ItemStack, equipmentSlot: EquipmentSlot): Multimap<EntityAttribute, EntityAttributeModifier>\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/CustomEnchantmentProvider.kt",
    "content": "package me.steven.indrev.api\n\nimport net.minecraft.enchantment.Enchantment\nimport net.minecraft.item.ItemStack\n\ninterface CustomEnchantmentProvider {\n    fun getLevel(enchantment: Enchantment, itemStack: ItemStack): Int\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/IREntityExtension.kt",
    "content": "package me.steven.indrev.api\n\nimport me.steven.indrev.inventories.IRInventory\n\ninterface IREntityExtension {\n    var machineInv: IRInventory?\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/IRPlayerEntityExtension.kt",
    "content": "package me.steven.indrev.api\n\nimport me.steven.indrev.tools.modular.ArmorModule\n\ninterface IRPlayerEntityExtension {\n\n    var shieldDurability: Double\n    var isRegenerating: Boolean\n\n    fun getMaxShieldDurability(): Double\n\n    fun getAppliedModules(): Map<ArmorModule, Int>\n    fun applyModule(module: ArmorModule, level: Int)\n    fun isApplied(module: ArmorModule): Boolean\n    fun getAppliedLevel(module: ArmorModule): Int\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/IRServerPlayerEntityExtension.kt",
    "content": "package me.steven.indrev.api\n\ninterface IRServerPlayerEntityExtension : IRPlayerEntityExtension {\n    fun shouldSync(): Boolean\n    fun sync()\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/OreDataCards.kt",
    "content": "package me.steven.indrev.api\n\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.registry.IRItemRegistry\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.item.Items\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.nbt.NbtList\nimport net.minecraft.tag.TagKey\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.registry.Registry\nimport java.util.*\nimport kotlin.math.pow\n\nobject OreDataCards {\n\n    const val MAX_SIZE = 2048\n    const val MAX_RICNHESS = 1.0\n    const val MAX_PER_CYCLE = 8\n\n    val INVALID_DATA = Data(emptyList(), mutableMapOf(), -1.0, -1, -1, -1, -1, -1)\n\n    fun isAllowed(stack: ItemStack): Boolean {\n        return IRConfig.miningRigConfig.allowedTags.any { stack.isIn(TagKey.of(Registry.ITEM_KEY, Identifier(it.key))) }\n    }\n\n    fun getCost(stack: ItemStack): Int {\n        return IRConfig.miningRigConfig.allowedTags.firstNotNullOfOrNull { if (stack.isIn(TagKey.of(Registry.ITEM_KEY, Identifier(it.key)))) it.value else null } ?: 0\n    }\n\n    fun readNbt(stack: ItemStack): Data? {\n        if (!stack.isOf(IRItemRegistry.ORE_DATA_CARD)) return null\n        val nbt = stack.getSubNbt(\"CardData\") ?: return null\n\n        val entriesNbt = nbt.getList(\"Entries\", 10)\n        val entries = mutableListOf<OreEntry>()\n        entriesNbt.forEach { element ->\n            val itemId = (element as NbtCompound).getString(\"ItemId\")\n            val optional = Registry.ITEM.getOrEmpty(Identifier(itemId))\n            if (!optional.isPresent) {\n                return INVALID_DATA\n            }\n\n            val count = element.getInt(\"Count\")\n            entries.add(OreEntry(optional.get(), count))\n        }\n\n        val modifiersNbt = nbt.getList(\"ModifiersUsed\", 10)\n        val modifiers = mutableMapOf<Modifier, Int>()\n        modifiersNbt.forEach { element ->\n            val modifierId = (element as NbtCompound).getInt(\"Modifier\")\n            val level = element.getInt(\"Level\")\n            modifiers[Modifier.values()[modifierId]] = level\n        }\n\n        val richness = nbt.getDouble(\"Richness\")\n        val size = nbt.getInt(\"Size\")\n        val used = nbt.getInt(\"Used\")\n        val speed = nbt.getInt(\"Speed\")\n        val rng = nbt.getInt(\"Rng\")\n        val energy = nbt.getInt(\"Energy\")\n\n        return Data(entries.toList(), modifiers, richness, speed, rng, energy, size, used)\n    }\n\n    fun writeNbt(stack: ItemStack, data: Data) {\n        if (!stack.isOf(IRItemRegistry.ORE_DATA_CARD)) return\n        val nbt = stack.getOrCreateSubNbt(\"CardData\")\n\n        val entriesNbt = NbtList()\n        data.entries.forEach { entry ->\n            val entryNbt = NbtCompound()\n            entryNbt.putString(\"ItemId\", Registry.ITEM.getId(entry.item).toString())\n            entryNbt.putInt(\"Count\", entry.count)\n            entriesNbt.add(entryNbt)\n        }\n        nbt.put(\"Entries\", entriesNbt)\n\n        val modifiersNbt = NbtList()\n        data.modifiersUsed.forEach { (modifier, level) ->\n            val modifierNbt = NbtCompound()\n            modifierNbt.putInt(\"Modifier\", modifier.ordinal)\n            modifierNbt.putInt(\"Level\", level)\n            modifiersNbt.add(modifierNbt)\n        }\n        nbt.put(\"ModifiersUsed\", modifiersNbt)\n\n        nbt.putDouble(\"Richness\", data.richness)\n        nbt.putInt(\"Size\", data.maxCycles)\n        nbt.putInt(\"Used\", data.used)\n        nbt.putInt(\"Speed\", data.speed)\n        nbt.putInt(\"Rng\", data.rng)\n        nbt.putInt(\"Energy\", data.energyRequired)\n    }\n\n    data class Data(val entries: List<OreEntry>, val modifiersUsed: MutableMap<Modifier, Int>, val richness: Double, val speed: Int, val rng: Int, val energyRequired: Int, val maxCycles: Int, var used: Int) {\n        fun isValid(): Boolean {\n            return this != INVALID_DATA && entries.isNotEmpty() && richness > 0 && maxCycles > 0 && maxCycles < MAX_SIZE\n        }\n\n        fun isEmpty(): Boolean {\n            return used >= maxCycles\n        }\n\n        fun pickRandom(random: Random): Item {\n            entries.forEach { entry ->\n                entry.order = -random.nextFloat().pow(1.0f / entry.count.toFloat())\n            }\n            return entries.minByOrNull { a -> a.order }!!.item\n        }\n    }\n\n    data class OreEntry(val item: Item, val count: Int, var order: Float = 0.0f)\n\n    enum class Modifier(val item: Item) {\n        RICHNESS(IRItemRegistry.ENRICHED_NIKOLITE_DUST()),\n        SPEED(Items.REDSTONE),\n        SIZE(Items.STONE),\n        RNG(Items.EMERALD);\n\n        val translationKey = \"item.indrev.ore_data_card.modifier.${name.lowercase()}\"\n\n        companion object {\n            fun isModifierItem(item: Item): Boolean = values().any { it.item == item }\n\n            fun byItem(item: Item): Modifier? = values().firstOrNull { it.item == item }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/ServerWorldExtension.kt",
    "content": "package me.steven.indrev.api\n\nimport it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap\nimport me.steven.indrev.networks.energy.EnergyNetworkState\nimport me.steven.indrev.networks.fluid.FluidNetworkState\nimport me.steven.indrev.networks.item.ItemNetworkState\nimport net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache\nimport net.minecraft.util.math.Direction\nimport team.reborn.energy.api.EnergyStorage\n\ninterface ServerWorldExtension {\n    fun indrev_getEnergyCache(): Long2ObjectOpenHashMap<BlockApiCache<EnergyStorage, Direction>>\n\n    fun indrev_getEnergyNetworkState(): EnergyNetworkState\n\n    fun indrev_getFluidNetworkState(): FluidNetworkState\n\n    fun indrev_getItemNetworkState(): ItemNetworkState\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/machines/Tier.kt",
    "content": "package me.steven.indrev.api.machines\n\nenum class Tier(val io: Long, val id: String) {\n    MK1(64, \"mk1\"),\n    MK2(128, \"mk2\"),\n    MK3(512, \"mk3\"),\n    MK4(4096, \"mk4\"),\n    CREATIVE(4096, \"creative\");\n\n    fun next(): Tier {\n        return when (this) {\n            MK1 -> MK2\n            MK2 -> MK3\n            MK3 -> MK4\n            else -> error(\"no tier after $this\")\n        }\n    }\n\n    companion object {\n        val VALUES = arrayOf(MK1, MK2, MK3, MK4)\n\n        val ALL_VALUES = values()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/machines/TransferMode.kt",
    "content": "package me.steven.indrev.api.machines\n\nenum class TransferMode(val rgb: Long, val input: Boolean, val output: Boolean) {\n    INPUT(0x997e75ff, true, false),\n    INPUT_FIRST(0x9975ff8e, true, false),\n    INPUT_SECOND(0x9975ff8e, true, false),\n    OUTPUT_FIRST(0x9975ff8e, false, true),\n    OUTPUT_SECOND(0x9975ff8e, false, true),\n    OUTPUT(0x99ffb175, false, true),\n    INPUT_OUTPUT(0x99d875ff, true, true),\n    NONE(-1, false, false);\n\n    fun next(): TransferMode = when (this) {\n        INPUT -> INPUT_FIRST\n        INPUT_FIRST -> INPUT_SECOND\n        INPUT_SECOND -> OUTPUT_FIRST\n        OUTPUT_FIRST -> OUTPUT_SECOND\n        OUTPUT_SECOND -> OUTPUT\n        OUTPUT -> INPUT_OUTPUT\n        INPUT_OUTPUT -> NONE\n        NONE -> INPUT\n    }\n\n    fun next(available: Array<out TransferMode>): TransferMode {\n        var current = this\n        for (i in values().indices) {\n            val possible = current.next()\n            if (available.contains(possible)) {\n                return possible\n            }\n            current = possible\n        }\n        return this\n    }\n\n    companion object {\n        val DEFAULT = arrayOf(INPUT, OUTPUT, INPUT_OUTPUT, NONE)\n        val SOLID_INFUSER = arrayOf(INPUT, OUTPUT, INPUT_OUTPUT, INPUT_FIRST, INPUT_SECOND, NONE)\n        val ELECTROLYTIC_SEPARATOR = arrayOf(TransferMode.INPUT, TransferMode.OUTPUT_FIRST, TransferMode.OUTPUT_SECOND, TransferMode.NONE)\n    }\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/sideconfigs/Configurable.kt",
    "content": "package me.steven.indrev.api.sideconfigs\n\nimport me.steven.indrev.api.machines.TransferMode\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.math.Direction\n\ninterface Configurable {\n    fun isConfigurable(type: ConfigurationType): Boolean\n    fun isFixed(type: ConfigurationType): Boolean\n    fun getValidConfigurations(type: ConfigurationType): Array<TransferMode>\n    fun getCurrentConfiguration(type: ConfigurationType): SideConfiguration\n    fun applyDefault(state: BlockState, type: ConfigurationType, configuration: MutableMap<Direction, TransferMode>)\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/sideconfigs/ConfigurationType.kt",
    "content": "package me.steven.indrev.api.sideconfigs\n\nimport me.steven.indrev.api.machines.TransferMode\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\n\nenum class ConfigurationType(val title: Text, vararg val validModes: TransferMode) {\n    ITEM(TranslatableText(\"item.indrev.wrench.item\"), TransferMode.INPUT, TransferMode.OUTPUT, TransferMode.INPUT_OUTPUT, TransferMode.INPUT_FIRST, TransferMode.INPUT_SECOND, TransferMode.NONE),\n    FLUID(TranslatableText(\"item.indrev.wrench.fluid\"), TransferMode.INPUT, TransferMode.OUTPUT, TransferMode.INPUT_OUTPUT, TransferMode.NONE),\n    ENERGY(TranslatableText(\"item.indrev.wrench.energy\"), TransferMode.INPUT, TransferMode.OUTPUT, TransferMode.NONE);\n\n    fun next(): ConfigurationType {\n        return when (this) {\n            ITEM -> FLUID\n            FLUID -> ENERGY\n            ENERGY -> ITEM\n        }\n    }\n\n    fun next(available: Array<ConfigurationType>): ConfigurationType {\n        var current = this\n        for (i in values().indices) {\n            val possible = current.next()\n            if (available.contains(possible)) {\n                return possible\n            }\n            current = possible\n        }\n        return this\n    }\n\n    companion object {\n        fun getTypes(blockEntity: Configurable) = values().filter { type -> blockEntity.isConfigurable(type) && !blockEntity.isFixed(type) }.toTypedArray()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/api/sideconfigs/SideConfiguration.kt",
    "content": "package me.steven.indrev.api.sideconfigs\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WToggleButton\nimport io.github.cottonmc.cotton.gui.widget.WWidget\nimport io.netty.buffer.Unpooled\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.blocks.machine.FacingMachineBlock\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport me.steven.indrev.gui.widgets.machines.WMachineSideDisplay\nimport me.steven.indrev.packets.common.ConfigureIOPackets\nimport me.steven.indrev.utils.add\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.World\nimport java.util.*\nimport java.util.function.Consumer\n\ndata class SideConfiguration(val type: ConfigurationType, private val transferConfig: EnumMap<Direction, TransferMode> = EnumMap(Direction::class.java))\n    : MutableMap<Direction, TransferMode> by transferConfig {\n\n    var autoPush = true\n    var autoPull = true\n\n    init {\n        Direction.values().forEach { dir -> this[dir] = TransferMode.NONE }\n    }\n\n    fun canInput(direction: Direction): Boolean {\n        return this[direction]?.input == true\n    }\n\n    fun canOutput(direction: Direction): Boolean {\n        return this[direction]?.output == true\n    }\n\n    fun writeNbt(tag: NbtCompound?) {\n        var transferConfigTag = tag?.getCompound(\"TransferConfig\")\n        if (tag?.contains(\"TransferConfig\") == false) {\n            transferConfigTag = NbtCompound()\n            tag.put(\"TransferConfig\", transferConfigTag)\n        }\n        val configTag = NbtCompound()\n        forEach { (dir, mode) ->\n            configTag.putString(dir.toString(), mode.toString())\n        }\n        transferConfigTag?.put(type.toString().lowercase(Locale.getDefault()), configTag)\n        configTag.putBoolean(\"AutoPush\", autoPush)\n        configTag.putBoolean(\"AutoPull\", autoPull)\n    }\n\n    fun readNbt(tag: NbtCompound?) {\n        if (tag?.contains(\"TransferConfig\") == true) {\n            val transferConfigTag = tag.getCompound(\"TransferConfig\")\n            val configTag = transferConfigTag.getCompound(type.toString().lowercase(Locale.getDefault()))\n            Direction.values().forEach { dir ->\n                val value = configTag.getString(dir.toString()).uppercase(Locale.getDefault())\n                if (value.isNotEmpty()) {\n                    val mode = TransferMode.valueOf(value)\n                    this[dir] = mode\n                }\n            }\n            if (configTag.contains(\"AutoPush\"))\n                autoPush = configTag.getBoolean(\"AutoPush\")\n            if (configTag.contains(\"AutoPull\"))\n                autoPull = configTag.getBoolean(\"AutoPull\")\n        }\n    }\n\n    fun writeBuf(buf: PacketByteBuf) {\n        buf.writeBoolean(autoPush)\n        buf.writeBoolean(autoPull)\n        forEach { dir, mode ->\n            buf.writeByte(dir.ordinal)\n            buf.writeByte(mode.ordinal)\n        }\n    }\n\n    fun readBuf(buf: PacketByteBuf) {\n        autoPush = buf.readBoolean()\n        autoPull = buf.readBoolean()\n        repeat(6) {\n            val direction = Direction.values()[buf.readByte().toInt()]\n            val mode = TransferMode.values()[buf.readByte().toInt()]\n            this[direction] = mode\n        }\n    }\n\n    fun getConfigurationPanel(world: World, pos: BlockPos, configurable: Configurable, playerInventory: PlayerInventory, type: ConfigurationType): WWidget {\n        val root = WGridPanel()\n        root.setSize(100, 128)\n\n        val configuration = this\n\n        if (configuration.type == ConfigurationType.ITEM) {\n            val autoPushBtn = WToggleButton(TranslatableText(\"item.indrev.wrench.autopush\"))\n            autoPushBtn.toggle = configuration.autoPush\n            autoPushBtn.onToggle = Consumer { v ->\n                configuration.autoPush = v\n                val buf = PacketByteBuf(Unpooled.buffer())\n                buf.writeEnumConstant(type)\n                buf.writeByte(0)\n                buf.writeBlockPos(pos)\n                buf.writeBoolean(v)\n                ClientPlayNetworking.send(ConfigureIOPackets.UPDATE_AUTO_OPERATION_PACKET_ID, buf)\n            }\n            root.add(autoPushBtn, 0, 4)\n            val autoPullBtn = WToggleButton(TranslatableText(\"item.indrev.wrench.autopull\"))\n            autoPullBtn.toggle = configuration.autoPull\n            autoPullBtn.onToggle = Consumer { v ->\n                configuration.autoPull = v\n                val buf = PacketByteBuf(Unpooled.buffer())\n                buf.writeEnumConstant(type)\n                buf.writeByte(1)\n                buf.writeBlockPos(pos)\n                buf.writeBoolean(v)\n                ClientPlayNetworking.send(ConfigureIOPackets.UPDATE_AUTO_OPERATION_PACKET_ID, buf)\n            }\n            root.add(autoPullBtn, 0.0, 4.8)\n        }\n\n        val blockState = world.getBlockState(pos)\n\n        val machineVisualizerPanel = WGridPanel()\n        MachineSide.values().forEach { side ->\n            var facing =\n                when {\n                    blockState.contains(HorizontalFacingMachineBlock.HORIZONTAL_FACING) ->\n                        blockState[HorizontalFacingMachineBlock.HORIZONTAL_FACING]\n                    blockState.contains(FacingMachineBlock.FACING) ->\n                        blockState[FacingMachineBlock.FACING]\n                    else ->\n                        Direction.UP\n                }\n            if (facing.axis.isVertical) {\n                facing = playerInventory.player.horizontalFacing.opposite\n            }\n            val direction = offset(facing, side.direction)\n            val mode = configuration[direction]!!\n            val widget = WMachineSideDisplay(side, direction, mode, world, pos)\n            widget.setOnClick {\n                widget.mode = widget.mode.next(configurable.getValidConfigurations(type))\n                configuration[direction] = widget.mode\n                val buf = PacketByteBuf(Unpooled.buffer())\n                buf.writeEnumConstant(type)\n                buf.writeBlockPos(pos)\n                buf.writeInt(direction.id)\n                buf.writeInt(widget.mode.ordinal)\n                ClientPlayNetworking.send(ConfigureIOPackets.UPDATE_MACHINE_SIDE_PACKET_ID, buf)\n            }\n            machineVisualizerPanel.add(widget, (side.x) * 1.2, (side.y) * 1.2)\n        }\n        root.add(machineVisualizerPanel, 0.5, 0.0)\n        return root\n    }\n\n    private fun offset(facing: Direction, side: Direction): Direction {\n        return if (side.axis.isVertical) side\n        else when (facing) {\n            Direction.NORTH -> side\n            Direction.SOUTH -> side.opposite\n            Direction.WEST -> side.rotateYCounterclockwise()\n            Direction.EAST -> side.rotateYClockwise()\n            else -> side\n        }\n    }\n\n    enum class MachineSide(val x: Int, val y: Int, val direction: Direction, val u1: Float, val v1: Float, val u2: Float, val v2: Float) {\n        FRONT(1, 1, Direction.NORTH, 5.333f, 5.333f, 10.666f, 10.666f),\n        LEFT(0, 1, Direction.EAST, 0.0f, 5.333f, 5.332f, 10.666f),\n        BACK(2, 2, Direction.SOUTH, 10.667f, 10.667f, 16.0f, 16f),\n        RIGHT(2, 1, Direction.WEST, 10.667f, 5.333f, 16.0f, 10.665f),\n        TOP(1, 0, Direction.UP, 5.333f, 0.0f, 10.666f, 5.333f),\n        BOTTOM(1, 2, Direction.DOWN, 5.333f, 10.667f, 10.666f, 15.998f)\n    }\n\n    companion object {\n        val EMPTY_ITEM = SideConfiguration(ConfigurationType.ITEM)\n        val EMPTY_FLUID = SideConfiguration(ConfigurationType.FLUID)\n        val EMPTY_ENERGY = SideConfiguration(ConfigurationType.ENERGY)\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/armor/IRArmorMaterial.kt",
    "content": "package me.steven.indrev.armor\n\nimport me.steven.indrev.registry.IRItemRegistry\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.item.ArmorMaterial\nimport net.minecraft.recipe.Ingredient\nimport net.minecraft.sound.SoundEvent\nimport net.minecraft.sound.SoundEvents\n\nenum class IRArmorMaterial(\n    private val armorName: String,\n    private val durabilityMultiplier: Int,\n    private val armorValues: IntArray,\n    private val enchantability: Int,\n    private val equipSound: SoundEvent,\n    private val toughness: Float,\n    private val knockbackResistance: Float,\n    private val repairIngredient: () -> Ingredient?) : ArmorMaterial {\n\n    MODULAR(\n        \"modular\", 0, intArrayOf(1, 3, 2, 1), 15,\n        SoundEvents.ITEM_ARMOR_EQUIP_IRON, 1.0F, 1.0F, { null }\n    ),\n    STEEL_ELYTRA(\n        \"reinforced_elytra\", 30, intArrayOf(1, 1, 1, 1), 15,\n        SoundEvents.ITEM_ARMOR_EQUIP_IRON, 1.0F, 0F, { Ingredient.ofItems(IRItemRegistry.STEEL_PLATE()) }\n    ),\n    JETPACK(\n        \"jetpack\", 30, intArrayOf(1, 1, 1, 1), 15,\n        SoundEvents.ITEM_ARMOR_EQUIP_IRON, 1.0F, 0F, { null }\n    ),\n    STEEL(\n        \"steel\", 30, intArrayOf(2, 6, 7, 2), 15,\n        SoundEvents.ITEM_ARMOR_EQUIP_IRON, 1.0F, 0F, { Ingredient.ofItems(IRItemRegistry.STEEL_INGOT()) }\n    ),\n    COPPER(\n        \"copper\", 14, intArrayOf(2, 4, 5, 2), 15,\n        SoundEvents.ITEM_ARMOR_EQUIP_GENERIC, 0.0F, 0.0F, { Ingredient.ofItems(IRItemRegistry.COPPER_INGOT()) }\n    ),\n    TIN(\n        \"tin\", 10, intArrayOf(1, 4, 5, 2), 15,\n        SoundEvents.ITEM_ARMOR_EQUIP_GENERIC, 0.0F, 0.0F, { Ingredient.ofItems(IRItemRegistry.TIN_INGOT()) }\n    ),\n    BRONZE(\n        \"bronze\", 15, intArrayOf(2, 5, 6, 2), 15,\n        SoundEvents.ITEM_ARMOR_EQUIP_GENERIC, 0.0F, 0.0F, { Ingredient.ofItems(IRItemRegistry.BRONZE_INGOT()) }\n    ),\n    LEAD(\n        \"lead\", 20, intArrayOf(2, 4, 4, 1), 15,\n        SoundEvents.ITEM_ARMOR_EQUIP_GENERIC, 0.0F, 0.0F, { Ingredient.ofItems(IRItemRegistry.LEAD_INGOT()) }\n    ),\n    SILVER(\n        \"silver\", 30, intArrayOf(1, 4, 4, 1), 15,\n        SoundEvents.ITEM_ARMOR_EQUIP_GENERIC, 0.0F, 0.0F, { Ingredient.ofItems(IRItemRegistry.SILVER_INGOT()) }\n    );\n\n    override fun getName(): String = armorName\n    override fun getEquipSound(): SoundEvent = equipSound\n    override fun getRepairIngredient(): Ingredient? = repairIngredient()\n    override fun getEnchantability(): Int = enchantability\n    override fun getProtectionAmount(slot: EquipmentSlot): Int = this.armorValues[slot.entitySlotId]\n    override fun getDurability(slot: EquipmentSlot): Int = BASE_DURABILITY[slot.entitySlotId] * durabilityMultiplier\n    override fun getKnockbackResistance(): Float = knockbackResistance\n    override fun getToughness(): Float = toughness\n\n    companion object {\n        private val BASE_DURABILITY = intArrayOf(13, 15, 16, 11)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/armor/ModuleFeatureRenderer.kt",
    "content": "package me.steven.indrev.armor\n\nimport me.steven.indrev.items.armor.IRModularArmorItem\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.render.OverlayTexture\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.entity.feature.ArmorFeatureRenderer\nimport net.minecraft.client.render.entity.feature.FeatureRendererContext\nimport net.minecraft.client.render.entity.model.BipedEntityModel\nimport net.minecraft.client.render.item.ItemRenderer\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.item.ArmorItem\nimport net.minecraft.util.Identifier\n\nclass ModuleFeatureRenderer<T : LivingEntity, M : BipedEntityModel<T>, A : BipedEntityModel<T>>(\n    context: FeatureRendererContext<T, M>,\n    private val leggingsModel: A,\n    private val bodyModel: A\n) : ArmorFeatureRenderer<T, M, A>(context, leggingsModel, bodyModel) {\n\n    override fun render(matrixStack: MatrixStack, vertexConsumerProvider: VertexConsumerProvider, i: Int, livingEntity: T, f: Float, g: Float, h: Float, j: Float, k: Float, l: Float) {\n        renderArmor(matrixStack, vertexConsumerProvider, livingEntity, EquipmentSlot.CHEST, i, getArmor(EquipmentSlot.CHEST))\n        renderArmor(matrixStack, vertexConsumerProvider, livingEntity, EquipmentSlot.LEGS, i, getArmor(EquipmentSlot.LEGS))\n        renderArmor(matrixStack, vertexConsumerProvider, livingEntity, EquipmentSlot.FEET, i, getArmor(EquipmentSlot.FEET))\n        renderArmor(matrixStack, vertexConsumerProvider, livingEntity, EquipmentSlot.HEAD, i, getArmor(EquipmentSlot.HEAD))\n    }\n\n    private fun renderArmor(matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, livingEntity: T, equipmentSlot: EquipmentSlot, light: Int, bipedEntityModel: A) {\n        val itemStack = livingEntity.getEquippedStack(equipmentSlot)\n        val item = itemStack.item as? IRModularArmorItem ?: return\n        if (item.slotType == equipmentSlot) {\n            (this.contextModel as? BipedEntityModel<T>)?.setAttributes(bipedEntityModel) ?: return\n            setVisible(bipedEntityModel, equipmentSlot)\n            val rgb = item.getColor(itemStack)\n            val r = (rgb and 0xFF0000 shr 16) / 255f\n            val g = (rgb and 0xFF00 shr 8) / 255f\n            val b = (rgb and 0xFF) / 255f\n            item.getInstalled(itemStack).filter { it.slots.contains(equipmentSlot) }.forEach { module ->\n                if (module.hasTexture) {\n                    renderArmorParts(\n                        matrices, vertexConsumers, light, item, itemStack.hasGlint(), bipedEntityModel, usesSecondLayer(equipmentSlot), r, g, b, module.key\n                    )\n                    if (module.hasOverlay) {\n                        renderArmorParts(\n                            matrices, vertexConsumers, 15728880, item, itemStack.hasGlint(), bipedEntityModel, usesSecondLayer(equipmentSlot), r, g, b, \"${module.key}_overlay\"\n                        )\n                    }\n                }\n            }\n        }\n    }\n\n    private fun renderArmorParts(\n        matrixStack: MatrixStack,\n        vertexConsumerProvider: VertexConsumerProvider,\n        light: Int,\n        armorItem: ArmorItem,\n        hasGlint: Boolean,\n        bipedEntityModel: A,\n        secondLayer: Boolean,\n        r: Float, g: Float, b: Float,\n        overlay: String?) {\n        val vertexConsumer = ItemRenderer.getArmorGlintConsumer(vertexConsumerProvider, RenderLayer.getArmorCutoutNoCull(getArmorTexture(armorItem, secondLayer, overlay)), false, hasGlint)\n        bipedEntityModel.render(matrixStack, vertexConsumer, light, OverlayTexture.DEFAULT_UV, r, g, b, 1.0f)\n    }\n\n    private fun getArmor(slot: EquipmentSlot): A {\n        return if (usesSecondLayer(slot)) leggingsModel else bodyModel\n    }\n\n    private fun usesSecondLayer(slot: EquipmentSlot): Boolean {\n        return slot == EquipmentSlot.LEGS\n    }\n\n    private fun getArmorTexture(armorItem: ArmorItem, secondLayer: Boolean, overlay: String?): Identifier {\n        val path = \"textures/models/armor/\" + armorItem.material.name + \"_layer_\" + (if (secondLayer) 2 else 1) + (if (overlay == null) \"\" else \"_$overlay\") + \".png\"\n        return MODULAR_ARMOR_TEXTURE_CACHE.computeIfAbsent(path) { id -> identifier(id) }\n    }\n\n    companion object {\n        val MODULAR_ARMOR_TEXTURE_CACHE = mutableMapOf<String, Identifier>()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/armor/ReinforcedElytraFeatureRenderer.kt",
    "content": "package me.steven.indrev.armor\n\nimport me.steven.indrev.items.armor.ReinforcedElytraItem\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.minecraft.client.network.AbstractClientPlayerEntity\nimport net.minecraft.client.render.OverlayTexture\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.entity.PlayerModelPart\nimport net.minecraft.client.render.entity.feature.FeatureRenderer\nimport net.minecraft.client.render.entity.feature.FeatureRendererContext\nimport net.minecraft.client.render.entity.model.ElytraEntityModel\nimport net.minecraft.client.render.entity.model.EntityModel\nimport net.minecraft.client.render.entity.model.EntityModelLayers\nimport net.minecraft.client.render.entity.model.EntityModelLoader\nimport net.minecraft.client.render.item.ItemRenderer\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.entity.LivingEntity\n\n@Environment(EnvType.CLIENT)\nclass ReinforcedElytraFeatureRenderer<T : LivingEntity, M : EntityModel<T>>(\n    context: FeatureRendererContext<T, M>,\n    loader: EntityModelLoader\n) : FeatureRenderer<T, M>(context) {\n\n    private val elytraModel: ElytraEntityModel<T> = ElytraEntityModel(loader.getModelPart(EntityModelLayers.ELYTRA))\n\n    override fun render(\n        matrices: MatrixStack,\n        vertexConsumers: VertexConsumerProvider,\n        light: Int,\n        entity: T,\n        limbAngle: Float,\n        limbDistance: Float,\n        tickDelta: Float,\n        animationProgress: Float,\n        headYaw: Float,\n        headPitch: Float\n    ) {\n        val itemStack = entity.getEquippedStack(EquipmentSlot.CHEST)\n        if (ReinforcedElytraItem.hasValidElytra(itemStack)) {\n            val textureId = when {\n                entity !is AbstractClientPlayerEntity -> REINFORCED_ELYTRA_SKIN\n                entity.canRenderElytraTexture() && entity.elytraTexture != null -> entity.elytraTexture!!\n                entity.canRenderCapeTexture() && entity.capeTexture != null && entity.isPartVisible(PlayerModelPart.CAPE) -> entity.capeTexture!!\n                else -> REINFORCED_ELYTRA_SKIN\n            }\n            matrices.push()\n            matrices.translate(0.0, 0.0, 0.125)\n            this.contextModel.copyStateTo(elytraModel)\n            elytraModel.setAngles(entity, limbAngle, limbDistance, animationProgress, headYaw, headPitch)\n            val vertexConsumer = ItemRenderer.getArmorGlintConsumer(\n                vertexConsumers,\n                RenderLayer.getArmorCutoutNoCull(textureId),\n                false,\n                itemStack.hasGlint()\n            )\n            elytraModel.render(matrices, vertexConsumer, light, OverlayTexture.DEFAULT_UV, 1.0f, 1.0f, 1.0f, 1.0f)\n            matrices.pop()\n        }\n    }\n\n    companion object {\n        private val REINFORCED_ELYTRA_SKIN = identifier(\"textures/entity/reinforced_elytra.png\")\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/BaseBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities\n\nimport com.google.common.base.Preconditions\nimport me.steven.indrev.components.GuiSyncableComponent\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\n\nabstract class BaseBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState) : BlockEntity(type, pos, state) {\n\n    open val guiSyncableComponent: GuiSyncableComponent? = null\n\n    fun sync() {\n        Preconditions.checkNotNull(world) // Maintain distinct failure case from below\n        check(world is ServerWorld) { \"Cannot call sync() on the logical client! Did you check world.isClient first?\" }\n        (world as ServerWorld).chunkManager.markForUpdate(getPos())\n    }\n\n    abstract fun toTag(tag: NbtCompound)\n\n    abstract fun fromTag(tag: NbtCompound)\n\n    open fun toClientTag(tag: NbtCompound) {\n        toTag(tag)\n    }\n\n    open fun fromClientTag(tag: NbtCompound) {\n        fromTag(tag)\n    }\n\n    override fun toUpdatePacket(): BlockEntityUpdateS2CPacket {\n        return BlockEntityUpdateS2CPacket.create(this)\n    }\n\n    override fun toInitialChunkDataNbt(): NbtCompound {\n        val nbt = super.toInitialChunkDataNbt()\n        toClientTag(nbt)\n        nbt.putBoolean(\"#c\", true)\n        return nbt\n    }\n\n    override fun writeNbt(nbt: NbtCompound) {\n        super.writeNbt(nbt)\n        toTag(nbt)\n    }\n\n    override fun readNbt(nbt: NbtCompound) {\n        super.readNbt(nbt)\n        if (nbt.contains(\"#c\")) {\n            fromClientTag(nbt)\n        } else {\n            fromTag(nbt)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/BaseMachineBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities\n\nimport me.steven.indrev.api.sideconfigs.Configurable\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.InventoryProvider\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.util.math.BlockPos\n\nabstract class BaseMachineBlockEntity(type: BlockEntityType<*>, pos: BlockPos, state: BlockState)\n    : BaseBlockEntity(type, pos, state), InventoryProvider, Configurable, Syncable {\n    /**\n     * Whether the machine should call #sync or not\n     */\n    open val syncToWorld = false\n\n    var isMarkedForUpdate: Boolean = true\n\n    override fun markForUpdate(condition: () -> Boolean) {\n        isMarkedForUpdate = isMarkedForUpdate || condition()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/GlobalStateController.kt",
    "content": "package me.steven.indrev.blockentities\n\nimport it.unimi.dsi.fastutil.longs.Long2BooleanOpenHashMap\nimport it.unimi.dsi.fastutil.longs.Long2ObjectMap\nimport it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap\nimport me.steven.indrev.packets.client.MachineStateUpdatePacket\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport me.steven.indrev.utils.component3\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents\nimport net.fabricmc.fabric.api.networking.v1.PacketByteBufs\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.ChunkPos\nimport net.minecraft.world.World\nimport java.util.function.LongFunction\n\nobject GlobalStateController {\n    @Environment(EnvType.CLIENT)\n    val chunksToUpdate: Long2ObjectMap<MutableSet<BlockPos>> = Long2ObjectOpenHashMap()\n    @Environment(EnvType.CLIENT)\n    val workingStateTracker = Long2BooleanOpenHashMap()\n\n    @Environment(EnvType.CLIENT)\n    fun queueUpdate(pos: BlockPos) {\n        val chunkPos = ChunkPos.toLong(pos.x shr 4, pos.z shr 4)\n        if (MinecraftClient.getInstance().isOnThread)\n            chunksToUpdate.computeIfAbsent(chunkPos, LongFunction { hashSetOf() }).add(pos)\n        else\n            MinecraftClient.getInstance().execute { chunksToUpdate.computeIfAbsent(chunkPos, LongFunction { hashSetOf() }).add(pos) }\n    }\n\n    fun update(world: World, pos: BlockPos, workingState: Boolean) {\n        val (x, y, z) = pos\n        val players = world.server!!.playerManager.playerList\n        for (i in players.indices) {\n            val player = players[i]\n            if (player.world.registryKey === world.registryKey) {\n                val xOffset = x - player.x\n                val yOffset = y - player.y\n                val zOffset = z - player.z\n                if (xOffset * xOffset + yOffset * yOffset + zOffset * zOffset < 64 * 64) {\n                    val buf = PacketByteBufs.create()\n                    buf.writeLong(pos.asLong())\n                    buf.writeBoolean(workingState)\n                    ServerPlayNetworking.send(player, MachineStateUpdatePacket.UPDATE_PACKET_ID, buf)\n                }\n            }\n        }\n    }\n\n    @Environment(EnvType.CLIENT)\n    fun initClient() {\n        var ticks = 0\n        ClientTickEvents.END_CLIENT_TICK.register { client ->\n            ticks++\n            val world = client.world\n            if (world != null && ticks % 15 == 0) {\n                chunksToUpdate.values.removeIf { positions ->\n                    positions.forEach { (x, y, z) ->\n                        client.worldRenderer.scheduleBlockRenders(x , y, z, x, y,z)\n                    }\n                    true\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/MachineBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities\n\nimport me.steven.indrev.IREnergyStorage\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.api.sideconfigs.SideConfiguration\nimport me.steven.indrev.blocks.machine.MachineBlock\nimport me.steven.indrev.components.*\nimport me.steven.indrev.components.multiblock.MultiBlockComponent\nimport me.steven.indrev.config.IConfig\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.bucket\nimport me.steven.indrev.utils.transferEnergy\nimport me.steven.indrev.utils.transferFluids\nimport me.steven.indrev.utils.transferItems\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.inventory.SidedInventory\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.WorldAccess\nimport kotlin.collections.set\n\nabstract class MachineBlockEntity<T : IConfig>(val tier: Tier, val registry: MachineRegistry, pos: BlockPos, state: BlockState)\n    : BaseMachineBlockEntity(registry.blockEntityType(tier), pos, state) {\n\n    val validConnections = Direction.values().toMutableList()\n\n    override var guiSyncableComponent: GuiSyncableComponent? = GuiSyncableComponent()\n\n    var energy: Long by autosync(ENERGY_ID, 0L) { value ->\n        when (tier) {\n            Tier.CREATIVE -> energyCapacity\n            else -> value.coerceIn(0, energyCapacity)\n        }\n    }\n\n    var inventoryComponent: InventoryComponent? = null\n    var temperatureComponent: TemperatureComponent? = null\n    var fluidComponent: FluidComponent? = null\n    var multiblockComponent: MultiBlockComponent? = null\n    var enhancerComponent: EnhancerComponent? = null\n\n    var itemTransferCooldown = 0\n\n    var workingState: Boolean = false\n        set(value) {\n            val update = value != field\n            field = value\n            if (update && world?.isClient == false)\n                GlobalStateController.update(world!!, pos, value)\n        }\n        get() {\n            if (world?.isClient == true && GlobalStateController.workingStateTracker.contains(pos.asLong())) {\n                MinecraftClient.getInstance().execute { field = GlobalStateController.workingStateTracker.remove(pos.asLong()) }\n            }\n            return field\n        }\n\n    var ticks = 0\n\n    @Suppress(\"UNCHECKED_CAST\")\n    val config: T by lazy { registry.config(tier) as T }\n\n    open val maxInput: Long = tier.io\n    open val maxOutput: Long = tier.io\n    open val energyCapacity: Long get() = config.maxEnergyStored + (IRConfig.upgrades.bufferUpgradeModifier * (enhancerComponent?.getCount(Enhancer.BUFFER) ?: 0))\n\n    init {\n        trackLong(MAX_ENERGY_ID) { energyCapacity }\n    }\n\n    open val storage = MachineEnergyStorage()\n\n    protected open fun machineTick() {}\n\n    @Environment(EnvType.CLIENT)\n    open fun machineClientTick() {}\n\n    fun tick() {\n        inventoryComponent?.run { enhancerComponent?.updateEnhancers(inventory) }\n        ticks++\n        multiblockComponent?.tick(world!!, pos, cachedState)\n        if (multiblockComponent?.isBuilt(world!!, pos, cachedState) == false) return\n\n        transferEnergy()\n        transferItems()\n        transferFluids()\n\n        machineTick()\n        if (isMarkedForUpdate) {\n            markDirty()\n            if (syncToWorld) sync()\n            isMarkedForUpdate = false\n        }\n    }\n\n    open fun getProcessingSpeed(): Double {\n        return 1.0 + (IRConfig.upgrades.speedUpgradeModifier * (enhancerComponent?.getCount(Enhancer.SPEED) ?: 0))\n    }\n\n    open fun getEnergyCost(): Long = 0\n\n    // internal consumption\n    fun use(amount: Long): Boolean {\n        val extracted = amount.coerceAtMost(energy)\n        if (extracted == amount) {\n            this.energy -= extracted\n            return true\n        }\n        return false\n    }\n\n    fun canUse(amount: Long): Boolean {\n        val extracted = amount.coerceAtMost(energy)\n        return extracted == amount\n    }\n\n    override fun getInventory(state: BlockState?, world: WorldAccess?, pos: BlockPos?): SidedInventory? = inventoryComponent?.inventory\n\n    override fun isConfigurable(type: ConfigurationType): Boolean {\n        return when (type) {\n            ConfigurationType.ITEM -> inventoryComponent != null\n                    && (inventoryComponent?.inventory?.inputSlots?.isNotEmpty() == true\n                    || inventoryComponent?.inventory?.outputSlots?.isNotEmpty() == true)\n            ConfigurationType.FLUID -> fluidComponent != null\n            ConfigurationType.ENERGY -> false\n        }\n    }\n\n    override fun applyDefault(state: BlockState, type: ConfigurationType, configuration: MutableMap<Direction, TransferMode>) {\n        val direction = (state.block as MachineBlock).getFacing(state)\n        when (type) {\n            ConfigurationType.ITEM -> {\n                configuration[direction.rotateYClockwise()] = TransferMode.INPUT\n                configuration[direction.rotateYCounterclockwise()] = TransferMode.OUTPUT\n            }\n            ConfigurationType.ENERGY -> throw IllegalArgumentException(\"cannot apply energy configuration to $this\")\n            else -> return\n        }\n    }\n\n    override fun getValidConfigurations(type: ConfigurationType): Array<TransferMode> {\n        return when (type) {\n            ConfigurationType.ITEM -> TransferMode.DEFAULT\n            ConfigurationType.FLUID, ConfigurationType.ENERGY -> arrayOf(TransferMode.INPUT, TransferMode.OUTPUT, TransferMode.NONE)\n        }\n    }\n\n    override fun getCurrentConfiguration(type: ConfigurationType): SideConfiguration {\n        return when (type) {\n            ConfigurationType.ITEM -> inventoryComponent!!.itemConfig\n            ConfigurationType.FLUID -> fluidComponent!!.transferConfig\n            ConfigurationType.ENERGY -> error(\"nope\")\n        }\n    }\n\n    override fun isFixed(type: ConfigurationType): Boolean = false\n\n    open fun getFluidTransferRate(): Long = when (tier) {\n        Tier.MK1 -> bucket / 3\n        Tier.MK2 -> bucket * 2 / 3\n        else -> bucket\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        inventoryComponent?.readNbt(tag)\n        inventoryComponent?.run { enhancerComponent?.updateEnhancers(inventory) }\n        temperatureComponent?.readNbt(tag)\n        fluidComponent?.fromTag(tag)\n        multiblockComponent?.readNbt(tag)\n        energy = tag.getLong(\"Energy\")\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        tag.putLong(\"Energy\", energy)\n        inventoryComponent?.writeNbt(tag)\n        temperatureComponent?.writeNbt(tag)\n        fluidComponent?.toTag(tag)\n        multiblockComponent?.writeNbt(tag)\n    }\n\n    override fun fromClientTag(tag: NbtCompound) {\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n    }\n\n    open inner class MachineEnergyStorage : IREnergyStorage() {\n\n        override fun getAmount(): Long = energy\n\n        override fun setAmount(v: Long) {\n            energy = v\n        }\n\n        override fun getCapacity(): Long = energyCapacity\n\n        override fun getMaxExtract(side: Direction?): Long = maxOutput\n\n        override fun getMaxInsert(side: Direction?): Long = maxInput\n\n        override fun onFinalCommit() {\n            super.onFinalCommit()\n            markForUpdate()\n        }\n    }\n\n    companion object {\n        const val ENERGY_ID = 0\n        const val MAX_ENERGY_ID = 1\n        const val TEMPERATURE_ID = 2\n        const val MAX_TEMPERATURE_ID = 3\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/Syncable.kt",
    "content": "package me.steven.indrev.blockentities\n\ninterface Syncable {\n    fun markForUpdate(condition: () -> Boolean = { true })\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/cables/BasePipeBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.cables\n\nimport com.google.common.base.Preconditions\nimport it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blocks.machine.pipes.BasePipeBlock\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.registry.IRBlockRegistry\nimport net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.nbt.NbtList\nimport net.minecraft.nbt.NbtOps\nimport net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\n\nclass BasePipeBlockEntity(val pipeType: Network.Type<*>, tier: Tier, pos: BlockPos, state: BlockState) :\n    BlockEntity(when (tier) {\n        Tier.MK1 -> IRBlockRegistry.COVERABLE_BLOCK_ENTITY_TYPE_MK1\n        Tier.MK2 -> IRBlockRegistry.COVERABLE_BLOCK_ENTITY_TYPE_MK2\n        Tier.MK3 -> IRBlockRegistry.COVERABLE_BLOCK_ENTITY_TYPE_MK3\n        Tier.MK4 -> IRBlockRegistry.COVERABLE_BLOCK_ENTITY_TYPE_MK4\n        Tier.CREATIVE -> error(\"no creative cable\")\n                            }, pos, state), RenderAttachmentBlockEntity {\n\n    val connections = Object2ObjectOpenHashMap<Direction, BasePipeBlock.ConnectionType>()\n\n    init {\n        connections.defaultReturnValue(BasePipeBlock.ConnectionType.NONE)\n    }\n\n    var coverState: BlockState? = null\n\n    override fun getRenderAttachmentData(): Any {\n        return PipeRenderData(\n            coverState,\n            connections.filterValues { type -> type == BasePipeBlock.ConnectionType.CONNECTED }.keys.toTypedArray()\n        )\n    }\n\n    override fun readNbt(tag: NbtCompound) {\n        this.coverState = null\n        if (tag.contains(\"coverState\")) {\n            BlockState.CODEC.decode(NbtOps.INSTANCE, tag.getCompound(\"coverState\")).result().ifPresent { pair ->\n                this.coverState = pair.first\n            }\n        }\n        val list = tag.getList(\"connections\", 10)\n        connections.clear()\n        list?.forEach { t ->\n            t as NbtCompound\n            val dir = Direction.byId(t.getByte(\"d\").toInt())\n            val type = BasePipeBlock.ConnectionType.byId(t.getByte(\"c\").toInt())\n            connections[dir] = type\n        }\n        super.readNbt(tag)\n\n        if (world != null && world!!.isClient) {\n            MinecraftClient.getInstance().worldRenderer.updateBlock(world!!, pos, null, null, 0)\n        }\n    }\n\n    override fun writeNbt(tag: NbtCompound) {\n        if (this.coverState != null) {\n            BlockState.CODEC.encode(this.coverState, NbtOps.INSTANCE, NbtCompound()).result().ifPresent { t ->\n                tag.put(\"coverState\", t)\n            }\n        }\n        val list = NbtList()\n        connections.forEach { (dir, conn) ->\n            if (conn != BasePipeBlock.ConnectionType.NONE) {\n                val t = NbtCompound()\n                t.putByte(\"d\", dir.id.toByte())\n                t.putByte(\"c\", conn.id.toByte())\n                list.add(t)\n            }\n        }\n        tag.put(\"connections\", list)\n    }\n\n    fun sync() {\n        Preconditions.checkNotNull(world) // Maintain distinct failure case from below\n        check(world is ServerWorld) { \"Cannot call sync() on the logical client! Did you check world.isClient first?\" }\n        (world as ServerWorld).chunkManager.markForUpdate(getPos())\n    }\n\n    override fun toUpdatePacket(): BlockEntityUpdateS2CPacket {\n        return BlockEntityUpdateS2CPacket.create(this)\n    }\n\n    override fun toInitialChunkDataNbt(): NbtCompound {\n        val nbt = super.toInitialChunkDataNbt()\n        writeNbt(nbt)\n        return nbt\n    }\n\n    data class PipeRenderData(val cover: BlockState?, val connections: Array<Direction>)\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/CompressorBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.CompressorRecipe\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.math.BlockPos\n\nclass CompressorBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    CraftingMachineBlockEntity<CompressorRecipe>(tier, MachineRegistry.COMPRESSOR_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1500)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(4, 5, 6, 7), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input { slot = 2 }\n            output { slot = 3 }\n        }\n\n        trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0])\n    }\n\n    override val type: IRRecipeType<CompressorRecipe> = CompressorRecipe.TYPE\n\n    companion object {\n        const val CRAFTING_COMPONENT_ID = 4\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/CompressorFactoryBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.CraftingComponent\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.multiblock.MultiBlockComponent\nimport me.steven.indrev.components.multiblock.definitions.FactoryStructureDefinition\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.CompressorRecipe\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\n\nclass CompressorFactoryBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    CraftingMachineBlockEntity<CompressorRecipe>(tier, MachineRegistry.COMPRESSOR_FACTORY_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(2, 3, 4, 5), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input { slots = intArrayOf(6, 8, 10, 12, 14) }\n            output { slots = intArrayOf(7, 9, 11, 13, 15) }\n        }\n        this.craftingComponents = Array(5) { index ->\n            val component = CraftingComponent(index, this).apply {\n                inputSlots = intArrayOf(6 + (index * 2))\n                outputSlots = intArrayOf(6 + (index * 2) + 1)\n            }\n            trackObject(CRAFTING_COMPONENT_START_ID + index, component)\n            component\n        }\n        this.multiblockComponent = MultiBlockComponent(FactoryStructureDefinition.SELECTOR)\n    }\n\n    override val syncToWorld: Boolean = true\n\n    override val type: IRRecipeType<CompressorRecipe> = CompressorRecipe.TYPE\n\n    override fun fromClientTag(tag: NbtCompound) {\n        multiblockComponent?.readNbt(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        multiblockComponent?.writeNbt(tag)\n    }\n\n    companion object {\n        const val CRAFTING_COMPONENT_START_ID = 4\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/CondenserBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.blocks.machine.MachineBlock\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.FluidComponent\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.CondenserRecipe\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.bucket\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\n\nclass CondenserBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    CraftingMachineBlockEntity<CondenserRecipe>(tier, MachineRegistry.CONDENSER_REGISTRY, pos, state) {\n\n    init {\n        this.enhancerComponent = EnhancerComponent(intArrayOf(3, 4, 5, 6), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            output { slot = 2 }\n            coolerSlot = 1\n        }\n        this.fluidComponent = object : FluidComponent({ this }, bucket * 8) {\n            init {\n                this.inputTanks = intArrayOf(0)\n            }\n        }\n\n        trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0])\n\n        trackObject(INPUT_TANK_ID, fluidComponent!![0])\n    }\n\n    override val syncToWorld: Boolean = true\n\n    override val type: IRRecipeType<CondenserRecipe> = CondenserRecipe.TYPE\n\n    override fun getMaxCount(enhancer: Enhancer): Int {\n        return if (enhancer == Enhancer.SPEED) 4 else super.getMaxCount(enhancer)\n    }\n\n    override fun applyDefault(\n        state: BlockState,\n        type: ConfigurationType,\n        configuration: MutableMap<Direction, TransferMode>\n    ) {\n        val direction = (state.block as MachineBlock).getFacing(state)\n        when (type) {\n            ConfigurationType.ITEM -> {\n                configuration[direction.rotateYCounterclockwise()] = TransferMode.OUTPUT\n            }\n            else -> super.applyDefault(state, type, configuration)\n        }\n    }\n\n    override fun getValidConfigurations(type: ConfigurationType): Array<TransferMode> {\n        return when (type) {\n            ConfigurationType.ITEM -> arrayOf(TransferMode.OUTPUT, TransferMode.NONE)\n            else -> return super.getValidConfigurations(type)\n        }\n    }\n\n    override fun fromClientTag(tag: NbtCompound) {\n        fluidComponent!!.fromTag(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        fluidComponent!!.toTag(tag)\n    }\n\n\n    companion object {\n        const val CRAFTING_COMPONENT_ID = 2\n        const val INPUT_TANK_ID = 3\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/CondenserBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport alexiil.mc.lib.attributes.fluid.render.FluidRenderFace\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.math.Direction\n\nclass CondenserBlockEntityRenderer : BlockEntityRenderer<CondenserBlockEntity> {\n\n    override fun render(\n        entity: CondenserBlockEntity?,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider?,\n        light: Int,\n        overlay: Int\n    ) {\n        val fluidComponent = entity?.fluidComponent ?: return\n        val faces = when (entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING]) {\n            Direction.NORTH -> NORTH_FACE\n            Direction.SOUTH -> SOUTH_FACE\n            Direction.WEST -> WEST_FACE\n            Direction.EAST -> EAST_FACE\n            else -> return\n        }\n        val volume = fluidComponent[0]\n        if (!volume.isEmpty) {\n            volume.render(faces, vertexConsumers, matrices)\n        }\n    }\n\n    companion object {\n        private val NORTH_FACE =\n            listOf(FluidRenderFace.createFlatFaceZ(0.815, 0.625, -0.005, 0.19, 0.815, -0.005, 1.0, true, false))\n        private val SOUTH_FACE =\n            listOf(FluidRenderFace.createFlatFaceZ(0.185, 0.625, 1.005, 0.81, 0.815, 1.005, 1.0, true, false))\n        private val WEST_FACE =\n            listOf(FluidRenderFace.createFlatFaceX(-0.005, 0.625, 0.185, -0.005, 0.815, 0.81, 1.0, false, false))\n        private val EAST_FACE =\n            listOf(FluidRenderFace.createFlatFaceX(1.005, 0.625, 0.815, 1.005, 0.815, 0.19, 1.0, false, false))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/CraftingMachineBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport it.unimi.dsi.fastutil.objects.Object2IntArrayMap\nimport it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.components.CraftingComponent\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.config.HeatMachineConfig\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.IRecipeGetter\nimport me.steven.indrev.recipes.machines.IRRecipe\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.entity.ExperienceOrbEntity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.nbt.NbtList\nimport net.minecraft.recipe.SmeltingRecipe\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Vec3d\nimport net.minecraft.world.World\nimport java.util.function.IntBinaryOperator\nimport kotlin.math.floor\n\nabstract class CraftingMachineBlockEntity<T : IRRecipe>(tier: Tier, registry: MachineRegistry, pos: BlockPos, state: BlockState) :\n    MachineBlockEntity<BasicMachineConfig>(tier, registry, pos, state) {\n\n    override val maxOutput: Long = 0\n\n    private var currentRecipe: T? = null\n    val usedRecipes = Object2IntOpenHashMap<Identifier>()\n    abstract val type: IRecipeGetter<T>\n    var craftingComponents = Array(1) { CraftingComponent(0, this) }\n    var isSplitOn = false\n\n    override fun machineTick() {\n        ticks++\n        craftingComponents.forEach { it.tick() }\n        workingState = craftingComponents.any { it.isCrafting }\n        if (ticks % 20 == 0 && isSplitOn) { splitStacks() }\n    }\n\n    override fun getProcessingSpeed(): Double {\n        val isFullEfficiency = temperatureComponent?.isFullEfficiency() == true\n        val baseSpeed = if (isFullEfficiency)\n            ((config as? HeatMachineConfig)?.processTemperatureBoost ?: 1.0) * config.processSpeed\n        else\n            config.processSpeed\n        return baseSpeed + (IRConfig.upgrades.speedUpgradeModifier * (enhancerComponent?.getCount(Enhancer.SPEED) ?: 0))\n    }\n\n    override fun getEnergyCost(): Long {\n        val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1)\n        return (if (temperatureComponent?.isFullEfficiency() == true) config.energyCost * 1.5\n        else config.energyCost).toLong() * speedEnhancers\n    }\n\n    open fun getMaxCount(enhancer: Enhancer): Int {\n        return when (enhancer) {\n            Enhancer.SPEED -> return 1\n            Enhancer.BUFFER -> 4\n            else -> 1\n        }\n    }\n\n    open fun splitStacks() {\n        if (craftingComponents.size <= 1) return\n        val inventory = inventoryComponent!!.inventory\n        splitStacks(inventory.inputSlots)\n    }\n\n    fun splitStacks(inputSlots: IntArray) {\n        if (craftingComponents.size <= 1) return\n        val inventory = inventoryComponent!!.inventory\n        val (item, sum) = inputSlots.associateStacks { inventory.getStack(it) }.maxByOrNull { it.value } ?: return\n        if (sum <= 0) return\n\n        val freeSlots = inputSlots.filter { inventory.fits(item, it) }\n        var remaining = sum\n        val slotsUsed = freeSlots.size.coerceAtMost(sum)\n        val rem = sum % slotsUsed\n        val isBelowLimit = slotsUsed > freeSlots.size\n        val baseAmount = Math.floorDiv(sum, slotsUsed)\n        freeSlots.forEachIndexed { index, slot ->\n            if (isBelowLimit && index + 1 > slotsUsed) {\n                inventory.setStack(slot, ItemStack.EMPTY)\n                return@forEachIndexed\n            }\n            var set = baseAmount\n            if (rem != 0 && rem > index && remaining > 0)\n                set++\n            if (index == slotsUsed - 1)\n                set += remaining\n            remaining -= set\n            if (remaining < 0) set += remaining\n            inventory.setStack(slot, ItemStack(item, set))\n        }\n    }\n\n    private inline fun IntArray.associateStacks(transform: (Int) -> ItemStack): Map<Item, Int> {\n        return associateToStacks(Object2IntArrayMap(5), transform)\n    }\n\n    private inline fun <M : Object2IntArrayMap<Item>> IntArray.associateToStacks(destination: M, transform: (Int) -> ItemStack): M {\n        for (element in this) {\n            val stack = transform(element)\n            if (!stack.isEmpty && stack.nbt?.isEmpty != false)\n                destination.mergeInt(stack.item, stack.count, IntBinaryOperator { old, new -> old + new })\n        }\n        return destination\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        val craftTags = tag.getList(\"craftingComponents\", 10)\n        craftTags?.forEach { craftTag ->\n            val index = (craftTag as NbtCompound).getInt(\"index\")\n            craftingComponents[index].readNbt(craftTag)\n        }\n        isSplitOn = tag.getBoolean(\"split\")\n        super.fromTag(tag)\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        val craftTags = NbtList()\n        craftingComponents.forEachIndexed { index, crafting ->\n            val craftTag = NbtCompound()\n            craftTags.add(crafting.writeNbt(craftTag))\n            craftTag.putInt(\"index\", index)\n        }\n        tag.put(\"craftingComponents\", craftTags)\n        tag.putBoolean(\"split\", isSplitOn)\n        return super.toTag(tag)\n    }\n\n    @Suppress(\"UNCHECKED_CAST\")\n    fun dropExperience(player: PlayerEntity) {\n        val list = mutableListOf<T>()\n\n        usedRecipes.forEach { (id, amount) ->\n            for (recipes in world!!.recipeManager.recipes.values) {\n                val recipe = recipes[id] ?: continue\n                list.add(recipe as? T ?: continue)\n                if (recipe is SmeltingRecipe)\n                    spawnOrbs(world!!, player.pos, amount, recipe.experience)\n                break\n            }\n        }\n\n        player.unlockRecipes(list.toList())\n        usedRecipes.clear()\n    }\n\n    private fun spawnOrbs(world: World, pos: Vec3d, amount: Int, experience: Float) {\n        val xp = amount.toFloat() * experience\n        var n = floor(xp).toInt()\n        val decimal = xp % 1\n        if (decimal != 0.0f && Math.random() < decimal.toDouble()) {\n            ++n\n        }\n        ExperienceOrbEntity.spawn(world as ServerWorld, pos, n)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/ElectricFurnaceBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.mixin.common.MixinAbstractCookingRecipe\nimport me.steven.indrev.recipes.IRecipeGetter\nimport me.steven.indrev.recipes.machines.VanillaCookingRecipeCachedGetter\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.math.BlockPos\n\nclass ElectricFurnaceBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    CraftingMachineBlockEntity<MixinAbstractCookingRecipe>(tier, MachineRegistry.ELECTRIC_FURNACE_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.1, 1300..1700, 2000)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(4, 5, 6, 7), Enhancer.FURNACE, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input { slot = 2 }\n            output { slot = 3 }\n        }\n        trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0])\n    }\n\n    @Suppress(\"UNCHECKED_CAST\")\n    override val type: IRecipeGetter<MixinAbstractCookingRecipe>\n        get() {\n            val upgrades = enhancerComponent!!.enhancers\n            return when (upgrades.keys.firstOrNull { it == Enhancer.BLAST_FURNACE || it == Enhancer.SMOKER }) {\n                Enhancer.BLAST_FURNACE -> VanillaCookingRecipeCachedGetter.BLASTING\n                Enhancer.SMOKER -> VanillaCookingRecipeCachedGetter.SMOKING\n                else -> VanillaCookingRecipeCachedGetter.SMELTING\n            } as IRecipeGetter<MixinAbstractCookingRecipe>\n        }\n\n    companion object {\n        const val CRAFTING_COMPONENT_ID = 4\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/ElectricFurnaceFactoryBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.CraftingComponent\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.multiblock.MultiBlockComponent\nimport me.steven.indrev.components.multiblock.definitions.FactoryStructureDefinition\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.mixin.common.MixinAbstractCookingRecipe\nimport me.steven.indrev.recipes.IRecipeGetter\nimport me.steven.indrev.recipes.machines.VanillaCookingRecipeCachedGetter\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\n\nclass ElectricFurnaceFactoryBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    CraftingMachineBlockEntity<MixinAbstractCookingRecipe>(tier, MachineRegistry.ELECTRIC_FURNACE_FACTORY_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.1, 1300..1700, 2000)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(2, 3, 4, 5), Enhancer.FURNACE, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input { slots = intArrayOf(6, 8, 10, 12, 14) }\n            output { slots = intArrayOf(7, 9, 11, 13, 15) }\n        }\n        this.craftingComponents = Array(5) { index ->\n            val component = CraftingComponent(index, this).apply {\n                inputSlots = intArrayOf(6 + (index * 2))\n                outputSlots = intArrayOf(6 + (index * 2) + 1)\n            }\n            trackObject(CRAFTING_COMPONENT_START_ID + index, component)\n            component\n        }\n        this.multiblockComponent = MultiBlockComponent(FactoryStructureDefinition.SELECTOR)\n    }\n\n    override val syncToWorld: Boolean = true\n\n    @Suppress(\"UNCHECKED_CAST\")\n    override val type: IRecipeGetter<MixinAbstractCookingRecipe>\n        get() {\n            val upgrades = enhancerComponent!!.enhancers\n            return when (upgrades.keys.firstOrNull { it == Enhancer.BLAST_FURNACE || it == Enhancer.SMOKER }) {\n                Enhancer.BLAST_FURNACE -> VanillaCookingRecipeCachedGetter.BLASTING\n                Enhancer.SMOKER -> VanillaCookingRecipeCachedGetter.SMOKING\n                else -> VanillaCookingRecipeCachedGetter.SMELTING\n            } as IRecipeGetter<MixinAbstractCookingRecipe>\n        }\n\n    override fun fromClientTag(tag: NbtCompound) {\n        multiblockComponent?.readNbt(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        multiblockComponent?.writeNbt(tag)\n    }\n\n    companion object {\n        const val CRAFTING_COMPONENT_START_ID = 4\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/ElectrolyticSeparatorBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.FluidComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.ElectrolysisRecipe\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.bucket\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\n\nclass ElectrolyticSeparatorBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : CraftingMachineBlockEntity<ElectrolysisRecipe>(tier, MachineRegistry.ELECTROLYTIC_SEPARATOR_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.06, 500..700, 900)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(1, 2, 3, 4), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            coolerSlot = 0\n        }\n        this.fluidComponent = ElectrolyticSeparatorFluidComponent()\n\n        trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0])\n\n        trackObject(INPUT_TANK_ID, fluidComponent!![0])\n        trackObject(FIRST_OUTPUT_TANK_ID, fluidComponent!![1])\n        trackObject(SECOND_OUTPUT_TANK_ID, fluidComponent!![2])\n    }\n\n    override val type: IRRecipeType<ElectrolysisRecipe> = ElectrolysisRecipe.TYPE\n\n    override fun applyDefault(\n        state: BlockState,\n        type: ConfigurationType,\n        configuration: MutableMap<Direction, TransferMode>\n    ) {\n        if (type != ConfigurationType.ITEM)\n            super.applyDefault(state, type, configuration)\n    }\n\n    override fun getValidConfigurations(type: ConfigurationType): Array<TransferMode> {\n        return when (type) {\n            ConfigurationType.FLUID -> TransferMode.ELECTROLYTIC_SEPARATOR\n            else -> return super.getValidConfigurations(type)\n        }\n    }\n\n    inner class ElectrolyticSeparatorFluidComponent : FluidComponent({ this }, bucket * 4, 3) {\n\n        init {\n            this.inputTanks = intArrayOf(0)\n            this.outputTanks = intArrayOf(1, 2)\n        }\n\n        override fun getValidTanks(dir: Direction): IntArray {\n            return when (transferConfig[dir]!!) {\n                TransferMode.OUTPUT_FIRST -> intArrayOf(1)\n                TransferMode.OUTPUT_SECOND -> intArrayOf(2)\n                else -> super.getValidTanks(dir)\n            }\n        }\n    }\n\n    companion object {\n        const val CRAFTING_COMPONENT_ID = 4\n        const val INPUT_TANK_ID = 5\n        const val FIRST_OUTPUT_TANK_ID = 6\n        const val SECOND_OUTPUT_TANK_ID = 7\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/FluidInfuserBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.FluidComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.FluidInfuserRecipe\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.bucket\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\n\nclass FluidInfuserBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : CraftingMachineBlockEntity<FluidInfuserRecipe>(tier, MachineRegistry.FLUID_INFUSER_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(4, 5, 6, 7), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input { slot = 2 }\n            output { slot = 3 }\n        }\n        this.fluidComponent = FluidInfuserFluidComponent()\n\n        trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0])\n\n        trackObject(INPUT_TANK_ID, fluidComponent!![0])\n        trackObject(OUTPUT_TANK_ID, fluidComponent!![1])\n    }\n\n    override val syncToWorld: Boolean = true\n\n    override val type: IRRecipeType<FluidInfuserRecipe> = FluidInfuserRecipe.TYPE\n\n    override fun fromClientTag(tag: NbtCompound) {\n        fluidComponent!!.fromTag(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        fluidComponent!!.toTag(tag)\n    }\n\n    inner class FluidInfuserFluidComponent : FluidComponent({ this }, bucket * 8 , 2) {\n        init {\n            this.inputTanks = intArrayOf(0)\n            this.outputTanks = intArrayOf(1)\n\n        }\n    }\n\n    companion object {\n        const val CRAFTING_COMPONENT_ID = 4\n        const val INPUT_TANK_ID = 5\n        const val OUTPUT_TANK_ID = 6\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/FluidInfuserBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport alexiil.mc.lib.attributes.fluid.render.FluidRenderFace\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.math.Direction\n\nclass FluidInfuserBlockEntityRenderer : BlockEntityRenderer<FluidInfuserBlockEntity>{\n    override fun render(\n        entity: FluidInfuserBlockEntity?,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider?,\n        light: Int,\n        overlay: Int\n    ) {\n        val fluidComponent = entity?.fluidComponent ?: return\n        val inputFace = when (entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING]) {\n            Direction.NORTH -> INPUT_NORTH_FACE\n            Direction.SOUTH -> INPUT_SOUTH_FACE\n            Direction.WEST -> INPUT_WEST_FACE\n            Direction.EAST -> INPUT_EAST_FACE\n            else -> return\n        }\n        val inputVolume = fluidComponent[0]\n        if (!inputVolume.isEmpty) {\n            inputVolume.render(inputFace, vertexConsumers, matrices)\n        }\n\n        val outputFace = when (entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING]) {\n            Direction.NORTH -> OUTPUT_NORTH_FACE\n            Direction.SOUTH -> OUTPUT_SOUTH_FACE\n            Direction.WEST -> OUTPUT_WEST_FACE\n            Direction.EAST -> OUTPUT_EAST_FACE\n            else -> return\n        }\n        val outputVolume = fluidComponent[1]\n        if (!outputVolume.isEmpty) {\n            outputVolume.render(outputFace, vertexConsumers, matrices)\n        }\n    }\n\n    companion object {\n        private val INPUT_NORTH_FACE =\n            listOf(FluidRenderFace.createFlatFaceZ(0.815, 0.690, -0.005, 0.19, 0.815, -0.005, 1.0, true, false))\n        private val INPUT_SOUTH_FACE =\n            listOf(FluidRenderFace.createFlatFaceZ(0.185, 0.690, 1.005, 0.81, 0.815, 1.005, 1.0, true, false))\n        private val INPUT_WEST_FACE =\n            listOf(FluidRenderFace.createFlatFaceX(-0.005, 0.690, 0.185, -0.005, 0.815, 0.81, 1.0, false, false))\n        private val INPUT_EAST_FACE =\n            listOf(FluidRenderFace.createFlatFaceX(1.005, 0.690, 0.815, 1.005, 0.815, 0.19, 1.0, false, false))\n\n        private val OUTPUT_NORTH_FACE =\n            listOf(FluidRenderFace.createFlatFaceZ(0.815, 0.190, -0.005, 0.19, 0.32, -0.005, 1.0, true, false))\n        private val OUTPUT_SOUTH_FACE =\n            listOf(FluidRenderFace.createFlatFaceZ(0.185, 0.190, 1.005, 0.81, 0.32, 1.005, 1.0, true, false))\n        private val OUTPUT_WEST_FACE =\n            listOf(FluidRenderFace.createFlatFaceX(-0.005, 0.190, 0.185, -0.005, 0.32, 0.81, 1.0, false, false))\n        private val OUTPUT_EAST_FACE =\n            listOf(FluidRenderFace.createFlatFaceX(1.005, 0.190, 0.815, 1.005, 0.32, 0.19, 1.0, false, false))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/PulverizerBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.recipes.machines.PulverizerRecipe\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.math.BlockPos\n\nclass PulverizerBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    CraftingMachineBlockEntity<PulverizerRecipe>(tier, MachineRegistry.PULVERIZER_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(5, 6, 7, 8), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input { slot = 2 }\n            output { slots = intArrayOf(3, 4) }\n        }\n\n        trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0])\n    }\n\n    override val type: IRRecipeType<PulverizerRecipe> = PulverizerRecipe.TYPE\n\n    companion object {\n        const val CRAFTING_COMPONENT_ID = 4\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/PulverizerFactoryBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.CraftingComponent\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.multiblock.MultiBlockComponent\nimport me.steven.indrev.components.multiblock.definitions.FactoryStructureDefinition\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.recipes.machines.PulverizerRecipe\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\n\nclass PulverizerFactoryBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    CraftingMachineBlockEntity<PulverizerRecipe>(tier, MachineRegistry.PULVERIZER_FACTORY_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(2, 3, 4, 5), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input { slots = intArrayOf(6, 8, 10, 12, 14) }\n            output { slots = intArrayOf(7, 9, 11, 13, 15) }\n        }\n        this.craftingComponents = Array(5) { index ->\n            val component = CraftingComponent(index, this).apply {\n                inputSlots = intArrayOf(6 + (index * 2))\n                outputSlots = intArrayOf(6 + (index * 2) + 1)\n            }\n            trackObject(CRAFTING_COMPONENT_START_ID + index, component)\n            component\n        }\n        this.multiblockComponent = MultiBlockComponent(FactoryStructureDefinition.SELECTOR)\n    }\n\n    override val syncToWorld: Boolean = true\n\n    override val type: IRRecipeType<PulverizerRecipe> = PulverizerRecipe.TYPE\n\n    override fun fromClientTag(tag: NbtCompound) {\n        multiblockComponent?.readNbt(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        multiblockComponent?.writeNbt(tag)\n    }\n\n    companion object {\n        const val CRAFTING_COMPONENT_START_ID = 4\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/RecyclerBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.recipes.machines.RecyclerRecipe\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.math.BlockPos\n\nclass RecyclerBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : CraftingMachineBlockEntity<RecyclerRecipe>(tier, MachineRegistry.RECYCLER_REGISTRY, pos, state) {\n\n    init {\n        this.enhancerComponent = object : EnhancerComponent(intArrayOf(4, 5, 6, 7), Enhancer.DEFAULT, this::getMaxCount) {\n            override fun isLocked(slot: Int, tier: Tier): Boolean = false\n        }\n\n        this.inventoryComponent = inventory(this) {\n            input { slot = 2 }\n            output { slot = 3 }\n            coolerSlot = 1\n        }\n\n        trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0])\n    }\n\n    override val type: IRRecipeType<RecyclerRecipe> = RecyclerRecipe.TYPE\n\n    companion object {\n        const val CRAFTING_COMPONENT_ID = 4\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/SawmillBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.recipes.machines.SawmillRecipe\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.math.BlockPos\n\nclass SawmillBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : CraftingMachineBlockEntity<SawmillRecipe>(tier, MachineRegistry.SAWMILL_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(7, 8, 9, 10), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input { slot = 2 }\n            output { slots = intArrayOf(3, 4, 5, 6) }\n        }\n\n        trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0])\n    }\n\n    override val type: IRRecipeType<SawmillRecipe> = SawmillRecipe.TYPE\n\n    companion object {\n        const val CRAFTING_COMPONENT_ID = 4\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/SmelterBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.blocks.machine.MachineBlock\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.FluidComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.recipes.machines.SmelterRecipe\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.bucket\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\n\nclass SmelterBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    CraftingMachineBlockEntity<SmelterRecipe>(tier, MachineRegistry.SMELTER_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.2, 1700..2500, 2700)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(3, 4, 5, 6), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input { slot = 2 }\n        }\n        this.fluidComponent = object : FluidComponent({ this }, bucket * 8) {\n            init {\n                this.outputTanks = intArrayOf(0)\n            }\n        }\n        trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0])\n        trackObject(TANK_ID, fluidComponent!![0])\n    }\n\n    override val type: IRRecipeType<SmelterRecipe> = SmelterRecipe.TYPE\n\n    override fun getMaxCount(enhancer: Enhancer): Int {\n        return if (enhancer == Enhancer.SPEED) 4 else super.getMaxCount(enhancer)\n    }\n\n    override fun applyDefault(\n        state: BlockState,\n        type: ConfigurationType,\n        configuration: MutableMap<Direction, TransferMode>\n    ) {\n        val direction = (state.block as MachineBlock).getFacing(state)\n        when (type) {\n            ConfigurationType.ITEM -> {\n                configuration[direction.rotateYClockwise()] = TransferMode.INPUT\n            }\n            else -> super.applyDefault(state, type, configuration)\n        }\n    }\n\n    override fun getValidConfigurations(type: ConfigurationType): Array<TransferMode> {\n        return when (type) {\n            ConfigurationType.ITEM -> arrayOf(TransferMode.INPUT, TransferMode.NONE)\n            else -> return super.getValidConfigurations(type)\n        }\n    }\n\n    companion object {\n        const val CRAFTING_COMPONENT_ID = 4\n        const val TANK_ID = 5\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/SolidInfuserBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.recipes.machines.InfuserRecipe\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\n\nclass SolidInfuserBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    CraftingMachineBlockEntity<InfuserRecipe>(tier, MachineRegistry.SOLID_INFUSER_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(5, 6, 7, 8), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input {\n                slots = intArrayOf(2, 3)\n                filter { _, dir, slot -> canInput(dir, slot) }\n            }\n            output { slot = 4 }\n        }\n\n        trackObject(CRAFTING_COMPONENT_ID, craftingComponents[0])\n    }\n\n    private fun canInput(side: Direction?, slot: Int): Boolean {\n        if (side == null) return true\n        return when (inventoryComponent!!.itemConfig[side]) {\n            TransferMode.INPUT_FIRST -> slot == 2\n            TransferMode.INPUT_SECOND -> slot == 3\n            else -> true\n        }\n    }\n\n    override fun getValidConfigurations(type: ConfigurationType): Array<TransferMode> {\n        return when (type) {\n            ConfigurationType.ITEM -> TransferMode.SOLID_INFUSER\n            else -> super.getValidConfigurations(type)\n        }\n    }\n\n    override val type: IRRecipeType<InfuserRecipe> = InfuserRecipe.TYPE\n\n    companion object {\n        const val CRAFTING_COMPONENT_ID = 4\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/crafters/SolidInfuserFactoryBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.crafters\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.components.CraftingComponent\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.multiblock.MultiBlockComponent\nimport me.steven.indrev.components.multiblock.definitions.FactoryStructureDefinition\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.machines.IRRecipeType\nimport me.steven.indrev.recipes.machines.InfuserRecipe\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\n\nclass SolidInfuserFactoryBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    CraftingMachineBlockEntity<InfuserRecipe>(tier, MachineRegistry.SOLID_INFUSER_FACTORY_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.06, 700..1100, 1400)\n        this.enhancerComponent = EnhancerComponent(intArrayOf(2, 3, 4, 5), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input {\n                slots = intArrayOf(6, 7, 9, 10, 12, 13, 15, 16, 18, 19)\n                filter { _, dir, slot -> canInput(dir, slot) }\n            }\n            output { slots = intArrayOf(8, 11, 14, 17, 20) }\n        }\n        this.craftingComponents = Array(5) { index ->\n            val component = CraftingComponent(index, this).apply {\n                inputSlots = intArrayOf(6 + (index * 3), 6 + (index * 3) + 1)\n                outputSlots = intArrayOf(6 + (index * 3) + 2)\n            }\n            trackObject(CRAFTING_COMPONENT_START_ID + index, component)\n            component\n        }\n        this.multiblockComponent = MultiBlockComponent(FactoryStructureDefinition.SELECTOR)\n    }\n\n    override val syncToWorld: Boolean = true\n\n    override fun splitStacks() {\n        splitStacks(TOP_SLOTS)\n        splitStacks(BOTTOM_SLOTS)\n    }\n\n    private fun canInput(side: Direction?, slot: Int): Boolean {\n        if (side == null) return true\n        return when (inventoryComponent!!.itemConfig[side]) {\n            TransferMode.INPUT_FIRST -> TOP_SLOTS.contains(slot)\n            TransferMode.INPUT_SECOND -> BOTTOM_SLOTS.contains(slot)\n            else -> true\n        }\n    }\n\n    override fun getValidConfigurations(type: ConfigurationType): Array<TransferMode> {\n        return when (type) {\n            ConfigurationType.ITEM -> TransferMode.SOLID_INFUSER\n            else -> super.getValidConfigurations(type)\n        }\n    }\n\n    override val type: IRRecipeType<InfuserRecipe> = InfuserRecipe.TYPE\n\n    override fun fromClientTag(tag: NbtCompound) {\n        multiblockComponent?.readNbt(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        multiblockComponent?.writeNbt(tag)\n    }\n\n    companion object {\n        val TOP_SLOTS = intArrayOf(6, 9, 12, 15, 18)\n        val BOTTOM_SLOTS = intArrayOf(7, 10, 13, 16, 19)\n        const val CRAFTING_COMPONENT_START_ID = 4\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/AOEMachineBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport io.netty.buffer.Unpooled\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.config.IConfig\nimport me.steven.indrev.packets.common.UpdateAOEMachineRangePacket\nimport me.steven.indrev.registry.MachineRegistry\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Box\n\nabstract class AOEMachineBlockEntity<T : IConfig>(tier: Tier, registry: MachineRegistry, pos: BlockPos, state: BlockState) : MachineBlockEntity<T>(tier, registry, pos, state) {\n    override val syncToWorld: Boolean = true\n\n    var renderWorkingArea = false\n    abstract var range: Int\n    open fun getWorkingArea(): Box {\n        val box = Box(pos)\n        return box.expand(range.toDouble(), 0.0, range.toDouble()).stretch(0.0, range.toDouble() * 2, 0.0)\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        tag.putInt(\"range\", range)\n        super.toTag(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        tag.putInt(\"range\", range)\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        super.fromTag(tag)\n        range = tag.getInt(\"range\")\n    }\n\n    override fun fromClientTag(tag: NbtCompound) {\n        range = tag.getInt(\"range\")\n    }\n\n    companion object {\n        fun sendValueUpdatePacket(value: Int, ctx: ScreenHandlerContext) {\n            if (value > 0) {\n                val packet = PacketByteBuf(Unpooled.buffer())\n                packet.writeInt(value)\n                ctx.run { _, pos -> packet.writeBlockPos(pos) }\n                ClientPlayNetworking.send(UpdateAOEMachineRangePacket.UPDATE_VALUE_PACKET_ID, packet)\n            }\n        }\n\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/AOEMachineBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.WorldRenderer\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.util.math.MatrixStack\n\nopen class AOEMachineBlockEntityRenderer : BlockEntityRenderer<AOEMachineBlockEntity<*>> {\n    override fun render(\n        blockEntity: AOEMachineBlockEntity<*>,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider,\n        light: Int,\n        overlay: Int\n    ) {\n        if (blockEntity.renderWorkingArea) {\n            val pos = blockEntity.pos\n            val area = blockEntity.getWorkingArea().offset(-pos.x.toDouble(), -pos.y.toDouble(), -pos.z.toDouble())\n            val vc = vertexConsumers.getBuffer(RenderLayer.getLines())\n            WorldRenderer.drawBox(matrices, vc, area,  1f, 0f, 1f, 1f)\n        }\n    }\n\n    override fun rendersOutsideBoundingBox(blockEntity: AOEMachineBlockEntity<*>?): Boolean = true\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/BiomassComposterBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport alexiil.mc.lib.attributes.fluid.amount.FluidAmount\nimport alexiil.mc.lib.attributes.fluid.render.FluidRenderFace\nimport alexiil.mc.lib.attributes.fluid.volume.FluidKeys\nimport me.steven.indrev.blockentities.BaseBlockEntity\nimport me.steven.indrev.blocks.misc.BiomassComposterBlock\nimport me.steven.indrev.registry.IRBlockRegistry\nimport me.steven.indrev.registry.IRFluidRegistry\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.utils.bucket\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.item.ItemVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions\nimport net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage\nimport net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.ComposterBlock\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.fluid.Fluids\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\n\nclass BiomassComposterBlockEntity(pos: BlockPos, state: BlockState) : BaseBlockEntity(IRBlockRegistry.BIOMASS_COMPOSTER_BLOCK_ENTITY, pos, state) {\n\n    var ticks = 0\n    var level = 0\n    val fluidInv = BiomassComposterFluidInv()\n    val itemInv = BiomassComposterItemInv()\n\n    companion object {\n        fun tick(state: BlockState, blockEntity: BiomassComposterBlockEntity) {\n            val vol = blockEntity.fluidInv\n            if (blockEntity.isInProgress()) {\n                blockEntity.ticks++\n            }\n            if (blockEntity.isReady() && state[BiomassComposterBlock.CLOSED] && vol.amount > 0 && vol.resource.isOf(Fluids.WATER)) {\n                blockEntity.fluidInv.variant = FluidVariant.of(IRFluidRegistry.METHANE_STILL)\n                blockEntity.reset()\n            } else if (blockEntity.isReady() && !blockEntity.hasFluids()) {\n                blockEntity.itemInv.amount = 1\n                blockEntity.itemInv.variant = ItemVariant.of(IRItemRegistry.BIOMASS)\n                blockEntity.reset()\n            }\n\n            blockEntity.doneInsertionThisTick = false\n        }\n    }\n\n    private var doneInsertionThisTick = false\n\n    private fun hasFluids() = !fluidInv.isResourceBlank && fluidInv.amount > 0\n\n    fun isReady() = level >= 7 && ticks >= getProgressTime()\n\n    fun reset() {\n        level = 0\n        ticks = 0\n        markDirty()\n        if (!world!!.isClient)\n            sync()\n    }\n\n    fun isInProgress(): Boolean {\n        return when {\n            level < 7 -> false\n            cachedState[BiomassComposterBlock.CLOSED] -> fluidInv.variant.isOf(Fluids.WATER)\n            else -> !hasFluids()\n        } && ticks < getProgressTime()\n    }\n\n    fun getProgressTime() = if (!cachedState[BiomassComposterBlock.CLOSED]) 120 else 440\n\n    inner class BiomassComposterFluidInv : SingleVariantStorage<FluidVariant>() {\n        override fun canInsert(variant: FluidVariant): Boolean = variant.isOf(Fluids.WATER)\n\n        override fun canExtract(variant: FluidVariant): Boolean = variant.isOf(IRFluidRegistry.METHANE_STILL)\n\n        override fun getCapacity(variant: FluidVariant?): Long = bucket\n\n        override fun getBlankVariant(): FluidVariant = FluidVariant.blank()\n\n        fun render(faces: List<FluidRenderFace?>?, vcp: VertexConsumerProvider?, matrices: MatrixStack?) {\n            if (!variant.isBlank)\n                FluidKeys.get(variant.fluid).withAmount(FluidAmount.BUCKET).render(faces, vcp, matrices)\n        }\n    }\n\n    inner class BiomassComposterItemInv : SingleVariantStorage<ItemVariant>() {\n\n        override fun canInsert(variant: ItemVariant): Boolean = ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.contains(variant.item) && level < 7 && !doneInsertionThisTick\n\n        override fun insert(insertedVariant: ItemVariant, maxAmount: Long, transaction: TransactionContext?): Long {\n            StoragePreconditions.notBlankNotNegative(insertedVariant, maxAmount)\n\n            if ((insertedVariant == variant || variant.isBlank) && canInsert(insertedVariant)) {\n\n                val chance = ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.getValue(variant.item)\n                val insertedAmount = maxAmount.coerceAtMost(getCapacity(insertedVariant) - amount)\n                var actuallyInserted = 0L\n                for (x in 0 until insertedAmount) {\n                    if (world!!.random.nextDouble() < chance) {\n                        actuallyInserted++\n                    }\n                }\n                if (insertedAmount > 0) {\n                    updateSnapshots(transaction)\n                    if (variant.isBlank) {\n                        variant = insertedVariant\n                        amount = actuallyInserted\n                    } else {\n                        amount += actuallyInserted\n                    }\n                    doneInsertionThisTick = true\n                }\n                return insertedAmount\n            }\n\n            return 0\n        }\n\n        override fun getCapacity(variant: ItemVariant): Long = 7 - level.toLong()\n\n        override fun canExtract(variant: ItemVariant): Boolean = variant.isOf(IRItemRegistry.BIOMASS)\n\n        override fun getBlankVariant(): ItemVariant = ItemVariant.blank()\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        tag.putInt(\"ticks\", ticks)\n        tag.putInt(\"level\", level)\n        tag.put(\"fluidVariant\", fluidInv.variant.toNbt())\n        tag.putLong(\"fluidAmt\", fluidInv.amount)\n        tag.put(\"itemVariant\", itemInv.variant.toNbt())\n        tag.putLong(\"itemAmt\", itemInv.amount)\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        ticks = tag.getInt(\"ticks\")\n        level = tag.getInt(\"level\")\n        fluidInv.variant = FluidVariant.fromNbt(tag.getCompound(\"fluidVariant\"))\n        fluidInv.amount = tag.getLong(\"fluidAmt\")\n        itemInv.variant = ItemVariant.fromNbt(tag.getCompound(\"itemVariant\"))\n        itemInv.amount = tag.getLong(\"itemAmt\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/BiomassComposterBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport alexiil.mc.lib.attributes.fluid.render.FluidRenderFace\nimport me.steven.indrev.blocks.misc.BiomassComposterBlock\nimport me.steven.indrev.utils.bucket\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.OverlayTexture\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.WorldRenderer\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.util.ModelIdentifier\nimport net.minecraft.client.util.math.MatrixStack\n\nclass BiomassComposterBlockEntityRenderer : BlockEntityRenderer<BiomassComposterBlockEntity> {\n    override fun render(\n        entity: BiomassComposterBlockEntity,\n        tickDelta: Float,\n        matrices: MatrixStack,\n        vertexConsumers: VertexConsumerProvider,\n        light: Int,\n        overlay: Int\n    ) {\n        if (entity.cachedState[BiomassComposterBlock.CLOSED]) return\n        matrices.run {\n\n            if (entity.level > 0) {\n                push()\n                translate(0.0, ((((entity.level - 1) * 2)) / 16.0), 0.0)\n\n                val model = MinecraftClient.getInstance().bakedModelManager.getModel(ModelIdentifier(identifier(\"composting\"), \"\"))\n                MinecraftClient.getInstance().blockRenderManager.modelRenderer.render(\n                    matrices.peek(),\n                    vertexConsumers.getBuffer(RenderLayer.getTranslucent()),\n                    null,\n                    model,\n                    -1f,\n                    -1f,\n                    -1f,\n                    WorldRenderer.getLightmapCoordinates(entity.world, entity.pos.up()),\n                    OverlayTexture.DEFAULT_UV\n                )\n                pop()\n            }\n            push()\n            val vol = entity.fluidInv.amount / bucket.toDouble()\n            translate(0.0, vol, 0.0)\n            entity.fluidInv.render(FACE, vertexConsumers, matrices)\n            pop()\n        }\n    }\n\n    companion object {\n        private val FACE = listOf(FluidRenderFace.createFlatFaceY(0.125, 0.0, 0.125, 0.875, 0.0, 0.875, 1.0, true, false))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/ChopperBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.*\nimport net.minecraft.block.*\nimport net.minecraft.item.AxeItem\nimport net.minecraft.item.BlockItem\nimport net.minecraft.item.BoneMealItem\nimport net.minecraft.item.ItemStack\nimport net.minecraft.item.SwordItem\nimport net.minecraft.loot.context.LootContext\nimport net.minecraft.loot.context.LootContextParameters\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.tag.BlockTags\nimport net.minecraft.tag.ItemTags\nimport net.minecraft.util.ItemScatterer\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Box\nimport net.minecraft.world.chunk.Chunk\n\nclass ChopperBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : AOEMachineBlockEntity<BasicMachineConfig>(tier, MachineRegistry.CHOPPER_REGISTRY, pos, state) {\n    \n    init {\n        this.enhancerComponent = EnhancerComponent(intArrayOf(15, 16, 17, 18), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input {\n                slots = intArrayOf(2, 3, 4, 5)\n                //todo tool tags?\n                2 filter { stack -> stack.item is AxeItem || stack.item is SwordItem }\n                3 filter { (_, item) -> item is BoneMealItem }\n                4..5 filter { (stack, item), _ -> stack.isIn(ItemTags.SAPLINGS)\n                        || (item is BlockItem && (item.block is MushroomPlantBlock || item.block is BambooBlock)) }\n            }\n            output { slots = intArrayOf(6, 7, 8, 9, 10, 11, 12, 13, 14) }\n            coolerSlot = 1\n        }\n    }\n\n    override val maxInput: Long = config.maxInput\n    override val maxOutput: Long = 0\n\n    private var scheduledBlocks = mutableListOf<BlockPos>().iterator()\n    private val scannedBlocks = mutableSetOf<BlockPos>()\n    override var range = 5\n    var cooldown = 0.0\n\n    override fun machineTick() {\n        if (world?.isClient == true) return\n        val inventory = inventoryComponent?.inventory ?: return\n        cooldown += getProcessingSpeed()\n        if (cooldown < config.processSpeed || ticks % 15 != 0 || !canUse(getEnergyCost()))\n            return\n        val area = getWorkingArea()\n        if (!scheduledBlocks.hasNext()) {\n            // includes tree branches that goes outside the actual area\n            val fullArea = area.expand(4.0).shrink(0.0, -4.0, 0.0)\n            scheduledBlocks = fullArea.map(::BlockPos).iterator()\n            scannedBlocks.clear()\n        } else {\n            var currentChunk: Chunk? = null\n            var performedActions = 0\n            val axeStack = inventory.getStack(2)\n            val brokenBlocks = hashMapOf<BlockPos, BlockState>()\n            while (scheduledBlocks.hasNext() && cooldown >= config.processSpeed) {\n                val pos = scheduledBlocks.next()\n                if (!scannedBlocks.add(pos)) continue\n                if (pos.x shr 4 != currentChunk?.pos?.x || pos.z shr 4 != currentChunk.pos.z) {\n                    currentChunk = world?.getChunk(pos)\n                }\n                val blockState = currentChunk?.getBlockState(pos) ?: continue\n                if (axeStack != null\n                    && !axeStack.isEmpty\n                    && tryChop(axeStack, pos, blockState, currentChunk)\n                ) {\n                    cooldown -= config.processSpeed\n                    if (!use(getEnergyCost())) break\n                    brokenBlocks[pos] = blockState\n                    performedActions++\n                }\n                if (pos.y == this.pos.y && pos in area) {\n                    for (slot in 3..5) {\n                        val stack = inventory.getStack(slot)\n                        if (stack.isEmpty || !tryUse(blockState, stack, pos)) continue\n                        cooldown -= config.processSpeed\n                        if (!use(getEnergyCost())) break\n                        brokenBlocks[pos] = blockState\n                        performedActions++\n                    }\n                }\n            }\n            brokenBlocks.forEach { (blockPos, blockState) ->\n                val droppedStacks = blockState.getDroppedStacks(\n                    LootContext.Builder(world as ServerWorld).random(world?.random)\n                        .parameter(LootContextParameters.ORIGIN, blockPos.toVec3d())\n                        .parameter(LootContextParameters.TOOL, axeStack)\n                )\n                droppedStacks.forEach {\n                    if (!inventory.output(it))\n                        ItemScatterer.spawn(world, blockPos.x.toDouble(), blockPos.y.toDouble(), blockPos.z.toDouble(), it)\n                }\n            }\n            temperatureComponent?.tick(performedActions > 0)\n            workingState = performedActions > 0\n        }\n        cooldown = 0.0\n    }\n\n    private fun tryChop(\n        toolStack: ItemStack,\n        blockPos: BlockPos,\n        blockState: BlockState,\n        chunk: Chunk\n    ): Boolean {\n        fun damageTool(amount: Int): Boolean {\n            return when {\n                energyOf(toolStack) != null -> energyOf(toolStack)!!.use(amount.toLong())\n                toolStack.isEmpty -> false\n                toolStack.isDamageable -> {\n                    toolStack.damage(amount, world?.random, null)\n                    if (toolStack.damage >= toolStack.maxDamage)\n                        toolStack.decrement(1)\n                    true\n                }\n                else -> true\n            }\n        }\n        val block = blockState.block\n        when {\n            toolStack.item is AxeItem\n                    && (blockState.isIn(BlockTags.LOGS) || block is MushroomBlock || block == Blocks.MUSHROOM_STEM) -> {\n                if (!damageTool(1)) return false\n                world?.setBlockState(blockPos, Blocks.AIR.defaultState, 3)\n            }\n            block is LeavesBlock -> {\n                world?.setBlockState(blockPos, Blocks.AIR.defaultState, 3)\n            }\n            toolStack.item is SwordItem && block is BambooBlock && blockPos.y > pos.y -> {\n                val upPos = blockPos.up()\n                val up = chunk.getBlockState(upPos)\n                scannedBlocks.add(upPos)\n                if (up.isOf(block))\n                    tryChop(toolStack, upPos, blockState, chunk)\n                if (!damageTool(2)) return false\n                world?.setBlockState(blockPos, Blocks.AIR.defaultState, 3)\n            }\n            else -> return false\n        }\n        return true\n    }\n\n    private fun tryUse(blockState: BlockState, itemStack: ItemStack, pos: BlockPos): Boolean {\n        val item = itemStack.item\n        val block = blockState.block\n        when {\n            item is BoneMealItem && itemStack.count > 1\n                    && (blockState.isIn(BlockTags.SAPLINGS) || block is MushroomPlantBlock || block is BambooBlock || block is BambooSaplingBlock)\n                    && block is Fertilizable\n                    && block.isFertilizable(world, pos, blockState, false)\n                    && block.canGrow(world, world?.random, pos, blockState) -> {\n                block.grow(world as ServerWorld, world?.random, pos, blockState)\n                world?.syncWorldEvent(2005, pos, 0)\n                itemStack.decrement(1)\n            }\n            block is AirBlock\n                    && item is BlockItem\n                    && (itemStack.isIn(ItemTags.SAPLINGS) || item.block is MushroomPlantBlock || item.block is BambooBlock)\n                    && item.block.defaultState.canPlaceAt(world, pos)\n                    && itemStack.count > 1 -> {\n                if (item.block is BambooBlock)\n                    world?.setBlockState(pos, Blocks.BAMBOO_SAPLING.defaultState, 3)\n                else\n                    world?.setBlockState(pos, item.block.defaultState, 3)\n                itemStack.decrement(1)\n            }\n            else -> return false\n        }\n        return true\n    }\n\n    override fun getEnergyCost(): Long {\n        val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1)\n        return config.energyCost * speedEnhancers\n    }\n\n    override fun getWorkingArea(): Box {\n        val box = Box(pos)\n        return box.expand(range.toDouble(), 0.0, range.toDouble()).stretch(0.0, 40.0, 0.0)\n    }\n    \n    fun getMaxCount(enhancer: Enhancer): Int {\n        return when (enhancer) {\n            Enhancer.SPEED -> return 12\n            Enhancer.BUFFER -> 4\n            else -> 1\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/ChopperBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.WorldRenderer\nimport net.minecraft.client.util.math.MatrixStack\n\nclass ChopperBlockEntityRenderer : AOEMachineBlockEntityRenderer() {\n    override fun render(\n        blockEntity: AOEMachineBlockEntity<*>,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider,\n        light: Int,\n        overlay: Int\n    ) {\n        super.render(blockEntity, tickDelta, matrices, vertexConsumers, light, overlay)\n        if (blockEntity.renderWorkingArea) {\n            val pos = blockEntity.pos\n            val area = blockEntity.getWorkingArea().offset(-pos.x.toDouble(), -pos.y.toDouble(), -pos.z.toDouble()).expand(4.0, 0.0, 4.0)\n            val vc = vertexConsumers.getBuffer(RenderLayer.getLines())\n            WorldRenderer.drawBox(matrices, vc, area, 1f, 0.2f, 0.2f, 1f)\n        }\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/DirtOxygenatorBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blocks.machine.FacingMachineBlock\nimport me.steven.indrev.config.MachineConfig\nimport me.steven.indrev.registry.IRFluidRegistry\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.bucket\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.Fertilizable\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\n\nclass DirtOxygenatorBlockEntity(pos: BlockPos, state: BlockState) : MachineBlockEntity<MachineConfig>(Tier.MK1, MachineRegistry.DIRT_OXYGENATOR_REGISTRY, pos, state) {\n\n    private var streak = 0\n    private var ticksUntilReset = 40\n    val fluidInv = FluidStorage()\n\n\n    override fun machineTick() {\n        val target = pos.offset(cachedState[FacingMachineBlock.FACING]).up()\n        val targetState = world!!.getBlockState(target)\n        val block = targetState.block\n        if (block !is Fertilizable || !targetState.canPlaceAt(world, target)) return\n        if (fluidInv.amount <= 0) {\n            ticksUntilReset--\n            if (ticksUntilReset <= 0)\n                streak = 0\n            return\n        } else {\n            streak++\n            ticksUntilReset = 40\n        }\n        val chance = (streak / 300.0).coerceIn(0.0, 1.0) * 0.6\n        if (world!!.random.nextDouble() < chance) {\n            if (\n                block.isFertilizable(world, pos, targetState, false)\n                && block.canGrow(world, world!!.random, target, targetState)\n            ) {\n                block.grow(world as ServerWorld, world!!.random, target, targetState)\n            }\n        }\n\n        fluidInv.variant = FluidVariant.blank()\n        fluidInv.amount = 0\n    }\n\n    inner class FluidStorage : SingleVariantStorage<FluidVariant>() {\n\n        override fun canExtract(variant: FluidVariant): Boolean = false\n\n        override fun canInsert(variant: FluidVariant): Boolean = variant.isOf(IRFluidRegistry.OXYGEN_STILL)\n\n        override fun getCapacity(variant: FluidVariant): Long = bucket\n\n        override fun getBlankVariant(): FluidVariant = FluidVariant.blank()\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/DrainBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.components.FluidComponent\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.bucket\nimport me.steven.indrev.utils.contains\nimport me.steven.indrev.utils.drainFluid\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.FluidBlock\nimport net.minecraft.block.FluidDrainable\nimport net.minecraft.fluid.FlowableFluid\nimport net.minecraft.fluid.Fluid\nimport net.minecraft.fluid.Fluids\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Box\nimport net.minecraft.util.math.Direction\n\nclass DrainBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : MachineBlockEntity<BasicMachineConfig>(tier, MachineRegistry.DRAIN_REGISTRY, pos, state) {\n\n    init {\n        this.fluidComponent = object : FluidComponent({ this }, bucket) {\n            init {\n                this.outputTanks = intArrayOf(0)\n            }\n        }\n    }\n\n    override val maxInput: Long = config.maxInput\n\n    override fun machineTick() {\n        val world = world ?: return\n        val fluidComponent = fluidComponent ?: return\n        if (ticks % 20 == 0 || !fluidComponent[0].isEmpty) return\n\n        val fluidState = world.getFluidState(pos.up())\n        if (fluidState?.isEmpty == false) {\n            val range = getWorkingArea()\n            // DOWN is intentionally excluded\n            val directions = arrayOf(Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)\n            val mutablePos = pos.mutableCopy()\n            val fluid = getStill(fluidState.fluid)\n            val bfs = mutableListOf(pos.up())\n            var i = 0\n            while (i < bfs.size) {\n                val current = bfs[i++]\n                for (dir in directions) {\n                    mutablePos.set(current, dir)\n                    if (mutablePos !in bfs && mutablePos in range && getStill(world.getFluidState(mutablePos).fluid) === fluid) {\n                        bfs.add(mutablePos.toImmutable())\n                    }\n                }\n            }\n            bfs.sortByDescending { it.y }\n\n            for (pos in bfs) {\n                val blockState = world.getBlockState(pos)\n                val block = blockState?.block\n                if (block is FluidDrainable && block is FluidBlock) {\n                    val drained = block.drainFluid(world, pos, blockState)\n                    if (drained != Fluids.EMPTY) {\n                        fluidComponent[0].insert(FluidVariant.of(drained), bucket, true)\n                        return\n                    }\n                }\n            }\n        }\n    }\n\n    private fun getStill(fluid: Fluid): Fluid = if (fluid is FlowableFluid) fluid.still else fluid\n\n    fun getWorkingArea(): Box = Box(pos.up()).expand(8.0, 0.0, 8.0).stretch(0.0, 4.0, 0.0)\n\n    override fun applyDefault(\n        state: BlockState,\n        type: ConfigurationType,\n        configuration: MutableMap<Direction, TransferMode>\n    ) {\n        Direction.values().forEach { dir -> configuration[dir] = TransferMode.OUTPUT }\n    }\n\n    override fun isFixed(type: ConfigurationType): Boolean = true\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/FarmerBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.map\nimport me.steven.indrev.utils.toVec3d\nimport net.minecraft.block.*\nimport net.minecraft.item.BlockItem\nimport net.minecraft.item.BoneMealItem\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.loot.context.LootContext\nimport net.minecraft.loot.context.LootContextParameters\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Box\nimport net.minecraft.util.math.Direction\n\nclass FarmerBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : AOEMachineBlockEntity<BasicMachineConfig>(tier, MachineRegistry.FARMER_REGISTRY, pos, state) {\n\n    init {\n        this.enhancerComponent = EnhancerComponent(intArrayOf(14, 15, 16, 17), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input { slots = intArrayOf(1, 2, 3, 4) }\n            output { slots = intArrayOf(5, 6, 7, 8, 9, 10, 11, 12, 13) }\n        }\n    }\n\n    override val maxInput: Long = config.maxInput\n    override val maxOutput: Long = 0\n\n    override var range: Int = 5\n\n    var cooldown = 0.0\n    private var nextBlocks = emptyList<BlockPos>().iterator()\n\n    override fun machineTick() {\n        cooldown += getProcessingSpeed()\n        if (cooldown < config.processSpeed) return\n        val world = world as ServerWorld\n        val energyCost = config.energyCost\n        if (!canUse(energyCost)) return\n        if (nextBlocks.hasNext()) {\n            while (nextBlocks.hasNext()) {\n                val pos = nextBlocks.next()\n                val state = world.getBlockState(pos)\n                if (state.block != Blocks.SUGAR_CANE)\n                    tryHarvest(state, pos, world)\n                else {\n                    for (i in 5 downTo 1) {\n                        val posToHarvest = pos.up(i)\n                        val toHarvest = world.getBlockState(posToHarvest)\n                        if (toHarvest.block == Blocks.SUGAR_CANE) {\n                            tryHarvest(state, posToHarvest, world)\n                        }\n                    }\n                }\n            }\n        } else {\n            nextBlocks = getWorkingArea().map(::BlockPos).iterator()\n        }\n        cooldown = 0.0\n    }\n\n    private fun tryHarvest(state: BlockState, pos: BlockPos, world: ServerWorld): Boolean {\n        val block = state.block\n\n        val inventory = inventoryComponent?.inventory\n        val performedAction = inventory?.inputSlots?.any { slot ->\n            val stack = inventory.getStack(slot)\n            val item = stack.item\n            val isCropBlock = block is CropBlock || block is StemBlock || block is SweetBerryBushBlock || block is CocoaBlock || block is NetherWartBlock\n            when {\n                item is BoneMealItem && isCropBlock && (block as? Fertilizable)?.isFertilizable(world, pos, state, false) == true && block.canGrow(world, world.random, pos, state) -> {\n                    stack.decrement(1)\n                    block.grow(world, world.random, pos, state)\n                    world.syncWorldEvent(2005, pos, 0)\n                    true\n                }\n                canHarvest(slot, state, block, item) -> {\n                    if ((block is CropBlock || block is SweetBerryBushBlock) && stack.count > 1) {\n                        world.setBlockState(pos, block.defaultState)\n                        stack.decrement(1)\n                    } else {\n                        world.setBlockState(pos, Blocks.AIR.defaultState)\n                    }\n                    val droppedStacks = state.getDroppedStacks(LootContext.Builder(world)\n                        .random(world.random)\n                        .parameter(LootContextParameters.ORIGIN, pos.toVec3d())\n                        .parameter(LootContextParameters.BLOCK_STATE, state)\n                        .parameter(LootContextParameters.TOOL, ItemStack.EMPTY))\n                    droppedStacks.forEach { inventory.output(it) }\n                    true\n                }\n                block is AirBlock && canPlant(item) && stack.count > 1 -> {\n                    var cropState = (item as BlockItem).block.defaultState\n\n                    if (item.block is CocoaBlock) {\n                        arrayOf(Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST).firstOrNull {\n                            cropState = cropState.with(CocoaBlock.FACING, it)\n                            cropState.canPlaceAt(world, pos)\n                        } ?: return@any false\n                    }\n\n                    if (cropState.canPlaceAt(world, pos) && world.isAir(pos)) {\n                        world.setBlockState(pos, cropState)\n                        stack.count--\n                        true\n                    } else false\n                }\n                else -> false\n            }\n        } ?: false\n        if (performedAction)\n            use(getEnergyCost())\n        return performedAction\n    }\n\n    override fun getEnergyCost(): Long {\n        val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1)\n        return config.energyCost * speedEnhancers\n    }\n\n    private fun canPlant(item: Item) =\n        item is BlockItem && (\n                item.block is CropBlock\n                        || item.block is StemBlock\n                        || item.block == Blocks.SUGAR_CANE\n                        || item.block is SweetBerryBushBlock\n                        || item.block is CocoaBlock\n                        || item.block is NetherWartBlock\n                )\n\n    private fun canHarvest(slot: Int, state: BlockState, block: Block, item: Item): Boolean =\n        (((block is CropBlock && block.isMature(state))\n                || (block is SweetBerryBushBlock && state[SweetBerryBushBlock.AGE] == 2))\n                && (item is BlockItem && item.block == block || slot == 4))\n                || block is GourdBlock\n                || block == Blocks.SUGAR_CANE\n                || (block is CocoaBlock && state[CocoaBlock.AGE] == 2)\n                || (block is NetherWartBlock && state[NetherWartBlock.AGE] == 3)\n\n    override fun getWorkingArea(): Box = Box(pos).expand(range.toDouble(), 0.0, range.toDouble())\n\n    fun getMaxCount(enhancer: Enhancer): Int {\n        return when (enhancer) {\n            Enhancer.SPEED -> return 1\n            Enhancer.BUFFER -> 4\n            else -> 1\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/FisherBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport me.steven.indrev.utils.toVec3d\nimport net.minecraft.block.BlockState\nimport net.minecraft.item.FishingRodItem\nimport net.minecraft.loot.context.LootContext\nimport net.minecraft.loot.context.LootContextParameters\nimport net.minecraft.loot.context.LootContextTypes\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\n\nclass FisherBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : MachineBlockEntity<BasicMachineConfig>(tier, MachineRegistry.FISHER_REGISTRY, pos, state) {\n\n    init {\n        this.enhancerComponent = EnhancerComponent(intArrayOf(6, 7, 8, 9), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input {\n                slot = 1\n                filter { (_, item), _ -> item is FishingRodItem }\n            }\n            output { slots = intArrayOf(2, 3, 4, 5) }\n        }\n    }\n\n    private var cooldown = config.processSpeed\n    \n    override val maxInput: Long = config.maxInput\n    override val maxOutput: Long = 0\n\n    override fun machineTick() {\n        if (!canUse(getEnergyCost())) return\n        val rodStack = inventoryComponent!!.inventory.getStack(1)\n        if (rodStack.isEmpty || rodStack.item !is FishingRodItem || !use(getEnergyCost())) return\n        cooldown += getProcessingSpeed()\n        if (cooldown < config.processSpeed) return\n        cooldown = 0.0\n        Direction.values().forEach { direction ->\n            val pos = pos.offset(direction)\n            if (world?.isWater(pos) == true) {\n                val identifiers = getIdentifiers(tier)\n                val id = identifiers[world!!.random!!.nextInt(identifiers.size)]\n                val lootTable = (world as ServerWorld).server.lootManager.getTable(id)\n                val ctx = LootContext.Builder(world as ServerWorld).random(world!!.random)\n                    .parameter(LootContextParameters.ORIGIN, pos.toVec3d())\n                    .parameter(LootContextParameters.TOOL, rodStack)\n                    .build(LootContextTypes.FISHING)\n                val loot = lootTable.generateLoot(ctx)\n                loot.forEach { stack -> inventoryComponent?.inventory?.output(stack) }\n                rodStack?.apply {\n                    damage++\n                    if (damage >= maxDamage) decrement(1)\n                }\n            }\n        }\n    }\n\n    override fun getEnergyCost(): Long {\n        val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1)\n        return config.energyCost * speedEnhancers\n    }\n\n    private fun getIdentifiers(tier: Tier) = when (tier) {\n        Tier.MK2 -> arrayOf(FISH_IDENTIFIER)\n        Tier.MK3 -> arrayOf(FISH_IDENTIFIER, FISH_IDENTIFIER, JUNK_IDENTIFIER, JUNK_IDENTIFIER, TREASURE_IDENTIFIER)\n        else -> arrayOf(FISH_IDENTIFIER, FISH_IDENTIFIER, FISH_IDENTIFIER, TREASURE_IDENTIFIER)\n    }\n\n    fun getMaxCount(enhancer: Enhancer): Int {\n        return when (enhancer) {\n            Enhancer.SPEED, Enhancer.BUFFER -> 4\n            else -> 1\n        }\n    }\n\n    companion object {\n        private val FISH_IDENTIFIER = Identifier(\"gameplay/fishing/fish\")\n        private val JUNK_IDENTIFIER = Identifier(\"gameplay/fishing/junk\")\n        private val TREASURE_IDENTIFIER = Identifier(\"gameplay/fishing/treasure\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/PumpBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue\nimport it.unimi.dsi.fastutil.longs.LongOpenHashSet\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport me.steven.indrev.components.*\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.bucket\nimport me.steven.indrev.utils.drainFluid\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.FluidBlock\nimport net.minecraft.block.FluidDrainable\nimport net.minecraft.fluid.FlowableFluid\nimport net.minecraft.fluid.Fluid\nimport net.minecraft.fluid.FluidState\nimport net.minecraft.fluid.Fluids\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport kotlin.math.floor\nimport kotlin.math.roundToInt\n\nclass PumpBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : MachineBlockEntity<BasicMachineConfig>(tier, MachineRegistry.PUMP_REGISTRY, pos, state) {\n\n    init {\n        this.fluidComponent = FluidComponent({ this }, bucket)\n        this.enhancerComponent = object : EnhancerComponent(intArrayOf(0, 1, 2, 3), Enhancer.DEFAULT, this::getMaxCount) {\n            override fun isLocked(slot: Int, tier: Tier): Boolean = false\n        }\n        this.inventoryComponent = inventory(this) {\n        }\n\n        trackObject(TANK_ID, fluidComponent!![0])\n        trackDouble(SPEED_ID) { config.processSpeed  - enhancerComponent!!.getCount(Enhancer.SPEED) * 10 }\n    }\n\n    override val syncToWorld: Boolean = true\n\n    override val maxInput: Long = config.maxInput\n    override val maxOutput: Long = 0\n\n    var pipePosition = 0.0\n    private var isSearchingDown = false\n\n    private var currentTarget: BlockPos = pos\n\n    private val scanned = LongOpenHashSet()\n    private var queue = LongArrayFIFOQueue()\n    private var cooldown = 20.0\n\n    override fun machineTick() {\n        if (pipePosition == 0.0) {\n            isSearchingDown = true\n        }\n\n        if (!isSearchingDown && currentTarget == pos) {\n            searchFluid()\n        }\n\n        if (queue.isEmpty) {\n            isSearchingDown = true\n        }\n\n        if (currentTarget != pos) {\n            cooldown--\n            if (cooldown <= 0) {\n                drainTargetFluid()\n            }\n        }\n\n        if (isSearchingDown) {\n            searchDown()\n        }\n    }\n\n    private fun searchFluid() {\n        val currentLevel = floor(pipePosition).toInt()\n        val lookLevel = pos.offset(Direction.DOWN, currentLevel)\n        val currentFluid = world!!.getFluidState(lookLevel)\n        val targetFluid = world!!.getFluidState(currentTarget)\n\n        if (currentFluid.isEmpty) {\n            isSearchingDown = true\n            return\n        }\n\n        if ((targetFluid.isEmpty || !targetFluid.isStill) && !currentFluid.isEmpty) {\n            scanned.clear()\n            val searching = BlockPos.Mutable()\n            val neighbor = BlockPos.Mutable()\n            while (!queue.isEmpty) {\n                val packedPos = queue.dequeue()\n                searching.set(BlockPos.unpackLongX(packedPos), BlockPos.unpackLongY(packedPos), BlockPos.unpackLongZ(packedPos))\n                SEARCH_DIRECTIONS.forEach { dir ->\n                    neighbor.set(searching.x + dir.offsetX, searching.y + dir.offsetY, searching.z + dir.offsetZ)\n                    if (neighbor.getSquaredDistance(lookLevel) < IRConfig.machines.pumpMaxRange * IRConfig.machines.pumpMaxRange\n                        && scanned.add(neighbor.asLong())\n                        && getStill(world!!.getFluidState(neighbor).fluid) == getStill(currentFluid.fluid)) {\n                        queue.enqueue(neighbor.asLong())\n                    }\n                }\n                val fluidState = world!!.getFluidState(searching)\n                if (fluidState.isStill && !fluidState.isEmpty && fluidState.fluid == getStill(currentFluid.fluid)) {\n                    currentTarget = searching\n                    break\n                }\n            }\n        }\n    }\n\n    private fun getFluidTouchingPipe(): FluidState {\n        val currentLevel = floor(pipePosition).toInt()\n        val lookLevel = pos.offset(Direction.DOWN, currentLevel)\n        return world!!.getFluidState(lookLevel)\n    }\n\n    private fun drainTargetFluid() {\n        val fluidToPump = world!!.getFluidState(currentTarget)\n        if (!fluidToPump.isEmpty && canUse(config.energyCost) && fluidComponent!![0].isEmpty) {\n            val blockState = world!!.getBlockState(currentTarget)\n            val block = blockState?.block\n            if (block is FluidDrainable && block is FluidBlock) {\n                val drained = block.drainFluid(world!!, currentTarget, blockState)\n                if (drained != Fluids.EMPTY) {\n                    fluidComponent!![0].insert(FluidVariant.of(drained), bucket, true)\n                    use(getEnergyCost())\n                    cooldown = config.processSpeed - enhancerComponent!!.getCount(Enhancer.SPEED) * 10\n                }\n                currentTarget = pos\n            }\n        }\n    }\n\n    override fun getEnergyCost(): Long {\n        return config.energyCost * (enhancerComponent!!.getCount(Enhancer.SPEED) + 1)\n    }\n\n    private fun searchDown() {\n        if (use(2)) {\n            pipePosition += 0.01\n            sync()\n            val fluid = getFluidTouchingPipe()\n            if (!fluid.isEmpty) {\n                isSearchingDown = false\n                pipePosition = pipePosition.roundToInt().toDouble()\n                queue = LongArrayFIFOQueue()\n                val currentLevel = floor(pipePosition).toInt()\n                queue.enqueue(pos.offset(Direction.DOWN, currentLevel).asLong())\n            }\n        }\n    }\n\n    private fun getStill(fluid: Fluid): Fluid = if (fluid is FlowableFluid) fluid.still else fluid\n\n    override fun applyDefault(\n        state: BlockState,\n        type: ConfigurationType,\n        configuration: MutableMap<Direction, TransferMode>\n    ) {\n        assert(type == ConfigurationType.FLUID)\n        val facing = state[HorizontalFacingMachineBlock.HORIZONTAL_FACING]\n        configuration[facing] = TransferMode.OUTPUT\n    }\n\n    override fun isFixed(type: ConfigurationType): Boolean = true\n    fun getMaxCount(enhancer: Enhancer): Int {\n        return when (enhancer) {\n            Enhancer.SPEED -> return 1\n            Enhancer.BUFFER -> 4\n            else -> 1\n        }\n    }\n\n\n    override fun toTag(tag: NbtCompound) {\n        tag.putDouble(\"MovingTicks\", pipePosition)\n        super.toTag(tag)\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        pipePosition = tag.getDouble(\"MovingTicks\")\n        super.fromTag(tag)\n    }\n\n    override fun fromClientTag(tag: NbtCompound) {\n        pipePosition = tag.getDouble(\"MovingTicks\")\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        tag.putDouble(\"MovingTicks\", pipePosition)\n    }\n\n    companion object {\n        const val TANK_ID = 2\n        const val SPEED_ID = 3\n        val SEARCH_DIRECTIONS = mutableListOf(Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/PumpBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport alexiil.mc.lib.attributes.fluid.render.FluidRenderFace\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport me.steven.indrev.utils.IRFluidTank\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.OverlayTexture\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.WorldRenderer\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.util.ModelIdentifier\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.math.Vec3f\nimport kotlin.math.floor\n\nclass PumpBlockEntityRenderer : BlockEntityRenderer<PumpBlockEntity> {\n    override fun render(\n        entity: PumpBlockEntity,\n        tickDelta: Float,\n        matrices: MatrixStack,\n        vertexConsumers: VertexConsumerProvider,\n        light: Int,\n        overlay: Int\n    ) {\n        matrices.run {\n\n            push()\n            val inputVolume = entity.fluidComponent!![0]\n            if (!inputVolume.isEmpty) {\n\n                translate(0.5, 0.5, 0.5)\n                var direction = entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING]\n                if (direction.axis == Direction.Axis.X) direction = direction.opposite\n                multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(direction.asRotation()))\n                translate(-0.5, -0.5, -0.5)\n                push()\n                multiply(Vec3f.NEGATIVE_X.getDegreesQuaternion(22.5f))\n                renderFluid(inputVolume, vertexConsumers)\n                pop()\n\n                push()\n                multiply(Vec3f.NEGATIVE_Z.getDegreesQuaternion(22.5f))\n                translate(0.5, 0.5, 0.5)\n                multiply(Vec3f.NEGATIVE_Y.getDegreesQuaternion(90f))\n                translate(-0.5, -0.5, -0.5)\n                matrices.translate(0.0, 0.38, 0.0765)\n                renderFluid(inputVolume, vertexConsumers)\n                pop()\n\n                push()\n                multiply(Vec3f.POSITIVE_Z.getDegreesQuaternion(22.5f))\n                translate(0.5, 0.5, 0.5)\n                multiply(Vec3f.NEGATIVE_Y.getDegreesQuaternion(180f))\n                translate(-0.5, -0.5, -0.5)\n                matrices.translate(0.1284, 0.0, 0.1285)\n                renderFluid(inputVolume, vertexConsumers)\n                pop()\n            }\n            pop()\n            val currentY = floor(entity.pipePosition).toInt()\n            for (y in 1..currentY) {\n                push()\n                translate(0.0, -y.toDouble(), 0.0)\n                renderModel(vertexConsumers, entity)\n                pop()\n            }\n            if (currentY.toDouble() != entity.pipePosition) {\n                push()\n                scale(1.01f, 1f, 1.01f)\n                translate(-0.005, -entity.pipePosition, -0.005)\n                renderModel(vertexConsumers, entity)\n                pop()\n            }\n        }\n    }\n\n    private fun MatrixStack.renderModel(vertexConsumers: VertexConsumerProvider, entity: PumpBlockEntity) {\n        val model = MinecraftClient.getInstance().bakedModelManager.getModel(ModelIdentifier(identifier(\"pump_pipe\"), \"\"))\n        val buffer = vertexConsumers.getBuffer(RenderLayer.getSolid())\n        val light = WorldRenderer.getLightmapCoordinates(entity.world, entity.pos)\n        MinecraftClient.getInstance().blockRenderManager.modelRenderer.render(\n            peek(), buffer, null, model, -1f, -1f, -1f, light, OverlayTexture.DEFAULT_UV\n        )\n    }\n\n    private fun MatrixStack.renderFluid(inputVolume: IRFluidTank, vcp: VertexConsumerProvider) {\n\n        inputVolume.render(FACES, vcp, this)\n    }\n\n    override fun rendersOutsideBoundingBox(blockEntity: PumpBlockEntity?): Boolean = true\n\n    companion object {\n        val FACES = listOf(\n            FluidRenderFace.createFlatFaceZ(0.443, 0.15, 0.32, 0.556, 0.31, 0.32, 2.0, false, false),\n            FluidRenderFace.createFlatFaceZ(0.443, 0.15, 0.32+ (0.55-0.443), 0.556, 0.31, 0.32+ (0.55-0.443), 2.0, true, false),\n            FluidRenderFace.createFlatFaceX(0.443, 0.15, 0.323, 0.443, 0.31, 0.426, 2.0, false, false),\n            FluidRenderFace.createFlatFaceX(0.443+ (0.55-0.443), 0.15, 0.323, 0.443+ (0.55-0.443), 0.31, 0.426, 2.0, true, false)\n        )\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/RancherBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.components.autosync\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.redirectDrops\nimport net.minecraft.block.BlockState\nimport net.minecraft.entity.damage.DamageSource\nimport net.minecraft.entity.passive.AnimalEntity\nimport net.minecraft.item.ItemStack\nimport net.minecraft.item.SwordItem\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Hand\nimport net.minecraft.util.math.BlockPos\n\nclass RancherBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : AOEMachineBlockEntity<BasicMachineConfig>(tier, MachineRegistry.RANCHER_REGISTRY, pos, state) {\n\n    init {\n        this.enhancerComponent = EnhancerComponent(intArrayOf(15, 16, 17, 18), Enhancer.DEFAULT, this::getMaxCount)\n        this.inventoryComponent = inventory(this) {\n            input { slots = intArrayOf(2, 3, 4, 5) }\n            output { slots = intArrayOf(6, 7, 8, 9, 10, 11, 12, 13, 14) }\n            coolerSlot = 1\n        }\n    }\n\n    override val maxInput: Long = config.maxInput\n    override val maxOutput: Long = 0\n\n    var cooldown = 0.0\n    override var range = 5\n    private val fakePlayer by lazy { IndustrialRevolution.FAKE_PLAYER_BUILDER.create(world!!.server, world as ServerWorld, \"rancher\") }\n    var feedBabies: Boolean by autosync(FEED_BABIES_ID, true)\n    var mateAdults: Boolean by autosync(MATE_ADULTS, true)\n    var matingLimit: Int by autosync(MATING_LIMIT, 16)\n    var killAfter: Int by autosync(KILL_AFTER, 8)\n\n    override fun machineTick() {\n        if (world?.isClient == true) return\n        val inventory = inventoryComponent?.inventory ?: return\n        cooldown += getProcessingSpeed()\n        if (cooldown < config.processSpeed) return\n        val animals = world?.getEntitiesByClass(AnimalEntity::class.java, getWorkingArea()) { true } ?: emptyList()\n        if (animals.isEmpty() || !canUse(getEnergyCost())) {\n            workingState = false\n            return\n        } else workingState = true\n        val swordStack = inventory.inputSlots.map { inventory.getStack(it) }.firstOrNull { it.item is SwordItem }\n        fakePlayer.inventory.selectedSlot = 0\n        if (swordStack != null && !swordStack.isEmpty && swordStack.damage < swordStack.maxDamage) {\n            val swordItem = swordStack.item as SwordItem\n            val kill = filterAnimalsToKill(animals)\n            if (kill.isNotEmpty()) use(getEnergyCost())\n            kill.forEach { animal ->\n                animal.redirectDrops(inventory) {\n                    if (!animal.isAlive || !animal.damage(DamageSource.player(fakePlayer), swordItem.attackDamage)) return@forEach\n                    swordStack.damage(1, world?.random, null)\n                    if (swordStack.damage >= swordStack.maxDamage) swordStack.decrement(1)\n                }\n            }\n        }\n        for (animal in animals) {\n            inventory.inputSlots.forEach { slot ->\n                val stack = inventory.getStack(slot).copy()\n                animal.redirectDrops(inventory) {\n                    if (tryFeed(animals.size, animal, inventory.getStack(slot)).isAccepted) return@forEach\n                    fakePlayer.inventory.selectedSlot = 8\n                    fakePlayer.setStackInHand(Hand.MAIN_HAND, stack)\n                    if (animal.interactMob(fakePlayer, Hand.MAIN_HAND).isAccepted)\n                        use(getEnergyCost())\n                    val inserted = inventory.output(fakePlayer.inventory.getStack(0))\n                    val handStack = fakePlayer.getStackInHand(Hand.MAIN_HAND)\n                    if (!handStack.isEmpty && handStack.item != stack.item) {\n                        inventory.output(handStack)\n                        fakePlayer.setStackInHand(Hand.MAIN_HAND, ItemStack.EMPTY)\n                    }\n                    if (inserted)\n                        inventory.setStack(slot, stack)\n                    fakePlayer.inventory.clear()\n                }\n            }\n        }\n        fakePlayer.inventory.clear()\n        cooldown = 0.0\n    }\n\n    private fun tryFeed(size: Int, animalEntity: AnimalEntity, stack: ItemStack): ActionResult {\n        if (animalEntity.isBreedingItem(stack)) {\n            val breedingAge: Int = animalEntity.breedingAge\n            if (!world!!.isClient && breedingAge == 0 && animalEntity.canEat() && size <= matingLimit && mateAdults) {\n                animalEntity.eat(fakePlayer, Hand.MAIN_HAND, stack)\n                animalEntity.lovePlayer(fakePlayer)\n            }\n            if (animalEntity.isBaby && feedBabies) {\n                animalEntity.eat(fakePlayer, Hand.MAIN_HAND, stack)\n                animalEntity.growUp(((-breedingAge / 20f) * 0.1f).toInt(), true)\n            }\n            return ActionResult.SUCCESS\n        }\n        return ActionResult.PASS\n    }\n\n    private fun filterAnimalsToKill(entities: List<AnimalEntity>): List<AnimalEntity> {\n        val adults = entities.filter { !it.isBaby }\n        val types = adults.map { it.type }.associateWith { mutableListOf<AnimalEntity>() }\n        adults.forEach { types[it.type]?.add(it) }\n        return types.values.let { values ->\n            values.map { animals -> animals.dropLast((animals.size - killAfter).coerceAtLeast(killAfter)) }\n        }.flatten()\n    }\n\n    override fun getEnergyCost(): Long {\n        val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1)\n        return config.energyCost * speedEnhancers\n    }\n\n    fun getMaxCount(enhancer: Enhancer): Int {\n        return when (enhancer) {\n            Enhancer.SPEED -> return 1 \n            Enhancer.BUFFER -> 4\n            else -> 1\n        }\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        super.toTag(tag)\n        tag.putBoolean(\"feedBabies\", feedBabies)\n        tag.putBoolean(\"mateAdults\", mateAdults)\n        tag.putInt(\"matingLimit\", matingLimit)\n        tag.putInt(\"killAfter\", killAfter)\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        super.fromTag(tag)\n        feedBabies = tag.getBoolean(\"feedBabies\")\n        mateAdults = tag.getBoolean(\"mateAdults\")\n        matingLimit = tag.getInt(\"matingLimit\")\n        killAfter = tag.getInt(\"killAfter\")\n    }\n\n    companion object {\n        const val FEED_BABIES_ID = 2\n        const val MATE_ADULTS = 3\n        const val MATING_LIMIT = 4\n        const val KILL_AFTER = 5\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/farms/SlaughterBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.farms\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.redirectDrops\nimport net.minecraft.block.BlockState\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.entity.boss.WitherEntity\nimport net.minecraft.entity.damage.DamageSource\nimport net.minecraft.entity.decoration.ArmorStandEntity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.SwordItem\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\n\nclass SlaughterBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : AOEMachineBlockEntity<BasicMachineConfig>(tier, MachineRegistry.SLAUGHTER_REGISTRY, pos, state) {\n\n    init {\n        this.enhancerComponent = EnhancerComponent(\n            intArrayOf(11, 12, 13, 14),\n            arrayOf(Enhancer.SPEED, Enhancer.BUFFER, Enhancer.DAMAGE),\n            this::getMaxCount\n        )\n        this.inventoryComponent = inventory(this) {\n            input { slot = 1 }\n            output { slots = intArrayOf(2, 3, 4, 5, 6, 7, 8, 9, 10) }\n        }\n    }\n\n    override val maxInput: Long = config.maxInput\n    override val maxOutput: Long = 0\n\n    var cooldown = 0.0\n    override var range = 5\n    private val fakePlayer by lazy { IndustrialRevolution.FAKE_PLAYER_BUILDER.create(world!!.server, world as ServerWorld, \"slaughter\") }\n\n    override fun machineTick() {\n        if (world?.isClient == true) return\n        val inventory = inventoryComponent?.inventory ?: return\n        val enhancers = enhancerComponent!!.enhancers\n        cooldown += getProcessingSpeed()\n        if (cooldown < config.processSpeed) return\n        val source = DamageSource.player(fakePlayer)\n        val mobs = world?.getEntitiesByClass(LivingEntity::class.java, getWorkingArea()) { e -> e !is PlayerEntity && e !is ArmorStandEntity && !e.isDead && !e.isInvulnerableTo(source) && (e !is WitherEntity || e.invulnerableTimer <= 0) } ?: emptyList()\n        if (mobs.isEmpty() || !canUse(getEnergyCost())) {\n            workingState = false\n            return\n        } else workingState = true\n        val swordStack = inventory.inputSlots.map { inventory.getStack(it) }.firstOrNull { it.item is SwordItem }\n        fakePlayer.inventory.selectedSlot = 0\n        if (swordStack != null && !swordStack.isEmpty && swordStack.damage < swordStack.maxDamage) {\n            val swordItem = swordStack.item as SwordItem\n            use(getEnergyCost())\n            mobs.forEach { mob ->\n                swordStack.damage(1, world?.random, null)\n                if (swordStack.damage >= swordStack.maxDamage) swordStack.decrement(1)\n\n                if (mob.isAlive) {\n                    mob.redirectDrops(inventory) {\n                        mob.damage(source, (swordItem.attackDamage * Enhancer.getDamageMultiplier(enhancers)).toFloat())\n                    }\n                }\n            }\n        }\n        fakePlayer.inventory.clear()\n        cooldown = 0.0\n    }\n\n    override fun getEnergyCost(): Long {\n        val speedEnhancers = (enhancerComponent!!.getCount(Enhancer.SPEED) * 2).coerceAtLeast(1)\n        val dmgEnhancers = (enhancerComponent!!.getCount(Enhancer.DAMAGE) * 8).coerceAtLeast(1)\n        return config.energyCost * speedEnhancers * dmgEnhancers\n    }\n\n    fun getMaxCount(enhancer: Enhancer): Int {\n        return when (enhancer) {\n            Enhancer.SPEED, Enhancer.DAMAGE -> return 1\n            Enhancer.BUFFER -> 4\n            else -> 1\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/generators/BiomassGeneratorBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.generators\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.item.Item\nimport net.minecraft.util.math.BlockPos\n\nclass BiomassGeneratorBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : SolidFuelGeneratorBlockEntity(tier, MachineRegistry.BIOMASS_GENERATOR_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.08, 900..2000, 2500)\n        this.inventoryComponent = inventory(this) {\n            input { slot = 2 }\n        }\n    }\n\n    override fun getFuelMap(): Map<Item, Int> = BURN_TIME_MAP\n\n    companion object {\n        private val BURN_TIME_MAP = hashMapOf<Item, Int>()\n\n        init {\n            BURN_TIME_MAP[IRItemRegistry.BIOMASS] = 150\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/generators/CoalGeneratorBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.generators\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.AbstractFurnaceBlockEntity\nimport net.minecraft.item.Item\nimport net.minecraft.util.math.BlockPos\n\nclass CoalGeneratorBlockEntity(pos: BlockPos, state: BlockState) :\n    SolidFuelGeneratorBlockEntity(Tier.MK1, MachineRegistry.COAL_GENERATOR_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.08, 900..2000, 2500)\n        this.inventoryComponent = inventory(this) {\n            input {\n                slot = 2\n                2 filter { stack -> BURN_TIME_MAP.containsKey(stack.item) }\n            }\n        }\n    }\n\n    override fun getFuelMap(): Map<Item, Int> = BURN_TIME_MAP\n\n    companion object {\n        private val BURN_TIME_MAP = AbstractFurnaceBlockEntity.createFuelTimeMap()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/generators/GasBurningGeneratorBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.generators\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.components.FluidComponent\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.components.autosync\nimport me.steven.indrev.components.trackObject\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.registry.IRFluidFuelRegistry\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.bucket\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.minecraft.block.BlockState\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.math.BlockPos\n\nclass GasBurningGeneratorBlockEntity(pos: BlockPos, state: BlockState) : GeneratorBlockEntity(Tier.MK4, MachineRegistry.GAS_BURNING_GENERATOR_REGISTRY, pos, state) {\n\n    private var burnTime by autosync(BURN_TIME_ID, 0)\n    private var maxBurnTime by autosync(TOTAL_BURN_TIME_ID, 0)\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.3, 2200..2400, 2500)\n        this.inventoryComponent = inventory(this) {\n            coolerSlot = 0\n            output {\n                slot = 1\n            }\n        }\n        this.fluidComponent = GasBurningGeneratorFluidComponent()\n    }\n\n    var generatingTicks = 0\n\n    override fun machineTick() {\n        super.machineTick()\n        if (workingState) {\n            generatingTicks++\n\n            if (generatingTicks % 100 == 0 && world!!.random.nextDouble() < 0.6) {\n                inventoryComponent!!.inventory.output(ItemStack(IRItemRegistry.SOOT))\n            }\n        }\n    }\n\n    override fun shouldGenerate(): Boolean {\n        if (burnTime > 0) burnTime--\n        else if (energyCapacity > energy) {\n            val invFluid = fluidComponent!![0]\n            val fluid = invFluid.resource.fluid\n            if (invFluid.isEmpty || !IRFluidFuelRegistry.isFuel(fluid)) return false\n            val fuel = IRFluidFuelRegistry.get(fluid)!!\n            if (fluidComponent!![0].extract(fuel.consumptionRatio) == fuel.consumptionRatio) {\n                burnTime = fuel.burnTime\n                maxBurnTime = burnTime\n            }\n        }\n\n        return burnTime > 0 && energy < energyCapacity\n    }\n\n    override fun getGenerationRatio(): Long {\n        val invFluid = fluidComponent!![0]\n        val fluid = invFluid.resource.fluid\n        val modifier = if (temperatureComponent!!.isFullEfficiency()) config.temperatureBoost else 1.0\n        return ((IRFluidFuelRegistry.get(fluid)?.generationRatio ?: 0) * modifier).toLong()\n    }\n\n    override fun getValidConfigurations(type: ConfigurationType): Array<TransferMode> {\n        return when (type) {\n            ConfigurationType.ITEM -> arrayOf(TransferMode.OUTPUT, TransferMode.NONE)\n            ConfigurationType.FLUID -> arrayOf(TransferMode.INPUT, TransferMode.NONE)\n            else -> super.getValidConfigurations(type)\n        }\n    }\n\n    inner class GasBurningGeneratorFluidComponent : FluidComponent({ this }, bucket * 2, 1) {\n\n        init {\n            trackObject(TANK_ID, this[0])\n        }\n\n        override fun isFluidValidForTank(index: Int, variant: FluidVariant): Boolean {\n            return IRFluidFuelRegistry.isFuel(variant.fluid)\n        }\n    }\n\n    companion object {\n        const val BURN_TIME_ID = 4\n        const val TOTAL_BURN_TIME_ID = 5\n        const val TANK_ID = 6\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/generators/GeneratorBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.generators\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.config.GeneratorConfig\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.math.BlockPos\n\nabstract class GeneratorBlockEntity(tier: Tier, registry: MachineRegistry, pos: BlockPos, state: BlockState) :\n    MachineBlockEntity<GeneratorConfig>(tier, registry, pos, state) {\n\n    override fun machineTick() {\n        if (world?.isClient == false) {\n            val ratio = getGenerationRatio()\n            if (shouldGenerate() && energyCapacity > energy + ratio) {\n                this.energy += ratio\n                this.temperatureComponent?.tick(true)\n                workingState = true\n            } else {\n                workingState = false\n                this.temperatureComponent?.tick(false)\n            }\n        }\n    }\n\n    override val maxInput: Long = 0\n    override val maxOutput: Long = config.maxOutput\n\n    abstract fun shouldGenerate(): Boolean\n\n    open fun getGenerationRatio(): Long = (config.ratio * if (this.temperatureComponent?.isFullEfficiency() == true) config.temperatureBoost else 1.0).toLong()\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/generators/HeatGeneratorBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.generators\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.*\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.bucket\nimport net.minecraft.block.BlockState\nimport net.minecraft.fluid.Fluids\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\n\nclass HeatGeneratorBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : GeneratorBlockEntity(tier, MachineRegistry.HEAT_GENERATOR_REGISTRY, pos, state) {\n\n    private var burnTime by autosync(GasBurningGeneratorBlockEntity.BURN_TIME_ID, 0)\n    private var maxBurnTime by autosync(GasBurningGeneratorBlockEntity.TOTAL_BURN_TIME_ID, 0)\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.8, 7000..9000, 10000)\n        this.inventoryComponent = inventory(this) {\n            input { slot = 2 }\n        }\n        this.fluidComponent = FluidComponent({ this }, bucket * 4)\n        fluidComponent!!.inputTanks = intArrayOf(0)\n\n        trackObject(TANK_ID, fluidComponent!![0])\n\n        trackLong(GENERATION_RATIO_ID) { getGenerationRatio() }\n        trackLong(CONSUMPTION_RATIO_ID) { getConsumptionRate() }\n    }\n\n    override val syncToWorld: Boolean = true\n\n    override fun shouldGenerate(): Boolean {\n        if (burnTime > 0) burnTime--\n        else if (energyCapacity > energy) {\n            val tank = fluidComponent!![0]\n            val consume = getConsumptionRate()\n            if (tank.variant.isOf(Fluids.LAVA)\n                && tank.tryExtract(consume)) {\n                burnTime = 10\n                maxBurnTime = burnTime\n                tank.extract(consume, true)\n            }\n            markDirty()\n        }\n        return burnTime > 0 && energy < energyCapacity\n    }\n\n    override fun getGenerationRatio(): Long {\n        return (config.ratio * (temperatureComponent!!.temperature / temperatureComponent!!.optimalRange.first).coerceAtMost(1.0)).toLong()\n    }\n\n    fun getConsumptionRate(temperature: Double = temperatureComponent!!.temperature): Long {\n        return ((temperature / temperatureComponent!!.optimalRange.first).coerceIn(0.001, 1.0) * 810).toLong()\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        super.fromTag(tag)\n        burnTime = tag.getInt(\"BurnTime\")\n        maxBurnTime = tag.getInt(\"MaxBurnTime\")\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        tag.putInt(\"BurnTime\", burnTime)\n        tag.putInt(\"MaxBurnTime\", maxBurnTime)\n        super.toTag(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        fluidComponent!!.toTag(tag)\n    }\n\n    override fun fromClientTag(tag: NbtCompound) {\n        fluidComponent!!.fromTag(tag)\n    }\n\n    companion object {\n        const val TANK_ID = 4\n        const val GENERATION_RATIO_ID = 5\n        const val CONSUMPTION_RATIO_ID = 6\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/generators/HeatGeneratorBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.generators\n\nimport alexiil.mc.lib.attributes.fluid.render.FluidRenderFace\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport me.steven.indrev.utils.IRFluidTank\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.math.Vec3f\n\nclass HeatGeneratorBlockEntityRenderer : BlockEntityRenderer<HeatGeneratorBlockEntity> {\n    override fun render(\n        entity: HeatGeneratorBlockEntity?,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider,\n        light: Int,\n        overlay: Int\n    ) {\n        val volume = entity?.fluidComponent?.get(0) ?: return\n        if (!volume.isEmpty) {\n            matrices?.run {\n                push()\n                val direction = entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING]\n                    .let { if (it.axis == Direction.Axis.X) it.opposite else it }\n                translate(0.5, 0.5, 0.5)\n                multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(direction.asRotation()))\n                translate(-0.5, -0.5, -0.5)\n                matrices.renderFluid(volume, vertexConsumers)\n                pop()\n            }\n        }\n    }\n\n    private fun MatrixStack.renderFluid(inputVolume: IRFluidTank, vcp: VertexConsumerProvider) {\n        val yMax = (((inputVolume.amount / (4f*81000)) * 10.0) / 16.0).coerceAtLeast(0.1)\n        val face =\n            listOf(\n                FluidRenderFace.createFlatFaceZ(0.01, 0.1, 0.99, 0.99, yMax, 0.99, 2.0, true, false),\n                FluidRenderFace.createFlatFaceY(0.01, yMax, 0.8, 0.99, yMax, 0.99, 2.0, true, false),\n                FluidRenderFace.createFlatFaceX(0.01, 0.1, 0.8, 0.01, yMax, 0.99, 2.0, false, false),\n                FluidRenderFace.createFlatFaceX(0.99, 0.1, 0.8, 0.99, yMax, 0.99, 2.0, true, false)\n            )\n        inputVolume.render(face, vcp, this)\n    }\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/generators/SolarGeneratorBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.generators\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.TemperatureComponent\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.math.BlockPos\n\nclass SolarGeneratorBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    GeneratorBlockEntity(tier, MachineRegistry.SOLAR_GENERATOR_REGISTRY, pos, state) {\n\n    init {\n        this.temperatureComponent = TemperatureComponent(this, 0.1, 500..700, 1000)\n        this.inventoryComponent = inventory(this) {}\n\n    }\n\n    override fun shouldGenerate(): Boolean = this.world?.isSkyVisible(pos.up()) == true && this.world?.isDay == true && energy < energyCapacity\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/generators/SolidFuelGeneratorBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.generators\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.blocks.machine.MachineBlock\nimport me.steven.indrev.components.autosync\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\n\nabstract class SolidFuelGeneratorBlockEntity(tier: Tier, registry: MachineRegistry, pos: BlockPos, state: BlockState)\n    : GeneratorBlockEntity(tier, registry, pos, state) {\n\n    private var burnTime by autosync(BURN_TIME_ID, 0)\n    private var maxBurnTime by autosync(TOTAL_BURN_TIME_ID, 0)\n\n    override fun shouldGenerate(): Boolean {\n        if (burnTime > 0) burnTime--\n        else if (energyCapacity > energy) {\n            val inventory = inventoryComponent?.inventory ?: return false\n            val invStack = inventory.getStack(2)\n            val item = invStack.item\n            if (!invStack.isEmpty && getFuelMap().containsKey(invStack.item)) {\n                burnTime = getFuelMap()[invStack.item] ?: return false\n                maxBurnTime = burnTime\n                invStack.decrement(1)\n                if (item.hasRecipeRemainder())\n                    inventory.setStack(2, ItemStack(item.recipeRemainder))\n            }\n            markDirty()\n        }\n        return burnTime > 0 && energy < energyCapacity\n    }\n\n    override fun applyDefault(\n        state: BlockState,\n        type: ConfigurationType,\n        configuration: MutableMap<Direction, TransferMode>\n    ) {\n        val direction = (state.block as MachineBlock).getFacing(state)\n        when (type) {\n            ConfigurationType.ITEM -> {\n                configuration[direction.rotateYClockwise()] = TransferMode.INPUT\n            }\n            else -> super.applyDefault(state, type, configuration)\n        }\n    }\n\n    override fun getValidConfigurations(type: ConfigurationType): Array<TransferMode> {\n        return when (type) {\n            ConfigurationType.ITEM -> arrayOf(TransferMode.INPUT, TransferMode.NONE)\n            else -> return super.getValidConfigurations(type)\n        }\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        super.fromTag(tag)\n        burnTime = tag?.getInt(\"BurnTime\") ?: 0\n        maxBurnTime = tag?.getInt(\"MaxBurnTime\") ?: 0\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        tag?.putInt(\"BurnTime\", burnTime)\n        tag?.putInt(\"MaxBurnTime\", maxBurnTime)\n        super.toTag(tag)\n    }\n\n    abstract fun getFuelMap(): Map<Item, Int>\n\n    companion object {\n        const val BURN_TIME_ID = 4\n        const val TOTAL_BURN_TIME_ID = 5\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/generators/SteamTurbineBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.generators\n\nimport alexiil.mc.lib.attributes.fluid.amount.FluidAmount\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.components.FluidComponent\nimport me.steven.indrev.components.autosync\nimport me.steven.indrev.components.multiblock.MultiBlockComponent\nimport me.steven.indrev.components.multiblock.definitions.SteamTurbineStructureDefinition\nimport me.steven.indrev.components.trackLong\nimport me.steven.indrev.registry.IRFluidRegistry\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.bucket\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\n\nclass SteamTurbineBlockEntity(pos: BlockPos, state: BlockState) : GeneratorBlockEntity(Tier.MK4, MachineRegistry.STEAM_TURBINE_REGISTRY, pos, state) {\n\n    init {\n        this.multiblockComponent = SteamTurbineMultiblockComponent()\n        this.fluidComponent = SteamTurbineFluidComponent()\n    }\n\n    override val maxInput: Long = 0\n\n    var efficiency by autosync(EFFICIENCY, 1.0)\n\n    var generatingTicks = 0\n    var totalInserted: FluidAmount = FluidAmount.ZERO\n\n    init {\n        trackLong(GENERATING) { getGenerationRatio() * 100 }\n    }\n\n\n    // used for the screen handler\n    @Environment(EnvType.CLIENT)\n    var consuming: FluidAmount = FluidAmount.ZERO\n\n    override fun getGenerationRatio(): Long {\n        val radius = getRadius()\n        val eff  = totalInserted.div(20).asInexactDouble()\n        return (((eff * 2) / (radius.toDouble() / 7)) * 2048).toLong()\n    }\n\n    override fun shouldGenerate(): Boolean {\n        if (generatingTicks <= 0) {\n            if (!totalInserted.isZero) {\n                generatingTicks = 20\n                fluidComponent!![0].extract(Long.MAX_VALUE, true)\n            } else\n                return false\n        }\n        generatingTicks--\n        if (generatingTicks <= 0) totalInserted = FluidAmount.ZERO\n        return true\n    }\n\n    private fun getConsumptionRatio(): FluidAmount {\n        return totalInserted.div(20)\n    }\n\n    private fun getRadius(): Int {\n        //return 7\n        val matcher = multiblockComponent!!.getSelectedMatcher(world ?: return 0, pos, cachedState)\n        return SteamTurbineStructureDefinition.getRadius(matcher.builtId ?: return 0)\n    }\n\n    private inner class SteamTurbineFluidComponent : FluidComponent({ this }, bucket, 1) {\n\n        override fun getTankCapacity(index: Int): Long {\n            return (((getRadius() * getRadius().toLong()) * efficiency) * 81L).toLong()\n        }\n\n        override fun isFluidValidForTank(index: Int, variant: FluidVariant): Boolean {\n            return variant.isOf(IRFluidRegistry.STEAM_STILL)\n        }\n    }\n\n    private inner class SteamTurbineMultiblockComponent : MultiBlockComponent({ _, _, _ -> SteamTurbineStructureDefinition }) {\n        override fun tick(world: World, pos: BlockPos, blockState: BlockState) {\n            super.tick(world, pos, blockState)\n            SteamTurbineStructureDefinition\n                .getInputValvePositions(pos, blockState, getSelectedMatcher(world, pos, blockState))\n                .forEach { valvePos ->\n                    val valveBlockEntity = world.getBlockEntity(valvePos)\n                    if (valveBlockEntity is SteamTurbineSteamInputValveBlockEntity)\n                        valveBlockEntity.steamTurbinePos = pos\n                }\n\n            if (!isBuilt(world, pos, blockState)) {\n                fluidComponent!![0].amount = 0\n            }\n        }\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        tag.putDouble(\"Efficiency\", efficiency)\n        super.toTag(tag)\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        efficiency = tag.getDouble(\"Efficiency\")\n        super.fromTag(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        tag.put(\"Consuming\", getConsumptionRatio().toNbt())\n        multiblockComponent?.writeNbt(tag)\n    }\n\n    override fun fromClientTag(tag: NbtCompound) {\n        consuming = FluidAmount.fromNbt(tag.getCompound(\"Consuming\")) ?: consuming\n        multiblockComponent?.readNbt(tag)\n    }\n\n    companion object {\n        const val EFFICIENCY = 2\n        const val GENERATING = 3\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/generators/SteamTurbineSteamInputValveBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.generators\n\nimport me.steven.indrev.registry.IRBlockRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.util.math.BlockPos\n\nclass SteamTurbineSteamInputValveBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(IRBlockRegistry.STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ENTITY, pos, state) {\n\n    var steamTurbinePos: BlockPos = BlockPos(-1, -1, -1)\n    var inserted = false\n\n    fun getSteamTurbine(): SteamTurbineBlockEntity? {\n        val blockEntity = world!!.getBlockEntity(steamTurbinePos) as? SteamTurbineBlockEntity ?: return null\n        return if (blockEntity.multiblockComponent?.isBuilt(world!!, pos, blockEntity.cachedState) == true)\n            blockEntity\n        else null\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/laser/CapsuleBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.laser\n\nimport com.google.common.base.Preconditions\nimport me.steven.indrev.blocks.machine.LaserBlock\nimport me.steven.indrev.registry.IRBlockRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.inventory.Inventories\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.collection.DefaultedList\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.World\n\nclass CapsuleBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(IRBlockRegistry.CAPSULE_BLOCK_ENTITY, pos, state) {\n\n    var inventory: DefaultedList<ItemStack> = DefaultedList.ofSize(1, ItemStack.EMPTY)\n\n    var lastProgress = 0\n\n    fun getActiveLasersCount(): Int {\n        return Direction.values().count {\n            val laser = world?.getBlockEntity(pos.offset(it, 4)) as? LaserBlockEntity ?: return@count false\n            laser.cachedState[LaserBlock.POWERED]\n        }\n    }\n\n    override fun readNbt(tag: NbtCompound?) {\n        super.readNbt(tag)\n        inventory = DefaultedList.ofSize(1, ItemStack.EMPTY)\n        Inventories.readNbt(tag, inventory)\n    }\n\n    override fun writeNbt(tag: NbtCompound) {\n        super.writeNbt(tag)\n        Inventories.writeNbt(tag, inventory)\n    }\n\n    open fun sync() {\n        Preconditions.checkNotNull(world) // Maintain distinct failure case from below\n        check(world is ServerWorld) { \"Cannot call sync() on the logical client! Did you check world.isClient first?\" }\n        (world as ServerWorld).chunkManager.markForUpdate(getPos())\n    }\n\n    companion object {\n        fun tick(world: World, pos: BlockPos, state: BlockState, blockEntity: CapsuleBlockEntity) {\n            if (world.isClient) return\n            val tag = blockEntity.inventory[0].orCreateNbt\n            val progress = tag.getInt(\"Progress\")\n            if (tag.contains(\"Progress\") && progress == blockEntity.lastProgress) {\n                tag.remove(\"Progress\")\n            }\n            blockEntity.lastProgress = progress\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/laser/CapsuleBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.laser\n\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.WorldRenderer\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.render.model.json.ModelTransformation\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.math.Vec3f\n\nclass CapsuleBlockEntityRenderer : BlockEntityRenderer<CapsuleBlockEntity> {\n\n    override fun render(\n        entity: CapsuleBlockEntity?,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider?,\n        light: Int,\n        overlay: Int\n    ) {\n        entity ?: return\n        val itemStack = entity.inventory[0]\n        if (itemStack.isEmpty) return\n        val lightCoord = WorldRenderer.getLightmapCoordinates(entity.world, entity.pos)\n        matrices?.run {\n            push()\n            val time = entity.world?.time ?: 1\n            translate(0.5, 0.35, 0.5)\n            scale(1.2f, 1.2f, 1.2f)\n            multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion((time + tickDelta) * 16 * entity.getActiveLasersCount()))\n            MinecraftClient.getInstance().itemRenderer.renderItem(itemStack, ModelTransformation.Mode.GROUND, lightCoord, overlay, matrices, vertexConsumers, 0)\n            pop()\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/laser/LaserBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.laser\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blocks.machine.FacingMachineBlock\nimport me.steven.indrev.blocks.machine.LaserBlock\nimport me.steven.indrev.config.MachineConfig\nimport me.steven.indrev.recipes.machines.LaserRecipe\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport me.steven.indrev.utils.component3\nimport me.steven.indrev.utils.toVec3d\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.minecraft.block.BlockState\nimport net.minecraft.entity.Entity\nimport net.minecraft.item.ItemStack\nimport net.minecraft.particle.ParticleTypes\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.sound.SoundCategory\nimport net.minecraft.sound.SoundEvents\nimport net.minecraft.util.math.*\nimport net.minecraft.world.World\nimport net.minecraft.world.explosion.Explosion\nimport java.util.*\n\nclass LaserBlockEntity(pos: BlockPos, state: BlockState) : MachineBlockEntity<MachineConfig>(Tier.MK4, MachineRegistry.LASER_EMITTER_REGISTRY, pos, state) {\n\n    override val maxInput: Long = config.maxInput\n    override val maxOutput: Long = 0\n\n    private var ticksUntilExplode = 100\n\n    private var recipe: LaserRecipe? = null\n\n    override fun machineTick() {\n        if (!cachedState[LaserBlock.POWERED]) {\n            ticksUntilExplode = 100\n            return\n        }\n        val facing = cachedState[FacingMachineBlock.FACING]\n        val containerPos = pos.offset(facing, 4)\n\n        val container = world?.getBlockEntity(containerPos) as? CapsuleBlockEntity\n\n        if (container == null) {\n            ticksUntilExplode--\n            if (ticksUntilExplode < 0) explode()\n            return\n        } else {\n            ticksUntilExplode = 100\n            BlockPos.iterate(pos.offset(facing), pos.offset(facing, 3)).forEach {\n                world?.breakBlock(it, true)\n            }\n        }\n\n        val stack = container.inventory[0]\n        if (recipe == null) {\n            recipe = LaserRecipe.TYPE.getMatchingRecipe(world as ServerWorld, stack)\n                .firstOrNull { it.matches(stack, emptyList()) }\n        } else if (ItemStack.areItemsEqual(recipe!!.outputs.first().stack, stack)) {\n            return\n        }\n\n        if (recipe?.matches(stack, emptyList()) != true) {\n            world?.breakBlock(containerPos, false)\n            recipe = null\n            return\n        }\n\n        if (!use(config.energyCost)) {\n            world?.setBlockState(pos, cachedState.with(LaserBlock.POWERED, false))\n            return\n        }\n\n        val (x, y, z) = scale(facing.vec3f(), 3.0f)\n        val damageArea = Box(pos).stretch(x.toDouble(), y.toDouble(), z.toDouble()).let {\n            when {\n                facing.axis.isVertical ->\n                    it.shrink(0.3, 0.0, 0.3)\n                facing.axis == Direction.Axis.X ->\n                    it.shrink(0.0, 0.3, 0.3)\n                else ->\n                    it.shrink(0.3, 0.3, 0.0)\n            }\n        }\n        world?.getEntitiesByClass(Entity::class.java, damageArea) { true }?.forEach {\n            it.setOnFireFor(1000)\n            it.damage(LaserBlock.LASER_DAMAGE_SOURCE, 2f)\n        }\n\n        val tag = stack.orCreateNbt\n        val progress = tag.getDouble(\"Progress\")\n        if (progress >= recipe!!.ticks) {\n            container.inventory[0] = recipe!!.craft(null as Random?).first()\n            container.markDirty()\n            container.sync()\n            world?.updateNeighbors(containerPos, container.cachedState.block)\n        } else\n            tag.putDouble(\"Progress\", progress + config.energyCost)\n    }\n\n    @Environment(EnvType.CLIENT)\n    override fun machineClientTick() {\n        if (cachedState[LaserBlock.POWERED]) {\n            if (!isEmittingLaser()) {\n                if (world!!.random.nextDouble() > 0.7) {\n                    spawnParticles(world!!, pos, isFire = true)\n                    world!!.playSound(\n                        pos.x.toDouble() + 0.5,\n                        pos.y.toDouble() + 0.5,\n                        pos.z.toDouble() + 0.5,\n                        SoundEvents.BLOCK_FIRE_EXTINGUISH,\n                        SoundCategory.BLOCKS,\n                        0.6f,\n                        0.8f,\n                        false\n                    )\n                }\n                return\n            }\n            spawnParticles(world!!, pos, 0.4)\n        }\n    }\n\n    private fun spawnParticles(world: World, pos: BlockPos, width: Double = 0.5625, isFire: Boolean = false) {\n        val random = world.random\n        val facing = cachedState[FacingMachineBlock.FACING]\n        repeat(6) {\n            val axis = facing.axis\n            val x = if (axis != Direction.Axis.X) 0.7 - random.nextDouble() * width else 0.0\n            val y = if (axis != Direction.Axis.Y) 0.7 - random.nextDouble() * width else 0.0\n            val z = if (axis != Direction.Axis.Z) 0.7 - random.nextDouble() * width else 0.0\n            val source = Vec3d(pos.x + x, pos.y + y, pos.z + z).let {\n                val (x, y, z) = facing.unitVector\n                if (x > 0 || y > 0 || z > 0)\n                    it.add(Vec3d(facing.unitVector))\n                else it\n            }\n            val velocity = pos.offset(facing, 4).toVec3d().add(x, y, z).subtract(source).normalize().multiply(0.18)\n            world.addParticle(\n                if (isFire) ParticleTypes.SMOKE else IndustrialRevolution.LASER_PARTICLE,\n                true,\n                source.x,\n                source.y,\n                source.z,\n                if (isFire) 0.0 else velocity.x,\n                if (isFire) 0.05 else velocity.y,\n                if (isFire) 0.0 else velocity.z\n            )\n        }\n    }\n\n    fun isEmittingLaser(): Boolean {\n        val facing = cachedState[FacingMachineBlock.FACING]\n        val containerPos = pos.offset(facing, 4)\n        return cachedState[LaserBlock.POWERED] && world?.getBlockEntity(containerPos) as? CapsuleBlockEntity != null\n    }\n\n    private fun explode() {\n        world?.createExplosion(\n            null,\n            pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(),\n            3f,\n            true,\n            Explosion.DestructionType.DESTROY\n        )\n    }\n\n    companion object {\n        private fun Direction.vec3f() = Vec3f(offsetX.toFloat(), offsetY.toFloat(), offsetZ.toFloat())\n\n        private fun scale(v: Vec3f, scale: Float): Vec3f {\n            return Vec3f(v.x * scale, v.y * scale, v.z * scale)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/laser/LaserBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.laser\n\nimport me.steven.indrev.blocks.machine.FacingMachineBlock\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.block.entity.BeaconBlockEntityRenderer\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.Direction\n\nclass LaserBlockEntityRenderer : BlockEntityRenderer<LaserBlockEntity> {\n\n    override fun render(\n        entity: LaserBlockEntity?,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider?,\n        light: Int,\n        overlay: Int\n    ) {\n        entity ?: return\n        if (!entity.isEmittingLaser()) return\n        val direction = entity.cachedState[FacingMachineBlock.FACING]\n        matrices?.run {\n            push()\n            translate(direction.unitVector.x.toDouble(), direction.unitVector.y.toDouble(), direction.unitVector.z.toDouble())\n            translate(0.5, 0.5, 0.5)\n            val rotation = direction.let {\n                when {\n                    it.axis.isHorizontal -> it.rotateYCounterclockwise().unitVector.getDegreesQuaternion(90f)\n                    it == Direction.DOWN -> Direction.EAST.unitVector.getDegreesQuaternion(180f)\n                    else -> it.unitVector.getDegreesQuaternion(90f)\n                }\n            }\n            multiply(rotation)\n            translate(-0.5, -0.5, -0.5)\n            BeaconBlockEntityRenderer.renderBeam(\n                matrices,\n                vertexConsumers,\n                TEXTURE,\n                tickDelta,\n                1.0f,\n                entity.world!!.time,\n                0,\n                3,\n                floatArrayOf(1f, 1f, 1f),\n                0.085f,\n                0.1f\n            )\n            pop()\n        }\n\n    }\n\n    override fun rendersOutsideBoundingBox(blockEntity: LaserBlockEntity?): Boolean = true\n\n    companion object {\n        val TEXTURE: Identifier = identifier(\"textures/entity/laser.png\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/miningrig/DataCardWriterBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.miningrig\n\nimport me.steven.indrev.api.OreDataCards\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.components.autosync\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.math.BlockPos\n\nclass DataCardWriterBlockEntity (tier: Tier, pos: BlockPos, state: BlockState)\n    : MachineBlockEntity<BasicMachineConfig>(tier, MachineRegistry.DATA_CARD_WRITER_REGISTRY, pos, state) {\n    init {\n        this.inventoryComponent = inventory(this) {\n            input {\n                0 filter { itemStack, _ -> itemStack.item == IRItemRegistry.ORE_DATA_CARD }\n                1 until 13 filter { stack, _ -> OreDataCards.isAllowed(stack) }\n                13 until 16 filter { stack, _ -> OreDataCards.Modifier.isModifierItem(stack.item) }\n            }\n        }\n    }\n\n    var processTime by autosync(PROCESS_ID, 0)\n    var totalProcessTime by autosync(TOTAL_PROCESS_ID, 0)\n\n    private val modifiersToAdd = mutableMapOf<OreDataCards.Modifier, Int>()\n    private val toWrite = mutableListOf<ItemStack>()\n\n    override fun machineTick() {\n        if (totalProcessTime > 0 && use(getEnergyCost())) {\n            if (processTime >= totalProcessTime)\n                finish()\n            else {\n                processTime++\n                workingState = true\n            }\n        } else {\n            workingState = false\n        }\n    }\n\n    fun start() {\n        val inventory = inventoryComponent!!.inventory\n        val cardStack = inventory.getStack(0)\n        val oldData = OreDataCards.readNbt(cardStack)\n\n        ORES_SLOTS.forEach { slot ->\n            val stack = inventory.getStack(slot)\n            if (!stack.isEmpty && stack.count == 64) {\n                toWrite.add(stack)\n                inventory.setStack(slot, ItemStack.EMPTY)\n            }\n        }\n\n        if (toWrite.isEmpty() && oldData == null) {\n            return\n        }\n\n        MODIFIERS_SLOTS.forEach { slot ->\n            val stack = inventory.getStack(slot)\n            val modifier = OreDataCards.Modifier.byItem(stack.item)\n            var level = (modifiersToAdd[modifier] ?: 0) + (oldData?.modifiersUsed?.get(modifier) ?: 0)\n            when (modifier) {\n                OreDataCards.Modifier.RICHNESS -> {\n                    while (stack.count >= 16 && level < 40) {\n                        stack.decrement(16)\n                        level++\n                    }\n                }\n                OreDataCards.Modifier.SPEED, OreDataCards.Modifier.SIZE -> {\n                    while (stack.count >= 64) {\n                        stack.decrement(64)\n                        level++\n                    }\n                }\n                OreDataCards.Modifier.RNG -> {\n                    if (level == 0) {\n                        stack.decrement(1)\n                        val r = world!!.random.nextDouble()\n                        if (r > 0.95 && r <= 0.98) {\n                            level = -1\n                        } else if (r > 0.98) {\n                            level = 1\n                        }\n                    }\n                }\n                else -> return@forEach\n            }\n            modifiersToAdd[modifier] = level - (oldData?.modifiersUsed?.get(modifier) ?: 0)\n        }\n\n        processTime = 0\n        totalProcessTime = 20*10 + (toWrite.size * (5*modifiersToAdd.map { it.value }.sum()))\n    }\n\n    private fun finish() {\n        val inventory = inventoryComponent!!.inventory\n        val cardStack = inventory.getStack(0)\n        val oldData = OreDataCards.readNbt(cardStack)\n\n        val oreTypes = toWrite.map { it.item }.distinct().count()\n        val richnessDecrease = if (oreTypes == 1) 0.02 else 0.04\n        val richnessModifier = ((modifiersToAdd[OreDataCards.Modifier.RICHNESS] ?: 0) * 0.01).coerceAtMost(0.2)\n        val richness = ((oldData?.richness ?: 1.0) - (richnessDecrease * toWrite.size) + richnessModifier).coerceIn(richnessDecrease, 1.0)\n\n        val speedModifier = ((oldData?.modifiersUsed?.get(OreDataCards.Modifier.SPEED) ?: 0) + (modifiersToAdd[OreDataCards.Modifier.SPEED] ?: 0)) * 20\n        val speed = 100 + (richness * 1100) - speedModifier + (modifiersToAdd[OreDataCards.Modifier.SIZE] ?: 0) * 2\n\n        val rng = oldData?.rng ?: modifiersToAdd[OreDataCards.Modifier.RNG] ?: 0\n\n        val oreEnergyRequired = toWrite.sumOf { OreDataCards.getCost(it) * 16 }\n        val energyRequired = (oldData?.energyRequired ?: 32) + 8 * (modifiersToAdd[OreDataCards.Modifier.SPEED] ?: 0) + oreEnergyRequired\n\n        val cyclesModifiers = (modifiersToAdd[OreDataCards.Modifier.SIZE] ?: 0) * 128\n        val maxCycles = (oldData?.maxCycles ?: 0) + (toWrite.size * 64) + cyclesModifiers\n\n        val items = mutableMapOf<Item, Int>()\n        oldData?.entries?.forEach { entry ->\n            items[entry.item] = entry.count\n        }\n        toWrite.forEach { stack ->\n            items[stack.item] = items.getOrDefault(stack.item, 0) + stack.count\n        }\n        val entries = items.keys.map { OreDataCards.OreEntry(it, items[it]!!) }\n\n        val modifiersMap = mutableMapOf<OreDataCards.Modifier, Int>()\n        modifiersToAdd.forEach { (modifier, level) ->\n            modifiersMap[modifier] = (oldData?.modifiersUsed?.get(modifier)?: 0) + level\n        }\n        val data = OreDataCards.Data(entries, modifiersMap, richness, speed.toInt(), rng, energyRequired, maxCycles, oldData?.used ?: 0)\n\n        OreDataCards.writeNbt(cardStack, data)\n\n        modifiersToAdd.clear()\n        toWrite.clear()\n        processTime = 0\n        totalProcessTime = 0\n    }\n\n    override fun getEnergyCost(): Long {\n        return IRConfig.machines.dataCardWriter.energyCost\n    }\n\n    companion object {\n        const val PROCESS_ID = 2\n        const val TOTAL_PROCESS_ID = 3\n\n        val MODIFIERS_SLOTS = 13 until 16\n        val ORES_SLOTS = 1 until 13\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/miningrig/DrillBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.miningrig\n\nimport com.google.common.base.Preconditions\nimport io.netty.buffer.Unpooled\nimport me.steven.indrev.api.OreDataCards\nimport me.steven.indrev.blocks.machine.DrillBlock\nimport me.steven.indrev.gui.screenhandlers.machines.MiningRigDrillScreenHandler\nimport me.steven.indrev.packets.client.MiningRigSpawnBlockParticlesPacket\nimport me.steven.indrev.registry.IRBlockRegistry\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport me.steven.indrev.utils.component3\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\nimport net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.LootableContainerBlockEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.inventory.Inventories\nimport net.minecraft.item.BlockItem\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket\nimport net.minecraft.screen.ScreenHandler\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.server.network.ServerPlayerEntity\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.collection.DefaultedList\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.registry.Registry\nimport net.minecraft.world.World\n\nclass DrillBlockEntity(pos: BlockPos, state: BlockState) : LootableContainerBlockEntity(IRBlockRegistry.DRILL_BLOCK_ENTITY_TYPE, pos, state), ExtendedScreenHandlerFactory {\n    var inventory: DefaultedList<ItemStack> = DefaultedList.ofSize(1, ItemStack.EMPTY)\n\n    var position: Double = 1.0\n\n    var miningProgress: Double = 0.0\n\n    fun setWorkingState(working: Boolean) {\n        if (cachedState[DrillBlock.WORKING] != working)\n            world?.setBlockState(pos, cachedState.with(DrillBlock.WORKING, working))\n    }\n\n    fun tickMining(miningRig: MiningRigBlockEntity, data: OreDataCards.Data) {\n        setWorkingState(true)\n        miningProgress += getSpeedMultiplier()\n\n        if (miningProgress >= data.speed) {\n            miningProgress = 0.0\n            val item = data.pickRandom(world!!.random)\n            val rng = if (data.rng == 1 && world!!.random.nextDouble() < 0.1) 2 else 1\n            val count = ((5 * data.richness) + world!!.random.nextInt((OreDataCards.MAX_PER_CYCLE * data.richness).toInt().coerceAtLeast(1))).toInt() * rng\n            var stack = ItemStack(item, count)\n\n            if (data.rng == 1 && world!!.random.nextDouble() > 0.9) {\n                stack.count *= 2\n            } else if (data.rng == -1 && world!!.random.nextDouble() > 0.9) {\n                stack = ItemStack.EMPTY\n            }\n\n            miningRig.output(stack)\n\n            data.used++\n            miningRig.lastMinedItem = ItemStack(item)\n            miningRig.sync()\n\n            val drillStack = inventory[0]\n            drillStack.damage(1, world!!.random, null)\n            if (drillStack.damage >= drillStack.maxDamage) {\n                inventory[0] = ItemStack.EMPTY\n            }\n\n            if (item is BlockItem)\n                sendBlockBreakPacket(item.block)\n        }\n    }\n\n    private fun sendBlockBreakPacket(block: Block) {\n        val (x, y, z) = pos\n        val players = (world as ServerWorld).server.playerManager.playerList\n        for (i in players.indices) {\n            val serverPlayerEntity = players[i]\n            if (serverPlayerEntity.world.registryKey === world!!.registryKey) {\n                val xOffset = x - serverPlayerEntity.x\n                val yOffset = y - serverPlayerEntity.y\n                val zOffset = z - serverPlayerEntity.z\n                if (xOffset * xOffset + yOffset * yOffset + zOffset * zOffset < 64 * 64) {\n                    val buf = PacketByteBuf(Unpooled.buffer())\n                    buf.writeBlockPos(pos)\n                    buf.writeInt(Registry.BLOCK.getRawId(block))\n                    ServerPlayNetworking.send(serverPlayerEntity, MiningRigSpawnBlockParticlesPacket.BLOCK_BREAK_PACKET, buf)\n                }\n            }\n        }\n    }\n\n    override fun size(): Int = 1\n\n    override fun getContainerName(): Text = TranslatableText(\"block.indrev.drill\")\n\n    override fun createScreenHandler(syncId: Int, playerInventory: PlayerInventory): ScreenHandler {\n        return MiningRigDrillScreenHandler(syncId, playerInventory, ScreenHandlerContext.create(world, pos))\n    }\n\n    override fun getInvStackList(): DefaultedList<ItemStack> = inventory\n\n    override fun setInvStackList(list: DefaultedList<ItemStack>) {\n        inventory = list\n    }\n\n    override fun readNbt(tag: NbtCompound) {\n        super.readNbt(tag)\n        inventory = DefaultedList.ofSize(size(), ItemStack.EMPTY)\n        if (!deserializeLootTable(tag)) {\n            Inventories.readNbt(tag, inventory)\n        }\n        position = tag.getDouble(\"Position\")\n        miningProgress = tag.getDouble(\"Mining\")\n    }\n\n    override fun writeNbt(tag: NbtCompound) {\n        super.writeNbt(tag)\n        if (!serializeLootTable(tag)) {\n            Inventories.writeNbt(tag, inventory)\n        }\n        tag.putDouble(\"Position\", position)\n        tag.putDouble(\"Mining\", miningProgress)\n    }\n\n    override fun toUpdatePacket(): BlockEntityUpdateS2CPacket {\n        return BlockEntityUpdateS2CPacket.create(this)\n    }\n\n    override fun toInitialChunkDataNbt(): NbtCompound {\n        val nbt = super.toInitialChunkDataNbt()\n        writeNbt(nbt)\n        return nbt\n    }\n\n\n    fun sync() {\n        Preconditions.checkNotNull(world) // Maintain distinct failure case from below\n        check(world is ServerWorld) { \"Cannot call sync() on the logical client! Did you check world.isClient first?\" }\n        (world as ServerWorld).chunkManager.markForUpdate(getPos())\n    }\n\n    override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) {\n        buf.writeBlockPos(pos)\n    }\n\n    fun getSpeedMultiplier(): Double {\n        val item = inventory[0].item\n        return if (position > 0) 0.0 else when (item) {\n            IRItemRegistry.STONE_DRILL_HEAD -> 1.0\n            IRItemRegistry.IRON_DRILL_HEAD -> 2.0\n            IRItemRegistry.DIAMOND_DRILL_HEAD -> 4.0\n            IRItemRegistry.NETHERITE_DRILL_HEAD -> 8.0\n            else -> 0.0\n        }\n    }\n\n    companion object {\n\n        fun isValidDrill(item: Item) =\n            item == IRItemRegistry.STONE_DRILL_HEAD\n                    || item == IRItemRegistry.IRON_DRILL_HEAD\n                    || item == IRItemRegistry.DIAMOND_DRILL_HEAD\n                    || item == IRItemRegistry.NETHERITE_DRILL_HEAD\n\n        fun tick(world: World, pos: BlockPos, state: BlockState, blockEntity: DrillBlockEntity) {\n\n            if (blockEntity.inventory[0].isEmpty) {\n                blockEntity.position = 1.0\n                blockEntity.markDirty()\n                blockEntity.sync()\n            } else if (state[DrillBlock.WORKING]) {\n                if (blockEntity.position > 0) blockEntity.position -= 0.01\n\n                else return\n                blockEntity.markDirty()\n                blockEntity.sync()\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/miningrig/DrillBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.miningrig\n\nimport me.steven.indrev.blocks.machine.DrillBlock\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.OverlayTexture\nimport net.minecraft.client.render.RenderLayers\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.WorldRenderer\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.util.ModelIdentifier\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.math.Vec3f\n\nclass DrillBlockEntityRenderer : BlockEntityRenderer<DrillBlockEntity> {\n    override fun render(\n        entity: DrillBlockEntity,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider,\n        light: Int,\n        overlay: Int\n    ) {\n        val variant = when (entity.inventory[0].item) {\n            IRItemRegistry.STONE_DRILL_HEAD -> \"stone\"\n            IRItemRegistry.IRON_DRILL_HEAD -> \"iron\"\n            IRItemRegistry.DIAMOND_DRILL_HEAD -> \"diamond\"\n            IRItemRegistry.NETHERITE_DRILL_HEAD -> \"netherite\"\n            else -> return\n        }\n        val model =\n            MinecraftClient.getInstance().bakedModelManager.getModel(ModelIdentifier(identifier(\"drill_head\"), variant))\n        matrices?.run {\n            push()\n            val entry = peek()\n            translate(0.5, 0.0, 0.5)\n            if (entity.position <= 0.0 && entity.cachedState[DrillBlock.WORKING]) {\n                multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion( (entity.world!!.time + tickDelta) * 12))\n            }\n            translate(-0.5, entity.position, -0.5)\n            MinecraftClient.getInstance().blockRenderManager.modelRenderer.render(\n                entry,\n                vertexConsumers.getBuffer(RenderLayers.getBlockLayer(entity.cachedState)),\n                null,\n                model,\n                -1f,\n                -1f,\n                -1f,\n                WorldRenderer.getLightmapCoordinates(entity.world, entity.pos),\n                OverlayTexture.DEFAULT_UV\n            )\n            pop()\n        }\n    }\n\n    override fun rendersOutsideBoundingBox(blockEntity: DrillBlockEntity?): Boolean = true\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/miningrig/MiningRigBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.miningrig\n\nimport me.steven.indrev.api.OreDataCards\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blocks.machine.DrillBlock\nimport me.steven.indrev.blocks.machine.MachineBlock\nimport me.steven.indrev.components.*\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.misc.OreDataCardItem\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.*\nimport net.fabricmc.fabric.api.transfer.v1.item.ItemVariant\nimport net.fabricmc.fabric.api.transfer.v1.transaction.Transaction\nimport net.minecraft.block.BlockState\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport java.util.EnumSet\n\nclass MiningRigBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : MachineBlockEntity<BasicMachineConfig>(tier, MachineRegistry.MINING_RIG_REGISTRY, pos, state) {\n\n    init {\n        this.inventoryComponent = inventory(this) {\n            input {\n                0 filter { itemStack, _ -> itemStack.item is OreDataCardItem }\n            }\n        }\n        trackLong(ENERGY_REQUIRED_ID) { getEnergyCost() }\n        trackInt(MAX_SPEED_ID) {\n            OreDataCards.readNbt(inventoryComponent!!.inventory.getStack(0))?.speed ?: 0\n        }\n        VALID_DRILL_POSITIONS.forEachIndexed { index, offset ->\n            val id = START_DRILL_ID + index\n            val drillPos = pos.add(offset)\n            trackDouble(id) {\n                val block = world?.getBlockState(drillPos)?.block as? DrillBlock ?: return@trackDouble 0.0\n                val drillBlockEntity = world?.getBlockEntity(block.part.getBlockEntityPos(drillPos)) as? DrillBlockEntity ?: return@trackDouble 0.0\n                drillBlockEntity.miningProgress\n            }\n        }\n    }\n\n    override val syncToWorld: Boolean = true\n\n    override val maxInput: Long = config.maxInput\n    override val maxOutput: Long = 0\n\n    var lastMinedItem: ItemStack = ItemStack.EMPTY\n\n    val storageDirections = Direction.values().toMutableList()\n    private val remainingStacks = mutableListOf<ItemStack>()\n\n    override fun machineTick() {\n        val inventory = inventoryComponent?.inventory ?: return\n\n        if (remainingStacks.isNotEmpty()) {\n            val copy = ArrayList(remainingStacks)\n            remainingStacks.clear()\n            copy.forEach { output(it) }\n\n            getActiveDrills().forEach { drill -> drill.setWorkingState(false) }\n            workingState = false\n            return\n        }\n\n        val cardStack = inventory.getStack(0)\n        val data = OreDataCards.readNbt(cardStack)\n\n        if (data != null && data.isValid() && !data.isEmpty() && use(getEnergyCost())) {\n            workingState = true\n            val before = data.used\n            getActiveDrills().forEach { drill -> drill.tickMining(this, data) }\n            if (before != data.used)\n                OreDataCards.writeNbt(cardStack, data)\n        } else {\n            workingState = false\n            getActiveDrills().forEach { drill ->\n                drill.setWorkingState(false)\n                drill.miningProgress = 0.0\n            }\n        }\n    }\n\n    fun output(stack: ItemStack) {\n        val variant = ItemVariant.of(stack)\n        var count = stack.count.toLong()\n        storageDirections.removeIf { dir ->\n            val itemStorage = itemStorageOf(world!!, pos.offset(dir), dir.opposite) ?: return@removeIf true\n            if (count > 0) {\n                transaction { tx ->\n                    val inserted = itemStorage.insert(variant, count, tx)\n                    tx.commit()\n                    count -= inserted\n                }\n            }\n\n            false\n        }\n\n        if (count > 0) {\n            stack.count = count.toInt()\n            remainingStacks.add(stack)\n        }\n    }\n\n    fun getActiveDrills(): List<DrillBlockEntity> {\n        return VALID_DRILL_POSITIONS.map { pos.add(it) }.mapNotNull { pos ->\n            val blockState = world?.getBlockState(pos)\n            val block = blockState?.block\n            if (block is DrillBlock) {\n                val blockEntity = world?.getBlockEntity(block.part.getBlockEntityPos(pos)) as? DrillBlockEntity ?: return@mapNotNull null\n                val itemStack = blockEntity.inventory[0]\n                if (!itemStack.isEmpty && DrillBlockEntity.isValidDrill(itemStack.item)) {\n                    blockEntity\n                } else {\n                    blockEntity.setWorkingState(false)\n                    null\n                }\n            } else null\n        }\n    }\n\n    override fun getEnergyCost(): Long {\n        return config.energyCost + (IRConfig.machines.drill * getActiveDrills().size) + (OreDataCards.readNbt(inventoryComponent!!.inventory.getStack(0))?.energyRequired ?: 0)\n    }\n\n    override fun applyDefault(\n        state: BlockState,\n        type: ConfigurationType,\n        configuration: MutableMap<Direction, TransferMode>\n    ) {\n        val direction = (state.block as MachineBlock).getFacing(state)\n        when (type) {\n            ConfigurationType.ITEM -> {\n                configuration[direction.rotateYCounterclockwise()] = TransferMode.OUTPUT\n            }\n            else -> super.applyDefault(state, type, configuration)\n        }\n    }\n\n    override fun getValidConfigurations(type: ConfigurationType): Array<TransferMode> {\n        return when (type) {\n            ConfigurationType.ITEM -> arrayOf(TransferMode.OUTPUT, TransferMode.NONE)\n            else -> return super.getValidConfigurations(type)\n        }\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        tag.put(\"LastMinedBlock\", lastMinedItem.writeNbt(NbtCompound()))\n    }\n\n    override fun fromClientTag(tag: NbtCompound) {\n        lastMinedItem = ItemStack.fromNbt(tag.getCompound(\"LastMinedBlock\"))\n    }\n\n    companion object {\n        val VALID_DRILL_POSITIONS = arrayOf(\n            BlockPos(-1, 0, 0),\n            BlockPos(1, 0, 0),\n            BlockPos(0, 0, -1),\n            BlockPos(0, 0, 1),\n            BlockPos(-1, 0, -1),\n            BlockPos(-1, 0, 1),\n            BlockPos(1, 0, 1),\n            BlockPos(1, 0, -1)\n        )\n\n        const val ENERGY_REQUIRED_ID = 2\n        const val MAX_SPEED_ID = 3\n        const val START_DRILL_ID = 4\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/miningrig/MiningRigBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.miningrig\n\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.render.model.json.ModelTransformation\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.math.Vec3f\n\nclass MiningRigBlockEntityRenderer : BlockEntityRenderer<MiningRigBlockEntity> {\n    override fun render(\n        entity: MiningRigBlockEntity?,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider?,\n        light: Int,\n        overlay: Int\n    ) {\n        if (entity == null || !entity.workingState) return\n        matrices?.run {\n            push()\n            val direction = entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING]\n            when (direction) {\n                Direction.NORTH -> translate(0.34, 0.6, -0.01)\n                Direction.SOUTH -> translate(0.66, 0.6, 1.01)\n                Direction.WEST -> translate(-0.01, 0.6, 0.66)\n                Direction.EAST -> translate(1.01, 0.6, 0.34)\n                else -> {\n                    pop()\n                    return\n                }\n            }\n            multiply(Vec3f.NEGATIVE_Y.getDegreesQuaternion(direction.asRotation()))\n            scale(0.3f, 0.3f, 0.01f)\n            MinecraftClient.getInstance().itemRenderer.renderItem(entity.lastMinedItem, ModelTransformation.Mode.GUI, 15728880, overlay, this, vertexConsumers, 0)\n            pop()\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/modularworkbench/ModularWorkbenchBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.modularworkbench\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.components.autosync\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.items.armor.IRColorModuleItem\nimport me.steven.indrev.items.armor.IRModularArmorItem\nimport me.steven.indrev.items.armor.IRModuleItem\nimport me.steven.indrev.recipes.machines.ModuleRecipe\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.tools.modular.ArmorModule\nimport me.steven.indrev.tools.modular.IRModularItem\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport me.steven.indrev.utils.getRecipes\nimport net.minecraft.block.BlockState\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\n\nclass ModularWorkbenchBlockEntity(tier: Tier, pos: BlockPos, state: BlockState)\n    : MachineBlockEntity<BasicMachineConfig>(tier, MachineRegistry.MODULAR_WORKBENCH_REGISTRY, pos, state) {\n\n    init {\n        this.inventoryComponent = inventory(this) {\n\n            maxStackCount = 1\n\n            0 filter { (_, item) -> item !is IRModularItem<*> }\n            1 filter { stack -> stack.item is IRModuleItem }\n            2 filter { stack -> stack.item is IRModularItem<*> }\n            3 until 15 filter { stack, index -> recipe != null && recipe!!.input.size > index - 3 && recipe!!.input[index - 3].ingredient.test(stack) }\n            input {\n                slots = (1 until 15).map { it }.toIntArray()\n            }\n            output { slot = 15 }\n        }\n    }\n\n    override val syncToWorld: Boolean = true\n\n    override val maxOutput: Long = 0\n\n    var moduleProcessTime by autosync(PROCESS_TIME_ID, 0)\n    var moduleMaxProcessTime by autosync(MAX_PROCESS_TIME_ID, 0)\n\n    private var processTime by autosync(INSTALL_TIME_ID, 0)\n    private var maxProcessTime by autosync(MAX_INSTALL_TIME_ID, 0)\n\n    private var state: State by autosync(STATE_ID, State.IDLE, State.values())\n\n    var selectedRecipe: Identifier? = null\n    var recipe: ModuleRecipe? = null\n        get() {\n            if (selectedRecipe != null)\n                field = world!!.recipeManager.getRecipes(ModuleRecipe.TYPE)[selectedRecipe]!!\n            return field\n        }\n\n    override fun machineTick() {\n        tickModuleInstall()\n        tickModuleCraft()\n    }\n\n    private fun tickModuleCraft() {\n        val inventory = inventoryComponent?.inventory ?: return\n        if (!inventory.getStack(15).isEmpty) return\n        val inputStacks = inventory.inputSlots.map { inventory.getStack(it) }\n        when {\n            recipe?.matches(inputStacks, emptyList()) != true -> {\n                moduleMaxProcessTime = 0\n                moduleProcessTime = 0\n            }\n            moduleMaxProcessTime in 1..moduleProcessTime -> {\n                (3 until 15).forEach { slot -> inventory.setStack(slot, ItemStack.EMPTY) }\n                inventory.setStack(15, recipe!!.outputs[0].stack.copy())\n\n                moduleMaxProcessTime = 0\n                moduleProcessTime = 0\n            }\n            else -> {\n                moduleMaxProcessTime = recipe!!.ticks\n                moduleProcessTime++\n            }\n        }\n    }\n\n    private fun tickModuleInstall() {\n        val inventory = inventoryComponent?.inventory ?: return\n        val targetStack = inventory.getStack(2)\n        val moduleStack = inventory.getStack(1)\n        if (moduleStack.item !is IRModuleItem || targetStack.item !is IRModularItem<*>) {\n            processTime = 0\n            state = State.IDLE\n            return\n        }\n        val targetItem = targetStack.item as IRModularItem<*>\n        val moduleItem = moduleStack.item as IRModuleItem\n        val module = moduleItem.module\n        val compatible = targetItem.getCompatibleModules(targetStack)\n        if (inventory.isEmpty) {\n            processTime = 0\n            workingState = false\n            state = State.IDLE\n        } else {\n            if (isProcessing()\n                && compatible.contains(module)\n                && use(config.energyCost)) {\n                workingState = true\n                processTime += config.processSpeed.toInt()\n                if (processTime >= maxProcessTime) {\n                    inventory.setStack(1, ItemStack.EMPTY)\n                    val tag = targetStack.orCreateNbt\n                    when {\n                        module == ArmorModule.COLOR -> {\n                            if (targetItem !is IRModularArmorItem) return\n                            val colorModuleItem = moduleItem as IRColorModuleItem\n                            targetItem.setColor(targetStack, colorModuleItem.color)\n                        }\n                        tag.contains(module.key) -> {\n                            val level = tag.getInt(module.key) + 1\n                            tag.putInt(module.key, level.coerceAtMost(module.maxLevel))\n                        }\n                        else -> tag.putInt(module.key, 1)\n                    }\n                    processTime = 0\n                    state = State.IDLE\n                }\n            } else if (energy > 0 && !targetStack.isEmpty && !moduleStack.isEmpty && compatible.contains(module)) {\n                val tag = targetStack.orCreateNbt\n                if (tag.contains(module.key)) {\n                    val level = module.getMaxInstalledLevel(targetStack)\n                    if (module != ArmorModule.COLOR && level >= module.maxLevel) {\n                        state = State.MAX_LEVEL\n                        return\n                    }\n                }\n                processTime = 1\n                maxProcessTime = 1200\n                workingState = true\n                state = State.INSTALLING\n            } else {\n                state = State.INCOMPATIBLE\n            }\n        }\n    }\n\n    private fun isProcessing(): Boolean = processTime > 0 && energy > 0\n\n    override fun fromTag(tag: NbtCompound) {\n        processTime = tag.getInt(\"ProcessTime\")\n        if (tag.contains(\"SelectedRecipe\"))\n            selectedRecipe = Identifier(tag.getString(\"SelectedRecipe\"))\n        super.fromTag(tag)\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        tag.putInt(\"ProcessTime\", processTime)\n        if (selectedRecipe != null)\n            tag.putString(\"SelectedRecipe\", selectedRecipe!!.toString())\n        super.toTag(tag)\n    }\n\n    override fun fromClientTag(tag: NbtCompound) {\n        if (tag.contains(\"SelectedRecipe\"))\n            selectedRecipe = Identifier(tag.getString(\"SelectedRecipe\"))\n        inventoryComponent!!.readNbt(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        if (selectedRecipe != null)\n            tag.putString(\"SelectedRecipe\", selectedRecipe!!.toString())\n        inventoryComponent!!.writeNbt(tag)\n    }\n\n    enum class State {\n        IDLE,\n        INSTALLING,\n        INCOMPATIBLE,\n        MAX_LEVEL;\n    }\n\n    companion object {\n        const val PROCESS_TIME_ID = 2\n        const val MAX_PROCESS_TIME_ID = 3\n        const val INSTALL_TIME_ID = 4\n        const val MAX_INSTALL_TIME_ID = 5\n        const val STATE_ID = 6\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/modularworkbench/ModularWorkbenchBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.modularworkbench\n\nimport me.steven.indrev.armor.ModuleFeatureRenderer\nimport me.steven.indrev.components.multiblock.MultiblockBlockEntityRenderer\nimport me.steven.indrev.items.armor.IRModularArmorItem\nimport me.steven.indrev.tools.modular.IRModularItem\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.model.Dilation\nimport net.minecraft.client.model.TexturedModelData\nimport net.minecraft.client.network.AbstractClientPlayerEntity\nimport net.minecraft.client.render.OverlayTexture\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.WorldRenderer\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.render.entity.model.BipedEntityModel\nimport net.minecraft.client.render.entity.model.ElytraEntityModel\nimport net.minecraft.client.render.entity.model.PlayerEntityModel\nimport net.minecraft.client.render.item.ItemRenderer\nimport net.minecraft.client.render.model.json.ModelTransformation\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.item.ArmorItem\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.Vec3f\n\nclass ModularWorkbenchBlockEntityRenderer : BlockEntityRenderer<ModularWorkbenchBlockEntity> {\n\n    private val bodyModel = BipedEntityModel<AbstractClientPlayerEntity>(TexturedModelData.of(PlayerEntityModel.getTexturedModelData(Dilation.NONE, false), 64, 64).createModel())\n    private val leggingsModel = BipedEntityModel<AbstractClientPlayerEntity>(TexturedModelData.of(PlayerEntityModel.getTexturedModelData(Dilation.NONE, false), 64, 64).createModel())\n\n    init {\n        bodyModel.setVisible(false)\n        leggingsModel.setVisible(false)\n    }\n\n    override fun render(\n        entity: ModularWorkbenchBlockEntity,\n        tickDelta: Float,\n        matrices: MatrixStack,\n        vertexConsumers: VertexConsumerProvider,\n        light: Int,\n        overlay: Int\n    ) {\n        ElytraEntityModel.getTexturedModelData().createModel()\n        val itemStack = entity.inventoryComponent?.inventory?.getStack(2)\n        if (itemStack?.item is IRModularArmorItem) {\n            matrices.run {\n                push()\n                val yOffset = if (itemStack.item is IRModularArmorItem) {\n                    when ((itemStack.item as IRModularArmorItem).slotType) {\n                        EquipmentSlot.HEAD -> 1.0\n                        EquipmentSlot.CHEST -> 1.5\n                        EquipmentSlot.LEGS -> 1.7\n                        EquipmentSlot.FEET -> 2.0\n                        else -> -1.0\n                    }\n                } else 0.0\n                val time = entity.world?.time ?: 1\n                translate(0.5, yOffset, 0.5)\n                multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion((time + tickDelta) * 4))\n                multiply(Vec3f.POSITIVE_Z.getDegreesQuaternion(180f))\n                val lightMapCoords = WorldRenderer.getLightmapCoordinates(entity.world, entity.pos)\n                renderArmor(this, vertexConsumers, itemStack, lightMapCoords)\n                pop()\n            }\n        } else if (itemStack?.item is IRModularItem<*>) {\n            val lightCoord = WorldRenderer.getLightmapCoordinates(entity.world, entity.pos)\n            matrices.run {\n                push()\n                val time = entity.world?.time ?: 1\n                translate(0.5, 0.35, 0.5)\n                multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion((time + tickDelta) * 4))\n                MinecraftClient.getInstance().itemRenderer.renderItem(itemStack, ModelTransformation.Mode.GROUND, lightCoord, overlay, matrices, vertexConsumers, 0)\n                pop()\n            }\n        }\n    }\n\n    private fun renderArmor(\n        matrices: MatrixStack,\n        vertexConsumers: VertexConsumerProvider,\n        itemStack: ItemStack,\n        light: Int\n    ) {\n        val item = itemStack.item as? IRModularArmorItem ?: return\n        val slotType = item.slotType\n        val bipedEntityModel = getArmor(slotType)\n        setVisible(slotType)\n        val rgb = item.getColor(itemStack)\n        val r = (rgb and 0xFF0000 shr 16) / 255f\n        val g = (rgb and 0xFF00 shr 8) / 255f\n        val b = (rgb and 0xFF) / 255f\n        renderArmorParts(\n            matrices, vertexConsumers, light, item, itemStack.hasGlint(), bipedEntityModel, usesSecondLayer(slotType), r, g, b, null\n        )\n        item.getInstalled(itemStack).filter { it.slots.contains(slotType) }.forEach { module ->\n            if (module.hasTexture) {\n                renderArmorParts(\n                    matrices, vertexConsumers, light, item, itemStack.hasGlint(), bipedEntityModel, usesSecondLayer(slotType), r, g, b, module.key\n                )\n                if (module.hasOverlay) {\n                    renderArmorParts(\n                        matrices, vertexConsumers, 15728880, item, itemStack.hasGlint(), bipedEntityModel, usesSecondLayer(slotType), r, g, b, \"${module.key}_overlay\"\n                    )\n                }\n            }\n        }\n    }\n\n    private fun renderArmorParts(\n        matrixStack: MatrixStack,\n        vertexConsumerProvider: VertexConsumerProvider,\n        light: Int,\n        armorItem: ArmorItem,\n        hasGlint: Boolean,\n        bipedEntityModel: BipedEntityModel<AbstractClientPlayerEntity>,\n        secondLayer: Boolean,\n        r: Float, g: Float, b: Float,\n        overlay: String?\n    ) {\n        val vertexConsumer = ItemRenderer.getArmorGlintConsumer(\n            vertexConsumerProvider,\n            RenderLayer.getArmorCutoutNoCull(getArmorTexture(armorItem, secondLayer, overlay)),\n            false,\n            hasGlint\n        )\n        bipedEntityModel.render(matrixStack, vertexConsumer, light, OverlayTexture.DEFAULT_UV, r, g, b, 1.0f)\n    }\n\n    private fun getArmor(slot: EquipmentSlot): BipedEntityModel<AbstractClientPlayerEntity> {\n        return if (usesSecondLayer(slot)) leggingsModel else bodyModel\n    }\n\n    private fun usesSecondLayer(slot: EquipmentSlot): Boolean {\n        return slot == EquipmentSlot.LEGS\n    }\n\n    private fun setVisible(slot: EquipmentSlot) {\n        bodyModel.head.visible = slot == EquipmentSlot.HEAD\n        bodyModel.hat.visible = slot == EquipmentSlot.HEAD\n\n        bodyModel.body.visible = slot == EquipmentSlot.CHEST\n        bodyModel.rightArm.visible = slot == EquipmentSlot.CHEST\n        bodyModel.leftArm.visible = slot == EquipmentSlot.CHEST\n\n        leggingsModel.body.visible = slot == EquipmentSlot.LEGS\n        leggingsModel.rightLeg.visible = slot == EquipmentSlot.LEGS\n        leggingsModel.leftLeg.visible = slot == EquipmentSlot.LEGS\n\n        bodyModel.rightLeg.visible = slot == EquipmentSlot.FEET\n        bodyModel.leftLeg.visible = slot == EquipmentSlot.FEET\n    }\n\n    private fun getArmorTexture(armorItem: ArmorItem, bl: Boolean, string: String?): Identifier {\n        val path = \"textures/models/armor/\" + armorItem.material.name + \"_layer_\" + (if (bl) 2 else 1) + (if (string == null) \"\" else \"_$string\") + \".png\"\n        return ModuleFeatureRenderer.MODULAR_ARMOR_TEXTURE_CACHE.computeIfAbsent(path) { id ->\n            if (string == null) Identifier(id) else identifier(id)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/solarpowerplant/HeliostatBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.solarpowerplant\n\nimport me.steven.indrev.blockentities.BaseBlockEntity\nimport me.steven.indrev.blocks.HeliostatBlock\nimport me.steven.indrev.registry.IRBlockRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\n\nclass HeliostatBlockEntity(pos: BlockPos, state: BlockState) : BaseBlockEntity(IRBlockRegistry.HELIOSTAT_BLOCK_ENTITY, pos, state) {\n\n    var targetBlock: BlockPos = BlockPos.ORIGIN\n\n    var pitch: Float = 0.0f\n    var yaw: Float = 0.0f\n\n    companion object {\n        fun tick(world: World, pos: BlockPos, state: BlockState, blockEntity: HeliostatBlockEntity) {\n            //this doesn't work, i need another way to check if there's no obstruction\n            //val hit = world!!.raycastBlock(pos.toVec3d().add(0.5, 1.5, 0.5), targetBlock.toVec3d().add(0.5, 0.5, 0.5), pos, cachedState.getOutlineShape(world, pos), cachedState)\n            //if (hit !is BlockHitResult || hit.blockPos != targetBlock) return\n            if (!world.isSkyVisible(pos.up())) return\n            val receiver = world.getBlockEntity(blockEntity.targetBlock) as? SolarReceiverBlockEntity ?: return\n            val controller =\n                world.getBlockEntity(receiver.controllerPos) as? SolarPowerPlantTowerBlockEntity ?: return\n            controller.heliostats++\n        }\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        tag.putLong(\"target\", targetBlock.asLong())\n    }\n    \n    override fun fromTag(tag: NbtCompound) {\n        targetBlock = BlockPos.fromLong(tag.getLong(\"target\"))\n        yaw = HeliostatBlock.getYaw(pos, targetBlock)\n        pitch = HeliostatBlock.getPitch(pos, targetBlock)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/solarpowerplant/HeliostatBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.solarpowerplant\n\nimport net.minecraft.block.Blocks\nimport net.minecraft.block.HorizontalConnectingBlock\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.RenderLayers\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.math.Vec3f\n\nclass HeliostatBlockEntityRenderer : BlockEntityRenderer<HeliostatBlockEntity> {\n    override fun render(\n        entity: HeliostatBlockEntity?,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider?,\n        light: Int,\n        overlay: Int\n    ) {\n        if (entity == null) return\n\n        matrices?.run {\n            push()\n            val state = Blocks.GLASS_PANE.defaultState.with(HorizontalConnectingBlock.WEST, true).with(HorizontalConnectingBlock.EAST, true)\n            matrices.translate(0.5, 0.5, 0.5)\n            matrices.multiply(Vec3f.NEGATIVE_Y.getDegreesQuaternion(entity.yaw))\n            matrices.multiply(Vec3f.POSITIVE_X.getDegreesQuaternion(entity.pitch))\n            matrices.translate(-0.5, -0.5, -0.5)\n            val buffer = vertexConsumers?.getBuffer(RenderLayers.getBlockLayer(state))\n            MinecraftClient.getInstance().blockRenderManager.renderBlock(state, entity.pos, entity.world, this, buffer, false, entity.world!!.random)\n            pop()\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/solarpowerplant/SolarPowerPlantTowerBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.solarpowerplant\n\nimport me.steven.indrev.blockentities.BaseBlockEntity\nimport me.steven.indrev.blockentities.Syncable\nimport me.steven.indrev.components.*\nimport me.steven.indrev.components.multiblock.MultiBlockComponent\nimport me.steven.indrev.components.multiblock.definitions.SolarPowerPlantTowerStructureDefinition\nimport me.steven.indrev.registry.IRBlockRegistry\nimport me.steven.indrev.utils.bucket\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\n\nclass SolarPowerPlantTowerBlockEntity(pos: BlockPos, state: BlockState)\n    : BaseBlockEntity(IRBlockRegistry.SOLAR_POWER_PLANT_TOWER_BLOCK_ENTITY, pos, state), Syncable {\n\n    override val guiSyncableComponent = GuiSyncableComponent()\n\n    val temperatureComponent = TemperatureComponent(this, 0.09, 1100..1300, 1500)\n    val multiblockComponent = SolarPowerPlantMultiblockComponent()\n    val fluidComponent = object : FluidComponent({ this }, bucket * 16, 2) {\n        init {\n            this.inputTanks = intArrayOf(0)\n            this.outputTanks = intArrayOf(1)\n            this.unsided = true\n        }\n    }\n\n    var isMarkedForUpdate = false\n\n    init {\n        trackObject(INPUT_TANK_ID, fluidComponent[0])\n        trackObject(OUTPUT_TANK_ID, fluidComponent[1])\n    }\n\n    var heliostats = 0\n\n    companion object {\n        const val INPUT_TANK_ID = 4\n        const val OUTPUT_TANK_ID = 5\n\n        fun tick(world: World, pos: BlockPos, state: BlockState, blockEntity: SolarPowerPlantTowerBlockEntity) {\n            blockEntity.multiblockComponent.tick(world, pos, state)\n            if (blockEntity.multiblockComponent.isBuilt(world, pos, state)) {\n                val limit = blockEntity.heliostats * 6\n                blockEntity.temperatureComponent.tick(blockEntity.temperatureComponent.temperature < limit + (world.random.nextFloat() * 2 - 1) * 10)\n                blockEntity.markForUpdate()\n                blockEntity.heliostats = 0\n            }\n        }\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        temperatureComponent.readNbt(tag)\n        fluidComponent.fromTag(tag)\n        multiblockComponent.readNbt(tag)\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        temperatureComponent.writeNbt(tag)\n        fluidComponent.toTag(tag)\n        multiblockComponent.writeNbt(tag)\n    }\n\n    override fun fromClientTag(tag: NbtCompound) {\n        multiblockComponent.readNbt(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        multiblockComponent.writeNbt(tag)\n    }\n\n    override fun markForUpdate(condition: () -> Boolean) {\n        isMarkedForUpdate = isMarkedForUpdate || condition()\n    }\n\n    inner class SolarPowerPlantMultiblockComponent : MultiBlockComponent({ _, _, _ -> SolarPowerPlantTowerStructureDefinition }) {\n        override fun tick(world: World, pos: BlockPos, blockState: BlockState) {\n            super.tick(world, pos, blockState)\n            SolarPowerPlantTowerStructureDefinition.getSolarReceiverPositions(pos, blockState).forEach { receiverPos ->\n                val blockEntity = world.getBlockEntity(receiverPos) as? SolarReceiverBlockEntity ?: return@forEach\n                blockEntity.controllerPos = pos\n            }\n\n            SolarPowerPlantTowerStructureDefinition.getFluidInputPositions(pos, blockState).forEach { inputPos ->\n\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/solarpowerplant/SolarReceiverBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.solarpowerplant\n\nimport me.steven.indrev.registry.IRBlockRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\n\nclass SolarReceiverBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(IRBlockRegistry.SOLAR_RECEIVER_BLOCK_ENTITY, pos, state) {\n    var controllerPos: BlockPos = BlockPos.ORIGIN\n\n    override fun writeNbt(tag: NbtCompound?) {\n        tag?.putLong(\"controller\", controllerPos.asLong())\n        return super.writeNbt(tag)\n    }\n\n    override fun readNbt(tag: NbtCompound) {\n        super.readNbt(tag)\n        controllerPos = BlockPos.fromLong(tag.getLong(\"controller\"))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/storage/CabinetBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.storage\n\nimport me.steven.indrev.gui.screenhandlers.storage.CabinetScreenHandler\nimport me.steven.indrev.registry.IRBlockRegistry\nimport net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.LootableContainerBlockEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.inventory.Inventories\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.screen.ScreenHandler\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.server.network.ServerPlayerEntity\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.collection.DefaultedList\nimport net.minecraft.util.math.BlockPos\n\nclass CabinetBlockEntity(pos: BlockPos, state: BlockState) : LootableContainerBlockEntity(IRBlockRegistry.CABINET_BLOCK_ENTITY_TYPE, pos, state), ExtendedScreenHandlerFactory {\n\n    private var inventory: DefaultedList<ItemStack> = DefaultedList.ofSize(27, ItemStack.EMPTY)\n\n    override fun size(): Int = 27\n\n    override fun getContainerName(): Text = TranslatableText(\"block.indrev.cabinet\")\n\n    override fun createScreenHandler(syncId: Int, playerInventory: PlayerInventory): ScreenHandler {\n        return CabinetScreenHandler(syncId, playerInventory, ScreenHandlerContext.create(world, pos))\n    }\n\n    override fun getInvStackList(): DefaultedList<ItemStack> = inventory\n\n    override fun setInvStackList(list: DefaultedList<ItemStack>) {\n        inventory = list\n    }\n\n    override fun writeScreenOpeningData(player: ServerPlayerEntity?, buf: PacketByteBuf) {\n        buf.writeBlockPos(pos)\n    }\n\n    override fun readNbt(tag: NbtCompound?) {\n        super.readNbt(tag)\n        inventory = DefaultedList.ofSize(size(), ItemStack.EMPTY)\n        if (!deserializeLootTable(tag)) {\n            Inventories.readNbt(tag, inventory)\n        }\n    }\n\n    override fun writeNbt(tag: NbtCompound) {\n        super.writeNbt(tag)\n        if (!serializeLootTable(tag)) {\n            Inventories.writeNbt(tag, inventory)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/storage/ChargePadBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.storage\n\nimport com.google.common.collect.Iterables\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.asMutableList\nimport me.steven.indrev.utils.energyOf\nimport net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext\nimport net.minecraft.block.BlockState\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Box\nimport team.reborn.energy.api.EnergyStorage\n\nclass ChargePadBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) : MachineBlockEntity<BasicMachineConfig>(tier, MachineRegistry.CHARGE_PAD_REGISTRY, pos, state) {\n\n    init {\n        this.inventoryComponent = inventory(this) {\n            input { slot = 0 }\n            output { slot = 0 }\n        }\n    }\n\n    override val syncToWorld: Boolean = true\n\n    override val maxOutput: Long = 16384\n    override val maxInput: Long = 16384\n    override val energyCapacity: Long = 0L\n\n    val energyIo = ChargePadEnergyStorage()\n\n    var hasCollided = false\n\n    private fun getItemEnergyIo() = energyOf(inventoryComponent!!.inventory, 0)\n\n    override fun fromClientTag(tag: NbtCompound) {\n        inventoryComponent!!.readNbt(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        inventoryComponent!!.writeNbt(tag)\n    }\n\n    inner class ChargePadEnergyStorage : EnergyStorage {\n        override fun getAmount(): Long = getItemEnergyIo()?.amount ?: 0\n\n        override fun getCapacity(): Long = getItemEnergyIo()?.capacity ?: 0\n\n        override fun insert(amount: Long, transaction: TransactionContext?): Long {\n            val handlers = if (hasCollided) {\n                val collidedEntityItems = collectItemIos()\n                if (collidedEntityItems.isEmpty()) hasCollided = false\n                getItemEnergyIo()?.also { collidedEntityItems.add(it) }\n                collidedEntityItems\n            } else\n                getItemEnergyIo().let { if (it == null) emptyList() else listOf(it) }\n\n            if (handlers.isEmpty()) return 0\n\n            val max = amount.coerceAtMost(maxOutput)\n            var remainder = max\n            handlers.forEach { handler ->\n                if (remainder > 0)\n                    remainder -= handler.insert(remainder, transaction)\n                else return 0\n            }\n            return max - remainder\n        }\n\n        override fun extract(maxAmount: Long, transaction: TransactionContext?): Long = 0\n\n        override fun supportsExtraction(): Boolean = false\n\n        override fun supportsInsertion(): Boolean = true\n\n        private fun collectItemIos(): MutableList<EnergyStorage> {\n            return world!!.getEntitiesByClass(LivingEntity::class.java, Box(pos)) { true }.flatMap { entity ->\n                when (entity) {\n                    is PlayerEntity -> Iterables.concat(entity.inventory.armor.mapIndexed { index, _ ->  energyOf(entity.inventory, 36 + index) },\n                        listOf(energyOf(entity.inventory, entity.inventory.selectedSlot), energyOf(entity.inventory, 40)))\n                    else -> emptyList()\n                }\n            }.filterNotNull().asMutableList()\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/storage/ChargePadBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.storage\n\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.WorldRenderer\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.render.model.json.ModelTransformation\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.math.Vec3f\nimport kotlin.math.abs\nimport kotlin.math.sin\n\nclass ChargePadBlockEntityRenderer : BlockEntityRenderer<ChargePadBlockEntity> {\n    override fun render(\n        entity: ChargePadBlockEntity?,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider?,\n        light: Int,\n        overlay: Int\n    ) {\n        val inventory = entity?.inventoryComponent?.inventory\n        val stack = inventory?.getStack(0)\n        if (stack?.isEmpty == false) {\n            val facing = entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING].rotateYClockwise()\n            var x = abs(facing.offsetX.toFloat()) * 0.5\n            var z = abs(facing.offsetZ.toFloat()) * 0.5\n            if (facing.offsetX == -1)\n                z = x\n            else if (facing.offsetZ == 1) x = z\n            when (facing.axis) {\n                Direction.Axis.X -> z += 0.2\n                Direction.Axis.Z -> x += 0.2\n                else -> return\n            }\n            matrices?.run {\n                push()\n                val time = entity.world?.time ?: 1\n                val offset = sin((time + tickDelta) / 16.0) / 32.0\n                translate(x, 1.1 + offset, z)\n                multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion((time + tickDelta) * 4))\n                scale(0.5f, 0.5f, 0.5f)\n                val lightAbove = WorldRenderer.getLightmapCoordinates(entity.world, entity.pos.up())\n                MinecraftClient.getInstance().itemRenderer.renderItem(stack, ModelTransformation.Mode.GROUND, lightAbove, overlay, this, vertexConsumers, 0)\n                pop()\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/storage/LazuliFluxContainerBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.storage\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.api.sideconfigs.SideConfiguration\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blocks.machine.FacingMachineBlock\nimport me.steven.indrev.config.LFCConfig\nimport me.steven.indrev.inventories.inventory\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.energyOf\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport team.reborn.energy.api.EnergyStorageUtil\nimport kotlin.math.floor\n\nclass LazuliFluxContainerBlockEntity(tier: Tier, pos: BlockPos, state: BlockState) :\n    MachineBlockEntity<LFCConfig>(tier, MachineRegistry.LAZULI_FLUX_CONTAINER_REGISTRY, pos, state) {\n\n    init {\n        this.inventoryComponent = inventory(this) {\n            input { 0 filter { stack -> energyOf(stack) != null } }\n        }\n    }\n\n    override val syncToWorld: Boolean = true\n\n    override val maxInput: Long = config.maxInput\n    override val maxOutput: Long = config.maxOutput\n\n    val transferConfig: SideConfiguration = SideConfiguration(ConfigurationType.ENERGY)\n\n    override val storage = LazuliFluxContainerEnergyStorage()\n\n    private var clientLastRenderWidth = 0f\n\n    override fun machineTick() {\n        if (world?.isClient == true) return\n        val inventory = inventoryComponent?.inventory ?: return\n        val itemIo = energyOf(inventory, 0)\n        if (itemIo != null)\n            EnergyStorageUtil.move(storage.getSideStorage(null), itemIo, maxOutput, null)\n    }\n\n    private fun update() {\n        val width = floor((((energy.toFloat() / energyCapacity.toFloat()) * 0.5f) + 0.25f) * 16)\n        if (width != clientLastRenderWidth) {\n            sync()\n            clientLastRenderWidth = width\n        }\n    }\n\n    override fun isConfigurable(type: ConfigurationType): Boolean {\n        return type == ConfigurationType.ENERGY\n    }\n\n    override fun getCurrentConfiguration(type: ConfigurationType): SideConfiguration {\n        return transferConfig\n    }\n\n    override fun applyDefault(state: BlockState, type: ConfigurationType, configuration: MutableMap<Direction, TransferMode>) {\n        val facing = state[FacingMachineBlock.FACING]\n        Direction.values().forEach { dir ->\n            if (dir == facing && tier != Tier.CREATIVE) configuration[dir] = TransferMode.INPUT\n            else configuration[dir] = TransferMode.OUTPUT\n        }\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        transferConfig.writeNbt(tag)\n        super.toTag(tag)\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        super.fromTag(tag)\n        transferConfig.readNbt(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        tag.putLong(\"energy\", energy)\n        transferConfig.writeNbt(tag)\n    }\n\n    override fun fromClientTag(tag: NbtCompound) {\n        transferConfig.readNbt(tag)\n        energy = tag.getLong(\"energy\")\n    }\n\n    inner class LazuliFluxContainerEnergyStorage : MachineEnergyStorage() {\n        override fun getMaxInsert(side: Direction?): Long {\n            return if (side == null || transferConfig.canInput(side)) maxInput\n            else 0\n        }\n\n        override fun getMaxExtract(side: Direction?): Long {\n            return if (side == null || transferConfig.canOutput(side)) maxOutput\n            else 0\n        }\n\n        override fun onFinalCommit() {\n            super.onFinalCommit()\n            update()\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/storage/LazuliFluxContainerBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.storage\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.OverlayTexture\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.screen.PlayerScreenHandler\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.math.Vec3f\nimport kotlin.math.absoluteValue\nimport kotlin.math.floor\nimport kotlin.math.sin\n\nclass LazuliFluxContainerBlockEntityRenderer : BlockEntityRenderer<LazuliFluxContainerBlockEntity> {\n    override fun render(\n        entity: LazuliFluxContainerBlockEntity?,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider?,\n        light: Int,\n        overlay: Int\n    ) {\n        entity ?: return\n        val width = ((entity.energy.toFloat() / entity.energyCapacity.toFloat()) * 0.5f) + 0.25f\n        val sprite = MinecraftClient.getInstance().getSpriteAtlas(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE).apply(identifier(\"block/lazuli_flux_container_lf_level\"))\n        val color: Long = (255 shl 24 or when (entity.tier) {\n            Tier.MK1 -> 0xffbb19\n            Tier.MK2 -> 0x5d3dff\n            Tier.MK3 -> 0xfd47ff\n            else -> 0xff4070\n        }).toLong()\n        val maxX = floor((width * 16)) / 16f\n        val time = entity.world!!.time\n        matrices?.run {\n            val offset = 0.001\n            push()\n            translate(-0.0, 0.0, -offset)\n            drawOverlay(this, 0.25f, 0f, maxX, 1f, color, sprite, vertexConsumers, Direction.NORTH, width, tickDelta, time)\n            translate(0.0, 0.0, offset)\n\n            translate(0.5, 0.5, 0.5)\n            multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(90f))\n            translate(-0.5, -0.5, -0.5)\n            translate(0.0, 0.0, -offset)\n            drawOverlay(this, 0.25f, 0f, maxX, 1f, color, sprite, vertexConsumers, Direction.EAST, width, tickDelta, time)\n            translate(0.0, 0.0, offset)\n\n            translate(0.5, 0.5, 0.5)\n            multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(90f))\n            translate(-0.5, -0.5, -0.5)\n            translate(-0.0, 0.0, -offset)\n            drawOverlay(this, 0.25f, 0f, maxX, 1f, color, sprite, vertexConsumers, Direction.SOUTH, width, tickDelta, time)\n            translate(0.0, 0.0, offset)\n\n            translate(0.5, 0.5, 0.5)\n            multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(90f))\n            translate(-0.5, -0.5, -0.5)\n            translate(0.0, 0.0, -offset)\n            drawOverlay(this, 0.25f, 0f, maxX, 1f, color, sprite, vertexConsumers, Direction.WEST, width, tickDelta, time)\n            translate(0.0, 0.0, offset)\n            pop()\n        }\n    }\n\n    private fun drawOverlay(matrices: MatrixStack, x1: Float, y1: Float, x2: Float, y2: Float, color: Long, sprite: Sprite, vertexConsumers: VertexConsumerProvider?, direction: Direction, width: Float, tickDelta: Float, time: Long) {\n        val matrix = matrices.peek().positionMatrix\n        \n        var xx1 = x1\n        var xx2 = x2\n        var yy1 = x1\n        var yy2 = x2\n\n        if (x1 < x2) {\n            xx1 = x2\n            xx2 = x1\n        }\n\n        if (y1 < y2) {\n            yy1 = y2\n            yy2 = y1\n        }\n\n        val a = (color shr 24 and 255) / 255.0f\n        val r = (color shr 16 and 255) / 255.0f\n        val g = (color shr 8 and 255) / 255.0f\n        val b = (color and 255) / 255.0f\n\n        val vec = direction.unitVector\n        var maxU = sprite.getFrameU(4.0)\n        var minU = sprite.getFrameU(floor(width.toDouble() * 16))\n\n        val normal = matrices.peek().normalMatrix\n        vertexConsumers?.getBuffer(RenderLayer.getEntityTranslucent(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE))?.run {\n\n            fun vertex(x: Float, y: Float, u: Float, v: Float, alpha: Float = a) {\n                this@run.vertex(matrix, x, y, 0.0f).color(r, g, b, alpha).texture(u, v).overlay(OverlayTexture.DEFAULT_UV).light(EMISSIVE_LIGHT).normal(normal, vec.x, vec.y, vec.z).next()\n            }\n\n            vertex(xx1, yy1, minU, sprite.minV)\n            vertex(xx1, yy2, minU, sprite.maxV)\n            vertex(xx2, yy2, maxU, sprite.maxV)\n            vertex(xx1, yy1, minU, sprite.minV)\n            vertex(xx1, yy1, minU, sprite.minV)\n            vertex(xx2, yy2, maxU, sprite.maxV)\n            vertex(xx2, yy1, maxU, sprite.minV)\n            vertex(xx1, yy1, minU, sprite.minV)\n\n            if (width < 0.75 && width > 0.25) {\n                xx2 = xx1\n                xx1 = (floor(width * 16) + 1) / 16f\n                val opacity = sin((time + tickDelta) / 8).absoluteValue\n                maxU = minU\n                minU = sprite.getFrameU(floor(width.toDouble() * 16) + 1)\n                vertex(xx1, yy1, minU, sprite.minV, opacity)\n                vertex(xx1, yy2, minU, sprite.maxV, opacity)\n                vertex(xx2, yy2, maxU, sprite.maxV, opacity)\n                vertex(xx1, yy1, minU, sprite.minV, opacity)\n                vertex(xx1, yy1, minU, sprite.minV, opacity)\n                vertex(xx2, yy2, maxU, sprite.maxV, opacity)\n                vertex(xx2, yy1, maxU, sprite.minV, opacity)\n                vertex(xx1, yy1, minU, sprite.minV, opacity)\n            }\n        }\n    }\n\n    companion object {\n        private const val EMISSIVE_LIGHT = 15728880\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/storage/TankBlockEntity.kt",
    "content": "package me.steven.indrev.blockentities.storage\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.blockentities.BaseBlockEntity\nimport me.steven.indrev.blockentities.Syncable\nimport me.steven.indrev.blocks.misc.TankBlock\nimport me.steven.indrev.components.FluidComponent\nimport me.steven.indrev.registry.IRBlockRegistry\nimport me.steven.indrev.utils.bucket\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil\nimport net.fabricmc.fabric.api.transfer.v1.storage.base.CombinedStorage\nimport net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\n\nclass TankBlockEntity(pos: BlockPos, state: BlockState) : BaseBlockEntity(IRBlockRegistry.TANK_BLOCK_ENTITY, pos, state), Syncable {\n    val fluidComponent = object : FluidComponent({ this }, bucket * 8) {\n        init {\n            this.unsided = true\n        }\n    }\n\n    var isMarkedForUpdate: Boolean = true\n\n    companion object {\n        fun tick(world: World, pos: BlockPos, state: BlockState, blockEntity: TankBlockEntity) {\n            if (world.isClient) return\n            if (blockEntity.isMarkedForUpdate) {\n                blockEntity.markDirty()\n                blockEntity.sync()\n                blockEntity.isMarkedForUpdate = false\n            }\n            if (!state[TankBlock.DOWN]) return\n            val down = world.getBlockEntity(pos.down()) as? TankBlockEntity ?: return\n            val volume = StorageUtil.move(blockEntity.fluidComponent, down.fluidComponent, { true }, Long.MAX_VALUE, null)\n            if (volume > 0) {\n                down.isMarkedForUpdate = true\n                blockEntity.isMarkedForUpdate = true\n            }\n        }\n    }\n\n    override fun toTag(tag: NbtCompound) {\n        fluidComponent.toTag(tag)\n    }\n\n    override fun fromTag(tag: NbtCompound) {\n        fluidComponent.fromTag(tag)\n    }\n\n    override fun toClientTag(tag: NbtCompound) {\n        fluidComponent.toTag(tag)\n    }\n\n    override fun fromClientTag(tag: NbtCompound) {\n        fluidComponent.fromTag(tag)\n    }\n\n    override fun markDirty() {\n        if (world != null) {\n            world!!.markDirty(pos)\n        }\n    }\n\n    override fun markForUpdate(condition: () -> Boolean) {\n        isMarkedForUpdate = isMarkedForUpdate || condition()\n    }\n\n    class CombinedTankStorage : CombinedStorage<FluidVariant, FluidComponent>(mutableListOf<FluidComponent>()) {\n\n        var initialFluid = FluidVariant.blank()\n\n        fun add(tank: TankBlockEntity): Boolean {\n            val invFluid = tank.fluidComponent[0]\n            if (initialFluid.isBlank && !invFluid.isEmpty) {\n                initialFluid = invFluid.variant\n            } else if (!invFluid.isEmpty && initialFluid != invFluid.variant) {\n                IndustrialRevolution.LOGGER.debug(\"Found connected tanks with mismatching fluids @ ${tank.pos}\")\n                return false\n            }\n            parts.add(tank.fluidComponent)\n            return true\n        }\n\n        override fun insert(resource: FluidVariant?, maxAmount: Long, transaction: TransactionContext?): Long {\n            if (!initialFluid.isBlank && resource != initialFluid) return 0\n            return super.insert(resource, maxAmount, transaction)\n        }\n\n        override fun extract(resource: FluidVariant?, maxAmount: Long, transaction: TransactionContext?): Long {\n            if (!initialFluid.isBlank && resource != initialFluid) return 0\n            return super.extract(resource, maxAmount, transaction)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blockentities/storage/TankBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.blockentities.storage\n\nimport alexiil.mc.lib.attributes.fluid.render.FluidRenderFace\nimport me.steven.indrev.blocks.misc.TankBlock\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.util.math.MatrixStack\n\nclass TankBlockEntityRenderer : BlockEntityRenderer<TankBlockEntity> {\n    override fun render(\n        entity: TankBlockEntity,\n        tickDelta: Float,\n        matrices: MatrixStack?,\n        vertexConsumers: VertexConsumerProvider?,\n        light: Int,\n        overlay: Int\n    ) {\n        val fluidComponent = entity.fluidComponent\n        val volume = fluidComponent[0]\n        if (volume.isEmpty) return\n        val fluid = volume.amount\n        val maxFluid = fluidComponent.limit\n        var percent = fluid.toFloat() / maxFluid.toFloat()\n        val maxHeight = if (entity.cachedState[TankBlock.UP]) 16 else 14\n        percent = (percent * maxHeight).toInt() / 16f\n        val yHeight = percent.toDouble().coerceAtLeast(0.1)\n        val faces = mutableListOf(\n            FluidRenderFace.createFlatFaceZ(0.9, 0.0, 0.1, 0.1, yHeight, 0.1, 1.0, true, false),\n            FluidRenderFace.createFlatFaceZ(0.1, 0.0, 0.9, 0.9, yHeight, 0.9, 1.0, true, false),\n            FluidRenderFace.createFlatFaceX(0.1, 0.0, 0.1, 0.1, yHeight, 0.9, 1.0, false, false),\n            FluidRenderFace.createFlatFaceX(0.9, 0.0, 0.9, 0.9, yHeight, 0.1, 1.0, false, false),\n        )\n\n        var renderFluidTop = true\n        if (entity.cachedState[TankBlock.UP]) {\n            val aboveTank = entity.world!!.getBlockEntity(entity.pos.up()) as? TankBlockEntity\n            renderFluidTop = aboveTank?.fluidComponent?.get(0)?.variant != volume.variant\n        }\n        if (renderFluidTop) {\n            faces.add(FluidRenderFace.createFlatFaceY(0.1, yHeight, 0.1, 0.9, yHeight, 0.9, 1.0, true, false))\n        }\n\n        for (face in faces) {\n            face.light = light;\n        }\n\n        volume.render(faces, vertexConsumers, matrices);\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/HeliostatBlock.kt",
    "content": "package me.steven.indrev.blocks\n\nimport me.steven.indrev.blockentities.solarpowerplant.HeliostatBlockEntity\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockEntityProvider\nimport net.minecraft.block.BlockRenderType\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.block.entity.BlockEntityTicker\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.math.MathHelper\nimport net.minecraft.world.World\n\nclass HeliostatBlock(settings: Settings) : Block(settings), BlockEntityProvider {\n\n    override fun getRenderType(state: BlockState?): BlockRenderType = BlockRenderType.ENTITYBLOCK_ANIMATED\n\n    override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = HeliostatBlockEntity(pos, state)\n\n    override fun <T : BlockEntity?> getTicker(\n        world: World,\n        state: BlockState?,\n        type: BlockEntityType<T>?\n    ): BlockEntityTicker<T>? {\n        return if (world.isClient) null\n        else return BlockEntityTicker { world, pos, state, blockEntity ->\n            HeliostatBlockEntity.tick(world, pos, state, blockEntity as? HeliostatBlockEntity ?: return@BlockEntityTicker)\n        }\n    }\n\n    companion object {\n        private const val RAD2DEG = 57.2957763671875\n\n        fun getYaw(origin: BlockPos, target: BlockPos): Float {\n            val xOffset = target.x + 0.0 - origin.x.toDouble() + 0.0\n            val zOffset = target.z + 0.0 - origin.z.toDouble() + 0.0\n            return MathHelper.wrapDegrees((MathHelper.atan2(zOffset, xOffset) * RAD2DEG).toFloat() - 90.0f)\n        }\n\n        fun getPitch(origin: BlockPos, target: BlockPos): Float {\n            val xOffset = target.x + 0.0f - origin.x.toFloat() + 0.0f\n            val yOffset = target.y + 0.0f - origin.y.toFloat() + 0.0f\n            val zOffset = target.z + 0.0f - origin.z.toFloat() + 0.0f\n            val g = MathHelper.sqrt(xOffset * xOffset + zOffset * zOffset).toDouble()\n            return MathHelper.wrapDegrees((-(MathHelper.atan2(yOffset.toDouble(), g) * RAD2DEG)).toFloat())\n        }\n\n\n        fun findConnectingHeliostats(origin: BlockPos, world: World, scanned: MutableSet<Long>, positions: MutableSet<Long>) {\n            Direction.values().forEach { dir ->\n                val off = origin.offset(dir)\n                if (scanned.add(off.asLong()) && world.testBlockState(off) { state -> state.block is HeliostatBlock }) {\n                    positions.add(off.asLong())\n                    findConnectingHeliostats(off, world, scanned, positions)\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/CapsuleBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.blockentities.laser.CapsuleBlockEntity\nimport me.steven.indrev.recipes.machines.LaserRecipe\nimport net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockEntityProvider\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.Material\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.block.entity.BlockEntityTicker\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Hand\nimport net.minecraft.util.ItemScatterer\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.BlockView\nimport net.minecraft.world.World\n\nclass CapsuleBlock : Block(FabricBlockSettings.of(Material.GLASS).requiresTool().nonOpaque().strength(1f, 1f)), BlockEntityProvider {\n    override fun onUse(\n        state: BlockState?,\n        world: World,\n        pos: BlockPos?,\n        player: PlayerEntity,\n        hand: Hand?,\n        hit: BlockHitResult?\n    ): ActionResult {\n        if (!world.isClient && hand == Hand.MAIN_HAND) {\n            val stack = player.getStackInHand(hand)\n            val recipe = LaserRecipe.TYPE.getMatchingRecipe(world as ServerWorld, stack)\n                .firstOrNull { it.matches(stack, emptyList()) }\n            val blockEntity = world.getBlockEntity(pos) as? CapsuleBlockEntity ?: return ActionResult.PASS\n            if (recipe != null && blockEntity.inventory[0].isEmpty) {\n                player.setStackInHand(hand, ItemStack.EMPTY)\n                blockEntity.inventory[0] = stack\n            } else if (!blockEntity.inventory[0].isEmpty) {\n                val itemStack = blockEntity.inventory[0]\n                itemStack.nbt = null\n                player.inventory?.insertStack(itemStack)\n                blockEntity.inventory[0] = ItemStack.EMPTY\n            } else\n                return ActionResult.PASS\n            world.updateNeighbors(pos, this)\n            blockEntity.markDirty()\n            blockEntity.sync()\n            return ActionResult.SUCCESS\n        }\n        return ActionResult.PASS\n    }\n\n    override fun onStateReplaced(\n        state: BlockState?,\n        world: World,\n        pos: BlockPos,\n        newState: BlockState?,\n        moved: Boolean\n    ) {\n        val blockEntity = world.getBlockEntity(pos) as? CapsuleBlockEntity\n        if (blockEntity != null)\n            ItemScatterer.spawn(world, pos, blockEntity.inventory)\n        super.onStateReplaced(state, world, pos, newState, moved)\n    }\n\n    override fun emitsRedstonePower(state: BlockState?): Boolean = true\n\n    override fun getWeakRedstonePower(\n        state: BlockState?,\n        world: BlockView,\n        pos: BlockPos,\n        direction: Direction?\n    ): Int {\n        val blockEntity = world.getBlockEntity(pos) as? CapsuleBlockEntity ?: return 0\n        if (blockEntity.world!!.isClient) return 0\n        val stack = blockEntity.inventory[0]\n        val recipe = LaserRecipe.TYPE.getMatchingRecipe(world as ServerWorld, stack)\n            .firstOrNull { it.matches(stack, emptyList()) }\n        return if (recipe == null) 15\n        else 0\n    }\n\n    override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = CapsuleBlockEntity(pos, state)\n\n    override fun <T : BlockEntity?> getTicker(\n        world: World,\n        state: BlockState?,\n        type: BlockEntityType<T>?\n    ): BlockEntityTicker<T>? {\n        return if (world.isClient) null\n        else BlockEntityTicker { world, pos, state, blockEntity ->\n            CapsuleBlockEntity.tick(world, pos, state, blockEntity as CapsuleBlockEntity)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/ChargePadBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.storage.ChargePadBlockEntity\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.energyOf\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.ShapeContext\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.entity.Entity\nimport net.minecraft.entity.decoration.ArmorStandEntity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.Hand\nimport net.minecraft.util.function.BooleanBiFunction\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.shape.VoxelShape\nimport net.minecraft.util.shape.VoxelShapes\nimport net.minecraft.world.BlockView\nimport net.minecraft.world.World\nimport java.util.*\nimport java.util.stream.Stream\n\nclass ChargePadBlock(registry: MachineRegistry, settings: Settings, tier: Tier) :\n    HorizontalFacingMachineBlock(registry, settings, tier, null, null) {\n\n    override fun getOutlineShape(\n        state: BlockState,\n        world: BlockView?,\n        pos: BlockPos?,\n        context: ShapeContext?\n    ): VoxelShape =\n        when (state[HORIZONTAL_FACING]) {\n            Direction.NORTH -> FACING_NORTH\n            Direction.SOUTH -> FACING_SOUTH\n            Direction.EAST -> FACING_EAST\n            Direction.WEST -> FACING_WEST\n            else -> FACING_NORTH\n        }\n\n    override fun onUse(state: BlockState?, world: World, pos: BlockPos?, player: PlayerEntity?, hand: Hand?, hit: BlockHitResult?): ActionResult {\n        val blockEntity = world.getBlockEntity(pos) as? ChargePadBlockEntity ?: return ActionResult.PASS\n        val inventory = blockEntity.inventoryComponent?.inventory ?: return ActionResult.PASS\n        val machineStack = inventory.getStack(0)\n        if (!machineStack.isEmpty) {\n            player?.inventory?.insertStack(machineStack)\n            return ActionResult.SUCCESS\n        }\n        val handStack = player?.mainHandStack\n        if (energyOf(player?.inventory, player?.inventory?.selectedSlot?: return ActionResult.PASS) != null) {\n            inventory.setStack(0, handStack)\n            player.setStackInHand(Hand.MAIN_HAND, ItemStack.EMPTY)\n            return ActionResult.SUCCESS\n        }\n        return ActionResult.PASS\n    }\n\n    override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? {\n        super.getPlacementState(ctx)\n        return this.defaultState.with(HORIZONTAL_FACING, ctx?.playerFacing)\n    }\n\n    override fun onEntityCollision(state: BlockState?, world: World?, pos: BlockPos?, entity: Entity?) {\n        if (entity is PlayerEntity || entity is ArmorStandEntity) {\n            val blockEntity = world?.getBlockEntity(pos) as? ChargePadBlockEntity ?: return\n            blockEntity.hasCollided = true\n        }\n    }\n\n    override fun appendTooltip(stack: ItemStack?, view: BlockView?, tooltip: MutableList<Text>?, options: TooltipContext?) {\n        super.appendTooltip(stack, view, tooltip, options)\n        tooltip?.add(TranslatableText(\"block.indrev.charge_pad_mk4.tooltip\").formatted(Formatting.BLUE, Formatting.ITALIC))\n    }\n\n    @Environment(EnvType.CLIENT)\n    override fun randomDisplayTick(state: BlockState?, world: World, pos: BlockPos, random: Random?) {\n    }\n\n    companion object {\n        private val FACING_NORTH = Stream.of(\n            createCuboidShape(1.0, 0.0, 1.0, 15.0, 0.1, 15.0),\n            createCuboidShape(1.25, 0.0, 1.25, 14.75, 0.3, 14.75),\n            createCuboidShape(7.0, 0.0, 2.0, 9.0, 15.0, 4.0),\n            createCuboidShape(8.0, 15.0, 2.0, 9.0, 15.5, 3.0),\n            createCuboidShape(7.0, 15.0, 2.0, 8.0, 15.5, 3.0),\n            createCuboidShape(7.0, 15.0, 3.0, 8.0, 15.2, 4.0),\n            createCuboidShape(8.0, 15.0, 3.0, 9.0, 15.2, 4.0)\n        ).reduce { v1: VoxelShape?, v2: VoxelShape? -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null)\n        private val FACING_SOUTH: VoxelShape = Stream.of(\n            createCuboidShape(1.0, 0.0, 1.0, 15.0, 0.1, 15.0),\n            createCuboidShape(1.25, 0.0, 1.25, 14.75, 0.3, 14.75),\n            createCuboidShape(7.0, 0.0, 12.0, 9.0, 15.0, 14.0),\n            createCuboidShape(7.0, 15.0, 13.0, 8.0, 15.5, 14.0),\n            createCuboidShape(8.0, 15.0, 13.0, 9.0, 15.5, 14.0),\n            createCuboidShape(8.0, 15.0, 12.0, 9.0, 15.2, 13.0),\n            createCuboidShape(7.0, 15.0, 12.0, 8.0, 15.2, 13.0)\n        ).reduce { v1: VoxelShape?, v2: VoxelShape? -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null)\n        private val FACING_WEST = Stream.of(\n            createCuboidShape(1.0, 0.0, 1.0, 15.0, 0.1, 15.0),\n            createCuboidShape(1.25, 0.0, 1.25, 14.75, 0.3, 14.75),\n            createCuboidShape(2.0, 0.0, 7.0, 4.0, 15.0, 9.0),\n            createCuboidShape(2.0, 15.0, 7.0, 3.0, 15.5, 8.0),\n            createCuboidShape(2.0, 15.0, 8.0, 3.0, 15.5, 9.0),\n            createCuboidShape(3.0, 15.0, 8.0, 4.0, 15.2, 9.0),\n            createCuboidShape(3.0, 15.0, 7.0, 4.0, 15.2, 8.0)\n        ).reduce { v1: VoxelShape?, v2: VoxelShape? -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null)\n        private val FACING_EAST = Stream.of(\n            createCuboidShape(1.0, 0.0, 1.0, 15.0, 0.1, 15.0),\n            createCuboidShape(1.25, 0.0, 1.25, 14.75, 0.3, 14.75),\n            createCuboidShape(12.0, 0.0, 7.0, 14.0, 15.0, 9.0),\n            createCuboidShape(13.0, 15.0, 8.0, 14.0, 15.5, 9.0),\n            createCuboidShape(13.0, 15.0, 7.0, 14.0, 15.5, 8.0),\n            createCuboidShape(12.0, 15.0, 7.0, 13.0, 15.2, 8.0),\n            createCuboidShape(12.0, 15.0, 8.0, 13.0, 15.2, 9.0)\n        ).reduce { v1: VoxelShape?, v2: VoxelShape? -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/DirtOxygenatorBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.registry.MachineRegistry\nimport net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings\nimport net.minecraft.block.BlockState\nimport net.minecraft.item.ItemPlacementContext\n\nclass DirtOxygenatorBlock(registry: MachineRegistry, settings: FabricBlockSettings)\n    : FacingMachineBlock(registry, settings, Tier.MK1, IRConfig.machines.dirtOxygenator, null) {\n\n    override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? {\n        return this.defaultState.with(FACING, ctx?.playerLookDirection)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/DrillBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.blockentities.miningrig.DrillBlockEntity\nimport me.steven.indrev.registry.IRBlockRegistry\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockEntityProvider\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.Blocks\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.block.entity.BlockEntityTicker\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.item.ItemStack\nimport net.minecraft.state.StateManager\nimport net.minecraft.state.property.BooleanProperty\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Hand\nimport net.minecraft.util.ItemScatterer\nimport net.minecraft.util.StringIdentifiable\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.BlockView\nimport net.minecraft.world.World\nimport net.minecraft.world.WorldAccess\nimport net.minecraft.world.WorldView\n\nopen class DrillBlock private constructor(settings: Settings, val part: DrillPart) : Block(settings) {\n\n    init {\n        this.defaultState = stateManager.defaultState.with(WORKING, false)\n    }\n\n    override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {\n        val middle = ctx.world.getBlockState(ctx.blockPos.up())\n        val top = ctx.world.getBlockState(ctx.blockPos.up(2))\n        return if (middle.canReplace(ctx) && top.canReplace(ctx)) defaultState else null\n    }\n\n    override fun onPlaced(\n        world: World,\n        pos: BlockPos,\n        state: BlockState,\n        placer: LivingEntity?,\n        itemStack: ItemStack?\n    ) {\n        world.setBlockState(pos.up(2), DRILL_TOP.defaultState)\n        world.setBlockState(pos.up(), DRILL_MIDDLE.defaultState)\n    }\n\n    override fun onUse(\n        state: BlockState,\n        world: World,\n        pos: BlockPos,\n        player: PlayerEntity?,\n        hand: Hand?,\n        hit: BlockHitResult?\n    ): ActionResult {\n        if (!world.isClient) {\n            val blockEntity = world.getBlockEntity(part.getBlockEntityPos(pos)) as? DrillBlockEntity ?: return ActionResult.PASS\n            player?.openHandledScreen(blockEntity)\n        }\n        return ActionResult.CONSUME\n    }\n\n    override fun getStateForNeighborUpdate(\n        state: BlockState,\n        direction: Direction?,\n        newState: BlockState,\n        world: WorldAccess,\n        pos: BlockPos,\n        posFrom: BlockPos?\n    ): BlockState {\n        return if (newState.block is DrillBlock) state.with(WORKING, newState[WORKING])\n        else if (!part.test(world, pos)) Blocks.AIR.defaultState\n        else state\n    }\n\n    override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {\n        if (!world.isClient && player.isCreative && part != DrillPart.BOTTOM) {\n            val bottom = part.getBlockEntityPos(pos)\n            world.setBlockState(bottom, Blocks.AIR.defaultState)\n        }\n        super.onBreak(world, pos, state, player)\n    }\n\n    override fun getPickStack(world: BlockView, pos: BlockPos, state: BlockState): ItemStack = ItemStack(DRILL_BOTTOM)\n\n    override fun appendProperties(builder: StateManager.Builder<Block, BlockState>?) {\n        builder?.add(WORKING)\n    }\n\n    enum class DrillPart(val str: String) : StringIdentifiable {\n        TOP(\"top\") {\n            override fun test(world: WorldView, pos: BlockPos): Boolean {\n                return world.getBlockState(pos.down()).isOf(DRILL_MIDDLE)\n                        && world.getBlockState(pos.down(2)).isOf(DRILL_BOTTOM)\n            }\n\n            override fun getBlockEntityPos(pos: BlockPos): BlockPos = pos.down(2)\n        },\n        MIDDLE(\"middle\") {\n            override fun test(world: WorldView, pos: BlockPos): Boolean {\n                return world.getBlockState(pos.up()).isOf(DRILL_TOP)\n                        && world.getBlockState(pos.down()).isOf(DRILL_BOTTOM)\n            }\n            override fun getBlockEntityPos(pos: BlockPos): BlockPos = pos.down()\n        },\n        BOTTOM(\"bottom\") {\n            override fun test(world: WorldView, pos: BlockPos): Boolean {\n                return world.getBlockState(pos.up()).isOf(DRILL_MIDDLE)\n                        && world.getBlockState(pos.up(2)).isOf(DRILL_TOP)\n            }\n\n            override fun getBlockEntityPos(pos: BlockPos): BlockPos = pos\n        };\n\n        abstract fun test(world: WorldView, pos: BlockPos): Boolean\n\n        abstract fun getBlockEntityPos(pos: BlockPos): BlockPos\n\n        override fun asString(): String = str\n    }\n\n    class TopDrillBlock(settings: Settings) : DrillBlock(settings, DrillPart.TOP)\n\n    class MiddleDrillBlock(settings: Settings) : DrillBlock(settings, DrillPart.MIDDLE)\n\n    class BottomDrillBlock(settings: Settings) : DrillBlock(settings, DrillPart.BOTTOM), BlockEntityProvider {\n\n        override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = DrillBlockEntity(pos, state)\n\n        override fun <T : BlockEntity?> getTicker(\n            world: World,\n            state: BlockState?,\n            type: BlockEntityType<T>?\n        ): BlockEntityTicker<T>? {\n            return if (world.isClient) null\n            else BlockEntityTicker { world, pos, state, blockEntity -> DrillBlockEntity.tick(world, pos, state, blockEntity as DrillBlockEntity) }\n        }\n\n        override fun onStateReplaced(\n            state: BlockState,\n            world: World?,\n            pos: BlockPos?,\n            newState: BlockState,\n            moved: Boolean\n        ) {\n\n            if (!newState.isOf(this)) {\n                (world?.getBlockEntity(pos) as? DrillBlockEntity)?.let {\n                    ItemScatterer.spawn(world, pos, it)\n                }\n            }\n            super.onStateReplaced(state, world, pos, newState, moved)\n        }\n    }\n\n    companion object {\n        private val DRILL_TOP by lazy { IRBlockRegistry.DRILL_TOP }\n        private val DRILL_MIDDLE by lazy { IRBlockRegistry.DRILL_MIDDLE }\n        private val DRILL_BOTTOM by lazy { IRBlockRegistry.DRILL_BOTTOM }\n        val WORKING: BooleanProperty = BooleanProperty.of(\"working\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/DrillHeadModel.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport com.mojang.datafixers.util.Pair\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.render.model.BakedModel\nimport net.minecraft.client.render.model.ModelBakeSettings\nimport net.minecraft.client.render.model.ModelLoader\nimport net.minecraft.client.render.model.UnbakedModel\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.client.util.SpriteIdentifier\nimport net.minecraft.screen.PlayerScreenHandler\nimport net.minecraft.util.Identifier\nimport java.util.function.Function\n\nclass DrillHeadModel(val variant: String) : UnbakedModel {\n\n    private val spriteIdCollection = mutableListOf(\n        SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, identifier(\"item/${variant}_drill_head\"))\n    )\n\n    private val modelIdentifier = identifier(\"block/${variant}_drill_head\")\n    private var bakedModel: BakedModel? = null\n\n    override fun bake(\n        loader: ModelLoader,\n        textureGetter: Function<SpriteIdentifier, Sprite>,\n        rotationContainer: ModelBakeSettings?,\n        modelId: Identifier?\n    ): BakedModel? {\n        bakedModel = loader.getOrLoadModel(modelIdentifier).bake(loader, textureGetter, rotationContainer, modelId)\n        return bakedModel\n    }\n\n    override fun getModelDependencies(): MutableCollection<Identifier> = mutableListOf()\n\n    override fun getTextureDependencies(\n        unbakedModelGetter: Function<Identifier, UnbakedModel>?,\n        unresolvedTextureReferences: MutableSet<Pair<String, String>>?\n    ): MutableCollection<SpriteIdentifier> {\n        return spriteIdCollection\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/ElectrolyticSeparatorBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.gui.screenhandlers.machines.ElectrolyticSeparatorScreenHandler\nimport me.steven.indrev.registry.MachineRegistry\nimport net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings\n\nclass ElectrolyticSeparatorBlock(registry: MachineRegistry, settings: FabricBlockSettings, tier: Tier)\n    : HorizontalFacingMachineBlock(registry, settings, tier,\n    when (tier) {\n        Tier.MK1 -> IRConfig.machines.electrolyticSeparatorMk1\n        Tier.MK2 -> IRConfig.machines.electrolyticSeparatorMk2\n        Tier.MK3 -> IRConfig.machines.electrolyticSeparatorMk3\n        else -> IRConfig.machines.electrolyticSeparatorMk4\n                },\n    ::ElectrolyticSeparatorScreenHandler\n) {\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/FacingMachineBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.config.IConfig\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.screen.ScreenHandler\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.state.StateManager\nimport net.minecraft.state.property.DirectionProperty\nimport net.minecraft.state.property.Properties\nimport net.minecraft.util.BlockRotation\nimport net.minecraft.util.math.Direction\n\nopen class FacingMachineBlock(\n    registry: MachineRegistry,\n    settings: Settings,\n    tier: Tier,\n    config: IConfig?,\n    screenHandler: ((Int, PlayerInventory, ScreenHandlerContext) -> ScreenHandler)?,\n) : MachineBlock(registry, settings, tier, config, screenHandler) {\n\n    override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? {\n        super.getPlacementState(ctx)\n        return this.defaultState.with(FACING, ctx?.playerLookDirection?.opposite)\n    }\n\n    override fun appendProperties(builder: StateManager.Builder<Block, BlockState>?) {\n        super.appendProperties(builder)\n        builder?.add(FACING)\n    }\n\n    override fun rotate(state: BlockState, rotation: BlockRotation): BlockState {\n        return state.with(FACING, HorizontalFacingMachineBlock.getRotated(state[FACING], rotation))\n    }\n\n    override fun getFacing(state: BlockState): Direction = state[FACING]\n\n    companion object {\n        val FACING: DirectionProperty = Properties.FACING\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/HorizontalFacingMachineBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.config.IConfig\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.screen.ScreenHandler\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.state.StateManager\nimport net.minecraft.state.property.DirectionProperty\nimport net.minecraft.state.property.Properties\nimport net.minecraft.util.BlockRotation\nimport net.minecraft.util.math.Direction\n\nopen class HorizontalFacingMachineBlock(\n    registry: MachineRegistry,\n    settings: Settings,\n    tier: Tier,\n    config: IConfig?,\n    screenHandler: ((Int, PlayerInventory, ScreenHandlerContext) -> ScreenHandler)?\n) : MachineBlock(registry, settings, tier, config, screenHandler) {\n\n    override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? {\n        super.getPlacementState(ctx)\n        return this.defaultState.with(HORIZONTAL_FACING, ctx?.playerFacing?.opposite)\n    }\n\n    override fun appendProperties(builder: StateManager.Builder<Block, BlockState>?) {\n        super.appendProperties(builder)\n        builder?.add(HORIZONTAL_FACING)\n    }\n\n    override fun rotate(state: BlockState, rotation: BlockRotation): BlockState {\n        return state.with(HORIZONTAL_FACING, getRotated(state[HORIZONTAL_FACING], rotation))\n    }\n\n    override fun getFacing(state: BlockState): Direction = state[HORIZONTAL_FACING]\n\n    companion object {\n        val HORIZONTAL_FACING: DirectionProperty = Properties.HORIZONTAL_FACING\n\n        fun getRotated(direction: Direction, rotation: BlockRotation): Direction =\n            if (direction.axis.isVertical) direction else when (rotation) {\n                BlockRotation.NONE -> direction\n                BlockRotation.CLOCKWISE_90 -> direction.rotateYClockwise()\n                BlockRotation.CLOCKWISE_180 -> direction.opposite\n                BlockRotation.COUNTERCLOCKWISE_90 -> direction.rotateYCounterclockwise()\n            }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/LaserBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.gui.screenhandlers.machines.LaserEmitterScreenHandler\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport me.steven.indrev.utils.component3\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.entity.damage.DamageSource\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.sound.SoundCategory\nimport net.minecraft.state.StateManager\nimport net.minecraft.state.property.BooleanProperty\nimport net.minecraft.state.property.Properties\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\nimport java.util.*\n\nclass LaserBlock(registry: MachineRegistry, settings: Settings) : FacingMachineBlock(\n    registry, settings, Tier.MK4, IRConfig.machines.laser, ::LaserEmitterScreenHandler\n) {\n\n    override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? {\n        return super.getPlacementState(ctx)?.with(POWERED, false)\n    }\n\n    override fun appendProperties(builder: StateManager.Builder<Block, BlockState>?) {\n        super.appendProperties(builder)\n        builder?.add(POWERED)\n    }\n\n    @Suppress(\"DEPRECATION\")\n    override fun neighborUpdate(\n        state: BlockState?,\n        world: World?,\n        pos: BlockPos?,\n        block: Block?,\n        fromPos: BlockPos?,\n        notify: Boolean\n    ) {\n        super.neighborUpdate(state, world, pos, block, fromPos, notify)\n\n        world?.setBlockState(pos, state?.with(POWERED, world.isReceivingRedstonePower(pos)))\n    }\n\n    override fun randomDisplayTick(state: BlockState?, world: World, pos: BlockPos, random: Random?) {\n        if (state!![POWERED] && random!!.nextDouble() > 0.9) {\n            val (x, y, z) = pos\n            world.playSound(x.toDouble() + 0.5, y.toDouble(), z.toDouble() + 0.5,\n                IndustrialRevolution.LASER_SOUND_EVENT, SoundCategory.BLOCKS, 0.4f, 1f, false\n            )\n        }\n    }\n\n\n    companion object {\n        val POWERED: BooleanProperty = Properties.POWERED\n        val LASER_DAMAGE_SOURCE = object : DamageSource(\"laser\") {\n            init {\n                setFire()\n                setBypassesArmor()\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/LazuliFluxContainerBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.gui.screenhandlers.machines.LazuliFluxContainerScreenHandler\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\n\nclass LazuliFluxContainerBlock(registry: MachineRegistry, settings: Settings, tier: Tier) : FacingMachineBlock(\n    registry, settings, tier, when (tier) {\n        Tier.MK1 -> IRConfig.machines.lazuliFluxContainerMk1\n        Tier.MK2 -> IRConfig.machines.lazuliFluxContainerMk2\n        Tier.MK3 -> IRConfig.machines.lazuliFluxContainerMk3\n        else -> IRConfig.machines.lazuliFluxContainerMk4\n    }, ::LazuliFluxContainerScreenHandler\n) {\n\n    override fun onPlaced(\n        world: World?,\n        pos: BlockPos,\n        state: BlockState,\n        placer: LivingEntity?,\n        itemStack: ItemStack?\n    ) {\n        super.onPlaced(world, pos, state, placer, itemStack)\n        if (world?.isClient == true) {\n            val blockEntity = world.getBlockEntity(pos) as? LazuliFluxContainerBlockEntity ?: return\n            ConfigurationType.values().forEach { type ->\n                if (blockEntity.isConfigurable(type))\n                    blockEntity.applyDefault(state, type, blockEntity.getCurrentConfiguration(type))\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/MachineBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.api.sideconfigs.SideConfiguration\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity\nimport me.steven.indrev.config.IConfig\nimport me.steven.indrev.gui.IRScreenHandlerFactory\nimport me.steven.indrev.items.misc.IRMachineUpgradeItem\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.energyOf\nimport me.steven.indrev.utils.screwdriver\nimport me.steven.indrev.utils.wrench\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage\nimport net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockEntityProvider\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.InventoryProvider\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.block.entity.BlockEntityTicker\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.inventory.SidedInventory\nimport net.minecraft.item.BlockItem\nimport net.minecraft.item.ItemStack\nimport net.minecraft.particle.ParticleTypes\nimport net.minecraft.screen.ScreenHandler\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.stat.Stats\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.BlockRotation\nimport net.minecraft.util.Hand\nimport net.minecraft.util.ItemScatterer\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.World\nimport net.minecraft.world.WorldAccess\nimport java.util.*\n\nopen class MachineBlock(\n    val registry: MachineRegistry,\n    settings: Settings,\n    val tier: Tier,\n    val config: IConfig?,\n    private val screenHandler: ((Int, PlayerInventory, ScreenHandlerContext) -> ScreenHandler)?,\n) : Block(settings), BlockEntityProvider, InventoryProvider{\n\n    override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? = registry.blockEntityType(tier).instantiate(pos, state)\n\n    override fun <T : BlockEntity?> getTicker(\n        world: World,\n        state: BlockState?,\n        type: BlockEntityType<T>?\n    ): BlockEntityTicker<T>? {\n        return if (world.isClient)\n            BlockEntityTicker { _, _, _, blockEntity -> (blockEntity as? MachineBlockEntity<*>)?.machineClientTick() }\n        else\n            BlockEntityTicker { _, _, _, blockEntity -> (blockEntity as? MachineBlockEntity<*>)?.tick() }\n    }\n\n    override fun onUse(\n        state: BlockState?,\n        world: World,\n        pos: BlockPos?,\n        player: PlayerEntity?,\n        hand: Hand?,\n        hit: BlockHitResult?\n    ): ActionResult? {\n        if (world.isClient) return ActionResult.CONSUME\n        val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return ActionResult.FAIL\n        if (blockEntity.fluidComponent != null) {\n            val result = StorageUtil.move(ContainerItemContext.ofPlayerHand(player, hand).find(FluidStorage.ITEM), blockEntity.fluidComponent, { true }, Long.MAX_VALUE, null)\n            if (result > 0) return ActionResult.SUCCESS\n        }\n        val stack = player?.mainHandStack!!\n        val item = stack.item\n        if (item is IRMachineUpgradeItem) {\n            return ActionResult.PASS\n        } else if (stack.isIn(IndustrialRevolution.WRENCH_TAG)) {\n            return wrench(world, pos!!, state!!, blockEntity, player, stack)\n        } else if (stack.isIn(IndustrialRevolution.SCREWDRIVER_TAG)) {\n            return screwdriver(world, pos!!, state!!, blockEntity, player, stack)\n        } else if (blockEntity.multiblockComponent != null\n            && !blockEntity.multiblockComponent!!.isBuilt(world, pos!!, blockEntity.cachedState, true)) {\n            player.sendMessage(TranslatableText(\"text.multiblock.not_built\"), true)\n            blockEntity.multiblockComponent?.toggleRender(player.isSneaking)\n            blockEntity.markDirty()\n            blockEntity.sync()\n        } else if (screenHandler != null) {\n            player.openHandledScreen(IRScreenHandlerFactory(screenHandler, pos!!))\n        } else return ActionResult.PASS\n        return ActionResult.SUCCESS\n    }\n\n    @Suppress(\"DEPRECATION\")\n    override fun onStateReplaced(state: BlockState, world: World, pos: BlockPos, newState: BlockState, moved: Boolean) {\n        val oldBlockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*>\n        super.onStateReplaced(state, world, pos, newState, moved)\n        if (world.isClient) return\n\n        if (newState.isOf(this)) {\n            val oldFacing = getFacing(state)\n            val newFacing = getFacing(newState)\n            if (oldFacing == newFacing) return\n\n            val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return\n            val rotation = offset(oldFacing, newFacing)\n            blockEntity.inventoryComponent?.run {\n                update(EnumMap(itemConfig).clone(), itemConfig, rotation)\n            }\n            blockEntity.fluidComponent?.run {\n                update(EnumMap(transferConfig).clone(), transferConfig, rotation)\n            }\n            if (blockEntity is LazuliFluxContainerBlockEntity) {\n                update(EnumMap(blockEntity.transferConfig).clone(), blockEntity.transferConfig, rotation)\n            }\n            blockEntity.markDirty()\n        } else if (oldBlockEntity?.inventoryComponent != null) {\n            ItemScatterer.spawn(world, pos, oldBlockEntity.inventoryComponent!!.inventory)\n            world.updateComparators(pos, this)\n        }\n    }\n\n    private fun update(original: EnumMap<Direction, TransferMode>, config: SideConfiguration, rotation: BlockRotation) {\n        Direction.values().forEach { side ->\n            config[side] = original[rotation.rotate(side)]!!\n        }\n    }\n\n    private fun offset(old: Direction, new: Direction): BlockRotation {\n        return when (old) {\n            new.rotateYClockwise() -> BlockRotation.CLOCKWISE_90\n            new.rotateYCounterclockwise() -> BlockRotation.COUNTERCLOCKWISE_90\n            new.opposite -> BlockRotation.CLOCKWISE_180\n            else -> BlockRotation.NONE\n        }\n    }\n\n    override fun afterBreak(world: World?, player: PlayerEntity?, pos: BlockPos?, state: BlockState?, blockEntity: BlockEntity?, toolStack: ItemStack?) {\n        player?.incrementStat(Stats.MINED.getOrCreateStat(this))\n        player?.addExhaustion(0.005f)\n        writeNbtComponents(world, player, pos, state, blockEntity, toolStack)\n    }\n\n    fun writeNbtComponents(world: World?, player: PlayerEntity?, pos: BlockPos?, state: BlockState?, blockEntity: BlockEntity?, toolStack: ItemStack?) {\n        if (world is ServerWorld) {\n            getDroppedStacks(state, world, pos, blockEntity, player, toolStack).forEach { stack ->\n                val item = stack.item\n                if (blockEntity is MachineBlockEntity<*> && item is BlockItem && item.block is MachineBlock) {\n                    val itemIo = energyOf(stack)\n                    if (itemIo != null) {\n                        stack.orCreateNbt.putLong(\"energy\", blockEntity.energy)\n                    }\n                    val tag = stack.getOrCreateSubNbt(\"MachineInfo\")\n                    val temperatureController = blockEntity.temperatureComponent\n                    if (temperatureController != null)\n                        tag.putDouble(\"Temperature\", temperatureController.temperature)\n                }\n                dropStack(world, pos, stack)\n            }\n            state!!.onStacksDropped(world, pos, toolStack)\n        }\n    }\n\n    override fun onPlaced(world: World?, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack?) {\n        super.onPlaced(world, pos, state, placer, itemStack)\n        if (world?.isClient == true) return\n        val blockEntity = world?.getBlockEntity(pos)\n        if (blockEntity is MachineBlockEntity<*>) {\n            val tag = itemStack?.getSubNbt(\"MachineInfo\")\n            val temperatureController = blockEntity.temperatureComponent\n            val itemIo = energyOf(itemStack)\n            if (itemIo != null) {\n                blockEntity.energy = itemIo.amount\n            }\n            if (temperatureController != null) {\n                val temperature = tag?.getDouble(\"Temperature\")\n                if (temperature != null) temperatureController.temperature = temperature\n            }\n            ConfigurationType.values().forEach { type ->\n                if (blockEntity.isConfigurable(type))\n                    blockEntity.applyDefault(state, type, blockEntity.getCurrentConfiguration(type))\n            }\n            world.updateNeighbors(pos, this)\n            blockEntity.markDirty()\n        }\n    }\n\n    override fun neighborUpdate(\n        state: BlockState?,\n        world: World?,\n        pos: BlockPos?,\n        block: Block?,\n        fromPos: BlockPos?,\n        notify: Boolean\n    ) {\n        val blockEntity = world?.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return\n\n        blockEntity.validConnections.clear()\n        blockEntity.validConnections.addAll(Direction.values())\n    }\n\n    open fun getFacing(state: BlockState): Direction = Direction.UP\n\n    override fun getInventory(state: BlockState?, world: WorldAccess?, pos: BlockPos?): SidedInventory? {\n        val blockEntity = world?.getBlockEntity(pos) as? InventoryProvider ?: return null\n        return blockEntity.getInventory(state, world, pos)\n    }\n\n    @Environment(EnvType.CLIENT)\n    override fun randomDisplayTick(state: BlockState?, world: World, pos: BlockPos, random: Random?) {\n        val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return\n        if (blockEntity.workingState) {\n            val d = pos.x.toDouble() + 0.5\n            val e = pos.y.toDouble() + 1.0\n            val f = pos.z.toDouble() + 0.5\n            world.addParticle(ParticleTypes.SMOKE, d, e, f, 0.0, 0.0, 0.0)\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/MiningRigBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.miningrig.MiningRigBlockEntity\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.gui.screenhandlers.machines.MiningRigComputerScreenHandler\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.itemStorageOf\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.BlockView\nimport net.minecraft.world.World\n\nclass MiningRigBlock(registry: MachineRegistry, settings: Settings, tier: Tier) : HorizontalFacingMachineBlock(\n    registry,\n    settings,\n    tier,\n    IRConfig.machines.miner,\n    ::MiningRigComputerScreenHandler,\n) {\n    override fun appendTooltip(\n        stack: ItemStack?,\n        view: BlockView?,\n        tooltip: MutableList<Text>?,\n        options: TooltipContext?\n    ) {\n        super.appendTooltip(stack, view, tooltip, options)\n        tooltip?.add(\n            TranslatableText(\"block.indrev.mining_rig.tooltip\").formatted(Formatting.BLUE, Formatting.ITALIC)\n        )\n    }\n\n    override fun neighborUpdate(\n        state: BlockState?,\n        world: World?,\n        pos: BlockPos?,\n        block: Block?,\n        fromPos: BlockPos?,\n        notify: Boolean\n    ) {\n        super.neighborUpdate(state, world, pos, block, fromPos, notify)\n\n        if (world is ServerWorld) {\n            val dir = Direction.fromVector(fromPos!!.subtract(pos)) ?: return\n            if (itemStorageOf(world, fromPos, dir) != null) {\n                val blockEntity = world.getBlockEntity(pos) as? MiningRigBlockEntity ?: return\n                blockEntity.storageDirections.addAll(Direction.values())\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/PumpBlock.kt",
    "content": "package me.steven.indrev.blocks.machine\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.gui.screenhandlers.machines.PumpScreenHandler\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.ShapeContext\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.shape.VoxelShape\nimport net.minecraft.util.shape.VoxelShapes\nimport net.minecraft.world.BlockView\n\nclass PumpBlock(registry: MachineRegistry, settings: Settings) : HorizontalFacingMachineBlock(\n    registry,\n    settings,\n    Tier.MK1,\n    IRConfig.machines.pump,\n    ::PumpScreenHandler) {\n\n    override fun getOutlineShape(\n        state: BlockState?,\n        world: BlockView?,\n        pos: BlockPos?,\n        context: ShapeContext?\n    ): VoxelShape = when (state?.get(HORIZONTAL_FACING)) {\n        Direction.NORTH -> SHAPE_NORTH\n        Direction.SOUTH -> SHAPE_SOUTH\n        Direction.WEST -> SHAPE_WEST\n        Direction.EAST -> SHAPE_EAST\n        else -> VoxelShapes.fullCube()\n    }\n\n    override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? {\n        return this.defaultState.with(HORIZONTAL_FACING, ctx?.playerFacing)\n    }\n\n    companion object {\n        private val SHAPE_NORTH = createCuboidShape(2.5, 0.0, 0.0, 14.5, 16.0, 14.5)\n        private val SHAPE_SOUTH = createCuboidShape(2.5, 0.0, 2.5, 14.5, 16.0, 16.0)\n        private val SHAPE_WEST = createCuboidShape(0.0, 0.0, 2.5, 14.5, 16.0, 14.5)\n        private val SHAPE_EAST = createCuboidShape(2.5, 0.0, 2.5, 16.0, 16.0, 14.5)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/pipes/BasePipeBlock.kt",
    "content": "package me.steven.indrev.blocks.machine.pipes\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.cables.BasePipeBlockEntity\nimport me.steven.indrev.items.misc.IRServoItem\nimport me.steven.indrev.networks.EndpointData\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.ServoNetworkState\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport me.steven.indrev.utils.component3\nimport me.steven.indrev.utils.toVec3d\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockEntityProvider\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.ShapeContext\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.BlockItem\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Hand\nimport net.minecraft.util.ItemScatterer\nimport net.minecraft.util.collection.DefaultedList\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.math.Vec3d\nimport net.minecraft.util.shape.VoxelShape\nimport net.minecraft.util.shape.VoxelShapes\nimport net.minecraft.world.BlockView\nimport net.minecraft.world.World\n\nabstract class BasePipeBlock(settings: Settings, val tier: Tier, val type: Network.Type<*>) : Block(settings), BlockEntityProvider {\n\n    init {\n        this.defaultState = stateManager.defaultState\n    }\n\n    abstract fun getShape(blockEntity: BasePipeBlockEntity): VoxelShape\n\n    override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? = BasePipeBlockEntity(type, tier, pos, state)\n\n    override fun getOutlineShape(\n        state: BlockState,\n        view: BlockView,\n        pos: BlockPos?,\n        context: ShapeContext?\n    ): VoxelShape {\n        val blockEntity = view.getBlockEntity(pos) as? BasePipeBlockEntity ?: return VoxelShapes.empty()\n        return if (blockEntity.coverState != null) VoxelShapes.fullCube()\n        else getShape(blockEntity)\n    }\n\n    override fun hasDynamicBounds(): Boolean = true\n\n    override fun onBlockBreakStart(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity) {\n        if (!world.isClient) {\n            val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return\n            if (blockEntity.coverState != null && player.mainHandStack.isOf(IRItemRegistry.WRENCH)) {\n                val cover = blockEntity.coverState ?: return\n                ItemScatterer.spawn(world, pos, DefaultedList.ofSize(1, ItemStack(cover.block)))\n                blockEntity.coverState = null\n                blockEntity.markDirty()\n                blockEntity.sync()\n            }\n        }\n    }\n\n    override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {\n        val handStack = player.getStackInHand(hand) ?: return ActionResult.FAIL\n        val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return ActionResult.FAIL\n\n        val tryWrench = onWrench(state, world, pos, player, hand, hit)\n        if (tryWrench.isAccepted) return tryWrench\n        else if (handStack.item is IRServoItem) return ActionResult.PASS\n        val item = handStack.item\n        if (\n            blockEntity.coverState == null\n            && !player.isSneaking\n            && isValidCover(handStack, world, pos)\n        ) {\n            val result = (item as BlockItem).block.getPlacementState(ItemPlacementContext(player, hand, handStack, hit))\n            blockEntity.coverState = result\n            if (!world.isClient) {\n                blockEntity.markDirty()\n                blockEntity.sync()\n            }\n            if (!player.abilities.creativeMode)\n                handStack.decrement(1)\n            return ActionResult.success(world.isClient)\n        }\n        return ActionResult.PASS\n    }\n\n    private fun isValidCover(stack: ItemStack, world: World, pos: BlockPos): Boolean {\n        val item = stack.item\n        return !stack.isEmpty\n            && item is BlockItem\n            && item.block !is BlockEntityProvider\n            && item.block.defaultState.isFullCube(world, pos)\n    }\n\n    private fun onWrench(state: BlockState,world: World, pos: BlockPos,player: PlayerEntity,   hand: Hand, hit: BlockHitResult): ActionResult {\n        val handStack = player.getStackInHand(hand) ?: return ActionResult.FAIL\n        val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return ActionResult.FAIL\n        if (handStack.isIn(IndustrialRevolution.WRENCH_TAG) && world is ServerWorld) {\n            val dir = getSideFromHit(hit.pos, pos)\n            val (x, y, z) = hit.pos\n            val networkState = type.getNetworkState(world)\n            if (networkState is ServoNetworkState<*>) {\n                if (dir != null) {\n                    val data = networkState.removeEndpointData(pos, dir)\n                    when (data?.type) {\n                        EndpointData.Type.OUTPUT -> {\n                            ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_OUTPUT))\n                            return ActionResult.SUCCESS\n                        }\n                        EndpointData.Type.RETRIEVER -> {\n                            ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_RETRIEVER))\n                            return ActionResult.SUCCESS\n                        }\n                        else -> { }\n                    }\n                }\n            }\n\n            if (blockEntity.connections[dir]!!.isConnected())\n                blockEntity.connections[dir] = ConnectionType.WRENCHED\n            else\n                blockEntity.connections[hit.side] =\n                    if (isConnectable(world, pos, hit.side)) ConnectionType.CONNECTED else ConnectionType.NONE\n            blockEntity.markDirty()\n            blockEntity.sync()\n            world.updateNeighbors(pos, state.block)\n            Network.handleUpdate(networkState, pos)\n            return ActionResult.success(world.isClient)\n        }\n        return ActionResult.PASS\n    }\n\n    @Suppress(\"DEPRECATION\")\n    override fun onStateReplaced(\n        blockState: BlockState,\n        world: World,\n        pos: BlockPos,\n        newState: BlockState,\n        moved: Boolean\n    ) {\n        super.onStateReplaced(blockState, world, pos, newState, moved)\n        if (!world.isClient) {\n            val networkState = type.getNetworkState(world as ServerWorld)\n            if (networkState is ServoNetworkState<*>) {\n                if (blockState.isOf(newState.block)) {\n                    Network.handleUpdate(networkState, pos)\n                } else {\n                    Direction.values().forEach { dir ->\n                        val data = networkState.removeEndpointData(pos, dir)\n                        val (x, y, z) = pos.toVec3d()\n                        when (data?.type) {\n                            EndpointData.Type.OUTPUT ->\n                                ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_OUTPUT))\n                            EndpointData.Type.RETRIEVER ->\n                                ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_RETRIEVER))\n                            else -> {\n                            }\n                        }\n                    }\n                }\n            }\n            Network.handleBreak(networkState, pos)\n        }\n    }\n\n    abstract fun isConnectable(world: ServerWorld, pos: BlockPos, dir: Direction): Boolean\n\n    override fun onPlaced(\n        world: World,\n        pos: BlockPos,\n        state: BlockState,\n        placer: LivingEntity?,\n        itemStack: ItemStack?\n    ) {\n        super.onPlaced(world, pos, state, placer, itemStack)\n        if (!world.isClient) {\n            DIRECTIONS.forEach { facing ->\n                updateConnection(world as ServerWorld, pos, pos.offset(facing), facing)\n            }\n            val networkState = type.getNetworkState(world as ServerWorld)\n            Network.handleUpdate(networkState, pos)\n        }\n    }\n\n    override fun neighborUpdate(\n        state: BlockState,\n        world: World?,\n        pos: BlockPos,\n        block: Block?,\n        fromPos: BlockPos,\n        notify: Boolean\n    ) {\n        val (x, y, z) = pos.subtract(fromPos)\n        val facing = Direction.fromVector(x, y, z)?.opposite ?: return\n        if (world is ServerWorld) {\n            updateConnection(world, pos, fromPos, facing)\n        }\n    }\n\n    private fun updateConnection(world: ServerWorld, pos: BlockPos, neighborPos: BlockPos, facing: Direction) {\n        val networkState = type.getNetworkState(world)\n        val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return\n        val before = blockEntity.connections[facing]\n        val new = ConnectionType.getType(isConnectable(world, neighborPos, facing))\n        val neighborBlockEntity = world.getBlockEntity(neighborPos) as? BasePipeBlockEntity\n        if (before != new && before?.isConnectable() != false && new.isConnectable()) {\n            neighborBlockEntity?.connections?.put(facing.opposite, new)\n            neighborBlockEntity?.markDirty()\n            neighborBlockEntity?.sync()\n            blockEntity.connections[facing] = new\n            blockEntity.markDirty()\n            blockEntity.sync()\n            Network.handleUpdate(networkState, pos)\n        }\n    }\n\n    enum class ConnectionType(val id: Int) {\n        NONE(-1), CONNECTED(0), WRENCHED(1);\n\n        fun isConnected() = this == CONNECTED\n\n        fun isConnectable() = this != WRENCHED\n\n        companion object {\n            fun getType(connects: Boolean) = if (connects) CONNECTED else NONE\n\n            fun byId(id: Int): ConnectionType {\n                return when (id) {\n                    0 -> CONNECTED\n                    1 -> WRENCHED\n                    else -> NONE\n                }\n            }\n        }\n    }\n\n    companion object {\n\n        fun getSideFromHit(hit: Vec3d, pos: BlockPos): Direction? {\n            val x = hit.x - pos.x\n            val y = hit.y - pos.y\n            val z = hit.z - pos.z\n            return when {\n                y > 0.6625 -> Direction.UP\n                y < 0.3375 -> Direction.DOWN\n                x > 0.6793 -> Direction.EAST\n                x < 0.3169 -> Direction.WEST\n                z < 0.3169 -> Direction.NORTH\n                z > 0.6625 -> Direction.SOUTH\n                else -> null\n            }\n        }\n\n        val DIRECTIONS = Direction.values()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/pipes/CableBlock.kt",
    "content": "package me.steven.indrev.blocks.machine.pipes\n\nimport it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.cables.BasePipeBlockEntity\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.energy.CableEnergyIo\nimport me.steven.indrev.utils.energyOf\nimport me.steven.indrev.utils.pack\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.shape.VoxelShape\nimport net.minecraft.util.shape.VoxelShapes\nimport net.minecraft.world.BlockView\nimport java.util.function.IntFunction\n\nclass CableBlock(settings: Settings, tier: Tier) : BasePipeBlock(settings, tier, Network.Type.ENERGY) {\n    override fun appendTooltip(\n        stack: ItemStack?,\n        world: BlockView?,\n        tooltip: MutableList<Text>?,\n        options: TooltipContext?\n    ) {\n        tooltip?.add(\n            TranslatableText(\"gui.indrev.tooltip.maxTransferRate\").formatted(Formatting.AQUA)\n                .append(TranslatableText(\"gui.indrev.tooltip.lftick\", getMaxTransferRate()).formatted(Formatting.GRAY))\n        )\n    }\n\n    override fun isConnectable(world: ServerWorld, pos: BlockPos, dir: Direction): Boolean {\n        val handler = energyOf(world, pos, dir.opposite)\n        if (handler != null && handler !is CableEnergyIo) return true\n        val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return false\n        if (!blockEntity.cachedState.isOf(this)) return false\n        return blockEntity.connections[dir.opposite]!!.isConnectable()\n    }\n\n    private fun getMaxTransferRate() = when(tier) {\n        Tier.MK1 -> IRConfig.cables.cableMk1\n        Tier.MK2 -> IRConfig.cables.cableMk2\n        Tier.MK3 -> IRConfig.cables.cableMk3\n        else -> IRConfig.cables.cableMk4\n    }\n\n    override fun getShape(blockEntity: BasePipeBlockEntity): VoxelShape {\n        val directions = DIRECTIONS.filter { dir -> blockEntity.connections[dir] == ConnectionType.CONNECTED }\n        return SHAPE_CACHE.get().computeIfAbsent(pack(directions).toInt(), IntFunction {\n            var shape = CENTER_SHAPE\n            directions.forEach { direction ->\n                shape = VoxelShapes.union(shape, getShape(direction))\n            }\n            shape\n        })\n    }\n\n    companion object {\n\n        val SHAPE_CACHE: ThreadLocal<Int2ObjectOpenHashMap<VoxelShape>> = ThreadLocal.withInitial {\n            object : Int2ObjectOpenHashMap<VoxelShape>(64, 0.25f) {\n                override fun rehash(newN: Int) {\n                }\n            }\n        }\n\n        val DOWN_SHAPE: VoxelShape = createCuboidShape(6.0, 0.0, 6.0, 10.0, 6.0, 10.0)\n        val UP_SHAPE: VoxelShape = createCuboidShape(6.0, 10.5, 6.0, 10.0, 16.0, 10.0)\n        val SOUTH_SHAPE: VoxelShape = createCuboidShape(6.0, 6.0, 10.5, 10.0, 10.0, 16.0)\n        val NORTH_SHAPE: VoxelShape = createCuboidShape(6.0, 6.0, 0.0, 10.0, 10.0, 5.5)\n        val EAST_SHAPE: VoxelShape = createCuboidShape(10.5, 6.0, 6.0, 16.0, 10.0, 10.0)\n        val WEST_SHAPE: VoxelShape = createCuboidShape(0.0, 6.0, 6.0, 5.5, 10.0, 10.0)\n\n        val CENTER_SHAPE: VoxelShape = createCuboidShape(5.5, 5.5, 5.5, 10.5, 10.5, 10.5)\n\n        private fun getShape(direction: Direction): VoxelShape {\n            var shape = VoxelShapes.empty()\n            if (direction == Direction.NORTH) shape = NORTH_SHAPE\n            if (direction == Direction.SOUTH) shape = SOUTH_SHAPE\n            if (direction == Direction.EAST) shape = EAST_SHAPE\n            if (direction == Direction.WEST) shape = WEST_SHAPE\n            if (direction == Direction.UP) shape = UP_SHAPE\n            if (direction == Direction.DOWN) shape = DOWN_SHAPE\n            return shape\n        }\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/pipes/FluidPipeBlock.kt",
    "content": "package me.steven.indrev.blocks.machine.pipes\n\nimport it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.cables.BasePipeBlockEntity\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.ServoNetworkState\nimport me.steven.indrev.utils.fluidStorageOf\nimport me.steven.indrev.utils.pack\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.shape.VoxelShape\nimport net.minecraft.util.shape.VoxelShapes\nimport net.minecraft.world.BlockView\nimport java.util.function.IntFunction\n\nclass FluidPipeBlock(settings: Settings, tier: Tier) : BasePipeBlock(settings, tier, Network.Type.FLUID) {\n    override fun appendTooltip(\n        stack: ItemStack?,\n        world: BlockView?,\n        tooltip: MutableList<Text>?,\n        options: TooltipContext?\n    ) {\n        tooltip?.add(\n            TranslatableText(\"gui.indrev.tooltip.maxTransferRate\").formatted(Formatting.AQUA)\n                .append(TranslatableText(\"gui.indrev.tooltip.fluidsec\", getMaxTransferRate()).formatted(Formatting.GRAY))\n        )\n    }\n\n    override fun isConnectable(world: ServerWorld, pos: BlockPos, dir: Direction): Boolean {\n        if (fluidStorageOf(world, pos, dir.opposite) != null) return true\n        if ((type.getNetworkState(world) as ServoNetworkState<*>).hasServo(pos.offset(dir.opposite), dir))\n            return true\n        val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return false\n        if (!blockEntity.cachedState.isOf(this)) return false\n        return blockEntity.connections[dir.opposite]!!.isConnectable()\n    }\n\n    private fun getMaxTransferRate() = when(tier) {\n        Tier.MK1 -> IRConfig.cables.fluidPipeMk1\n        Tier.MK2 -> IRConfig.cables.fluidPipeMk2\n        Tier.MK3 -> IRConfig.cables.fluidPipeMk3\n        else -> IRConfig.cables.fluidPipeMk4\n    }\n\n    override fun getShape(blockEntity: BasePipeBlockEntity): VoxelShape {\n        val directions = DIRECTIONS.filter { dir -> blockEntity.connections[dir] == ConnectionType.CONNECTED }\n        return SHAPE_CACHE.get().computeIfAbsent(pack(directions).toInt(), IntFunction {\n            var shape = CENTER_SHAPE\n            directions.forEach { direction ->\n                shape = VoxelShapes.union(shape, getShape(direction))\n            }\n            shape\n        })\n    }\n\n    companion object {\n\n        val SHAPE_CACHE: ThreadLocal<Int2ObjectOpenHashMap<VoxelShape>> = ThreadLocal.withInitial {\n            object : Int2ObjectOpenHashMap<VoxelShape>(64, 0.25f) {\n                override fun rehash(newN: Int) {\n                }\n            }\n        }\n\n        val DOWN_SHAPE: VoxelShape = createCuboidShape(6.0, 0.0, 6.0, 10.0, 6.0, 10.0)\n        val UP_SHAPE: VoxelShape = createCuboidShape(6.0, 10.0, 6.0, 10.0, 16.0, 10.0)\n        val SOUTH_SHAPE: VoxelShape = createCuboidShape(6.0, 6.0, 10.5, 10.0, 10.0, 16.0)\n        val NORTH_SHAPE: VoxelShape = createCuboidShape(6.0, 6.0, 0.0, 10.0, 10.0, 6.0)\n        val EAST_SHAPE: VoxelShape = createCuboidShape(10.0, 6.0, 6.0, 16.0, 10.0, 10.0)\n        val WEST_SHAPE: VoxelShape = createCuboidShape(0.0, 6.0, 6.0, 6.0, 10.0, 10.0)\n\n        val CENTER_SHAPE: VoxelShape = createCuboidShape(6.0, 6.0, 6.0, 10.0, 10.0, 10.0)\n\n        private fun getShape(direction: Direction): VoxelShape {\n            var shape = VoxelShapes.empty()\n            if (direction == Direction.NORTH) shape = NORTH_SHAPE\n            if (direction == Direction.SOUTH) shape = SOUTH_SHAPE\n            if (direction == Direction.EAST) shape = EAST_SHAPE\n            if (direction == Direction.WEST) shape = WEST_SHAPE\n            if (direction == Direction.UP) shape = UP_SHAPE\n            if (direction == Direction.DOWN) shape = DOWN_SHAPE\n            return shape\n        }\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/pipes/ItemPipeBlock.kt",
    "content": "package me.steven.indrev.blocks.machine.pipes\n\nimport it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.cables.BasePipeBlockEntity\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.gui.screenhandlers.pipes.PipeFilterScreenFactory\nimport me.steven.indrev.gui.screenhandlers.pipes.PipeFilterScreenHandler\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.ServoNetworkState\nimport me.steven.indrev.utils.itemStorageOf\nimport me.steven.indrev.utils.pack\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.Hand\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.shape.VoxelShape\nimport net.minecraft.util.shape.VoxelShapes\nimport net.minecraft.world.BlockView\nimport net.minecraft.world.World\nimport java.util.function.IntFunction\n\nclass ItemPipeBlock(settings: Settings, tier: Tier) : BasePipeBlock(settings, tier, Network.Type.ITEM) {\n    override fun appendTooltip(\n        stack: ItemStack?,\n        world: BlockView?,\n        tooltip: MutableList<Text>?,\n        options: TooltipContext?\n    ) {\n        tooltip?.add(\n            TranslatableText(\"gui.indrev.tooltip.maxTransferRate\").formatted(Formatting.AQUA)\n                .append(TranslatableText(\"gui.indrev.tooltip.itemsec\", getMaxTransferRate()).formatted(Formatting.GRAY))\n        )\n    }\n\n    override fun onUse(\n        state: BlockState,\n        world: World,\n        pos: BlockPos,\n        player: PlayerEntity,\n        hand: Hand,\n        hit: BlockHitResult\n    ): ActionResult {\n        val dir = getSideFromHit(hit.pos, pos)\n        val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return ActionResult.PASS\n        if (hand == Hand.MAIN_HAND && !world.isClient && player.getStackInHand(hand).isEmpty && dir != null && blockEntity.connections[dir]!!.isConnected()) {\n            val type = Network.Type.ITEM\n            val networkState = type.getNetworkState(world as ServerWorld)\n            if (networkState.networksByPos.get(pos.asLong())?.containers?.containsKey(pos.offset(dir)) == true) {\n                player.openHandledScreen(PipeFilterScreenFactory(::PipeFilterScreenHandler, pos, dir))\n                return ActionResult.SUCCESS\n            }\n        }\n        return super.onUse(state, world, pos, player, hand, hit)\n    }\n\n    override fun isConnectable(world: ServerWorld, pos: BlockPos, dir: Direction): Boolean {\n        if (itemStorageOf(world, pos, dir.opposite) != null) return true\n        if ((type.getNetworkState(world) as ServoNetworkState<*>).hasServo(pos.offset(dir.opposite), dir))\n            return true\n        val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return false\n        if (!blockEntity.cachedState.isOf(this)) return false\n        return blockEntity.connections[dir.opposite]!!.isConnectable()\n    }\n\n\n    private fun getMaxTransferRate() = when(tier) {\n        Tier.MK1 -> IRConfig.cables.itemPipeMk1\n        Tier.MK2 -> IRConfig.cables.itemPipeMk2\n        Tier.MK3 -> IRConfig.cables.itemPipeMk3\n        else -> IRConfig.cables.itemPipeMk4\n    }\n\n    override fun getShape(blockEntity: BasePipeBlockEntity): VoxelShape {\n        val directions = DIRECTIONS.filter { dir -> blockEntity.connections[dir] == ConnectionType.CONNECTED }\n        return SHAPE_CACHE.get().computeIfAbsent(pack(directions).toInt(), IntFunction {\n            var shape = CENTER_SHAPE\n            directions.forEach { direction ->\n                shape = VoxelShapes.union(shape, getShape(direction))\n            }\n            shape\n        })\n    }\n\n    companion object {\n\n        val SHAPE_CACHE: ThreadLocal<Int2ObjectOpenHashMap<VoxelShape>> = ThreadLocal.withInitial {\n            object : Int2ObjectOpenHashMap<VoxelShape>(64, 0.25f) {\n                override fun rehash(newN: Int) {\n                }\n            }\n        }\n\n        val DOWN_SHAPE: VoxelShape = createCuboidShape(6.5, 0.0, 6.5, 9.5, 6.5, 9.5)\n        val UP_SHAPE: VoxelShape = createCuboidShape(6.5, 9.5, 6.5, 9.5, 16.0, 9.5)\n        val SOUTH_SHAPE: VoxelShape = createCuboidShape(6.5, 6.5, 9.5, 9.5, 9.5, 16.0)\n        val NORTH_SHAPE: VoxelShape = createCuboidShape(6.5, 6.5, 0.0, 9.5, 9.5, 6.5)\n        val EAST_SHAPE: VoxelShape = createCuboidShape(9.5, 6.5, 6.5, 16.0, 9.5, 9.5)\n        val WEST_SHAPE: VoxelShape = createCuboidShape(0.0, 6.5, 6.5, 6.5, 9.5, 9.5)\n\n        val CENTER_SHAPE: VoxelShape = createCuboidShape(6.5, 6.5, 6.5, 9.5, 9.5, 9.5)\n\n        private fun getShape(direction: Direction): VoxelShape {\n            var shape = VoxelShapes.empty()\n            if (direction == Direction.NORTH) shape = NORTH_SHAPE\n            if (direction == Direction.SOUTH) shape = SOUTH_SHAPE\n            if (direction == Direction.EAST) shape = EAST_SHAPE\n            if (direction == Direction.WEST) shape = WEST_SHAPE\n            if (direction == Direction.UP) shape = UP_SHAPE\n            if (direction == Direction.DOWN) shape = DOWN_SHAPE\n            return shape\n        }\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/FluidValveBlock.kt",
    "content": "package me.steven.indrev.blocks.machine.solarpowerplant\n\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.FacingBlock\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.state.StateManager\nimport net.minecraft.util.BlockMirror\nimport net.minecraft.util.BlockRotation\nimport net.minecraft.util.math.Direction\n\nopen class FluidValveBlock(settings: Settings) : FacingBlock(settings) {\n\n    init {\n        this.defaultState = stateManager.defaultState.with(FACING, Direction.NORTH)\n    }\n\n    override fun appendProperties(builder: StateManager.Builder<Block, BlockState>?) {\n        builder?.add(FACING)\n    }\n\n    override fun rotate(state: BlockState, rotation: BlockRotation): BlockState? {\n        return state.with(FACING, rotation.rotate(state[FACING]))\n    }\n\n    override fun mirror(state: BlockState, mirror: BlockMirror): BlockState? {\n        return state.rotate(mirror.getRotation(state[FACING]))\n    }\n\n    override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {\n        return defaultState.with(FACING, ctx.playerLookDirection.opposite)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/SolarPowerPlantFluidOutputBlock.kt",
    "content": "package me.steven.indrev.blocks.machine.solarpowerplant\n\nimport me.steven.indrev.blocks.misc.HorizontalFacingBlock\n\nclass SolarPowerPlantFluidOutputBlock(settings: Settings) : HorizontalFacingBlock(settings)"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/SolarPowerPlantTowerBlock.kt",
    "content": "package me.steven.indrev.blocks.machine.solarpowerplant\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.blockentities.solarpowerplant.HeliostatBlockEntity\nimport me.steven.indrev.blockentities.solarpowerplant.SolarPowerPlantTowerBlockEntity\nimport me.steven.indrev.blocks.misc.HorizontalFacingBlock\nimport me.steven.indrev.components.multiblock.definitions.SolarPowerPlantTowerStructureDefinition\nimport me.steven.indrev.gui.IRScreenHandlerFactory\nimport me.steven.indrev.gui.screenhandlers.machines.SolarPowerPlantTowerScreenHandler\nimport net.minecraft.block.BlockEntityProvider\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.block.entity.BlockEntityTicker\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.nbt.NbtLong\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Hand\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\n\nclass SolarPowerPlantTowerBlock(settings: Settings) : HorizontalFacingBlock(settings), BlockEntityProvider {\n\n    override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = SolarPowerPlantTowerBlockEntity(pos, state)\n\n    override fun <T : BlockEntity?> getTicker(\n        world: World,\n        state: BlockState?,\n        type: BlockEntityType<T>?\n    ): BlockEntityTicker<T>? {\n        return if (world.isClient) null\n        else return BlockEntityTicker { world, pos, state, blockEntity -> SolarPowerPlantTowerBlockEntity.tick(world, pos, state, blockEntity as? SolarPowerPlantTowerBlockEntity ?: return@BlockEntityTicker) }\n    }\n\n    override fun onUse(\n        state: BlockState,\n        world: World,\n        pos: BlockPos,\n        player: PlayerEntity,\n        hand: Hand,\n        hit: BlockHitResult?\n    ): ActionResult {\n        if (!world.isClient) {\n            val stack = player.getStackInHand(hand)\n            if (stack.isIn(IndustrialRevolution.SCREWDRIVER_TAG) && stack.orCreateNbt.contains(\"SelectedHeliostats\")) {\n                val positions = stack.nbt!!.getList(\"SelectedHeliostats\", 4)?.map { BlockPos.fromLong((it as NbtLong).longValue()) }\n                val receivers = SolarPowerPlantTowerStructureDefinition.getSolarReceiverPositions(pos, state)\n\n                positions?.forEach { p ->\n                    val heliostat = world.getBlockEntity(p) as HeliostatBlockEntity\n                    val target = receivers.minByOrNull { it.getManhattanDistance(p) } ?: return@forEach\n                    heliostat.targetBlock = target\n                    heliostat.markDirty()\n                    heliostat.sync()\n                }\n                player.sendMessage(LiteralText(\"Linked Heliostats!\"), true)\n            } else {\n                val blockEntity = world.getBlockEntity(pos) as? SolarPowerPlantTowerBlockEntity ?: return ActionResult.PASS\n                if (!blockEntity.multiblockComponent.isBuilt(world, pos, state)) {\n                    player.sendMessage(TranslatableText(\"text.multiblock.not_built\"), true)\n                    blockEntity.multiblockComponent.toggleRender(player.isSneaking)\n                    blockEntity.markDirty()\n                    blockEntity.sync()\n                } else\n                    player.openHandledScreen(IRScreenHandlerFactory(::SolarPowerPlantTowerScreenHandler, pos))\n            }\n        }\n        return ActionResult.success(world.isClient)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/SolarReceiverBlock.kt",
    "content": "package me.steven.indrev.blocks.machine.solarpowerplant\n\nimport me.steven.indrev.blockentities.solarpowerplant.SolarReceiverBlockEntity\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockEntityProvider\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.util.math.BlockPos\n\nclass SolarReceiverBlock(settings: Settings) : Block(settings), BlockEntityProvider {\n    override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = SolarReceiverBlockEntity(pos, state)\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/SteamTurbineBlock.kt",
    "content": "package me.steven.indrev.blocks.machine.solarpowerplant\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.gui.screenhandlers.machines.SteamTurbineScreenHandler\nimport me.steven.indrev.registry.MachineRegistry\n\nclass SteamTurbineBlock(registry: MachineRegistry, settings: Settings)\n    : HorizontalFacingMachineBlock(registry, settings, Tier.MK4, IRConfig.generators.steamTurbine, ::SteamTurbineScreenHandler)"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/machine/solarpowerplant/SteamTurbineSteamInputValveBlock.kt",
    "content": "package me.steven.indrev.blocks.machine.solarpowerplant\n\nimport me.steven.indrev.blockentities.generators.SteamTurbineSteamInputValveBlockEntity\nimport net.minecraft.block.BlockEntityProvider\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.block.entity.BlockEntityTicker\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\n\nclass SteamTurbineSteamInputValveBlock(settings: Settings) : FluidValveBlock(settings), BlockEntityProvider {\n    override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = SteamTurbineSteamInputValveBlockEntity(pos, state)\n\n    override fun <T : BlockEntity?> getTicker(\n        world: World,\n        state: BlockState?,\n        type: BlockEntityType<T>?\n    ): BlockEntityTicker<T>? {\n        return if (world.isClient) null\n        else BlockEntityTicker { _, _, _, blockEntity ->\n            (blockEntity as? SteamTurbineSteamInputValveBlockEntity)?.inserted = false\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/misc/AcidFluidBlock.kt",
    "content": "package me.steven.indrev.blocks.misc\n\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.Blocks\nimport net.minecraft.block.FluidBlock\nimport net.minecraft.entity.Entity\nimport net.minecraft.entity.damage.DamageSource\nimport net.minecraft.fluid.FlowableFluid\nimport net.minecraft.particle.ParticleTypes\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.World\nimport java.util.*\n\nclass AcidFluidBlock(fluid: FlowableFluid, settings: Settings) : FluidBlock(fluid, settings) {\n    override fun randomTick(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: Random?) {\n        Direction.values().forEach { dir ->\n            val neighbor = pos?.offset(dir)\n            val blockState = world?.getBlockState(neighbor)\n            val block = blockState?.block\n            if (block == Blocks.DIRT || block == Blocks.GRASS_BLOCK || block == Blocks.FARMLAND || block == Blocks.DIRT_PATH)\n                world?.setBlockState(pos, Blocks.COARSE_DIRT.defaultState)\n        }\n    }\n\n    override fun randomDisplayTick(state: BlockState?, world: World, pos: BlockPos, random: Random) {\n        if (!world.isAir(pos.up()) || random.nextInt(10) < 5) return\n        (0..1).forEach { a ->\n            (0..1).forEach { b ->\n                world.addParticle(\n                    ParticleTypes.SNEEZE, pos.x + a / 2.0 + (random.nextFloat() / 5), pos.y + 1.0, pos.z + b / 2.0 + (random.nextFloat() / 5), 0.0, 0.005, 0.0)\n            }\n        }\n    }\n\n    override fun onEntityCollision(state: BlockState?, world: World, pos: BlockPos?, entity: Entity?) {\n        if (world.time % 15 == 0L)\n            entity?.damage(ACID_DAMAGE_SOURCE, 4f)\n    }\n\n    companion object {\n        val ACID_DAMAGE_SOURCE = object : DamageSource(\"acid\") {\n            init {\n                setBypassesArmor()\n                setUnblockable()\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/misc/BiomassComposterBlock.kt",
    "content": "package me.steven.indrev.blocks.misc\n\nimport me.steven.indrev.blockentities.farms.BiomassComposterBlockEntity\nimport me.steven.indrev.registry.IRBlockRegistry\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.utils.bucket\nimport net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings\nimport net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage\nimport net.fabricmc.fabric.api.transfer.v1.item.ItemVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil\nimport net.minecraft.block.*\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.block.entity.BlockEntityTicker\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.ItemStack\nimport net.minecraft.state.StateManager\nimport net.minecraft.state.property.BooleanProperty\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Hand\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\n\nclass BiomassComposterBlock : Block(FabricBlockSettings.copyOf(Blocks.COMPOSTER)), BlockEntityProvider {\n\n    init {\n        this.defaultState = stateManager.defaultState.with(CLOSED, false)\n    }\n\n    override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = BiomassComposterBlockEntity(pos, state)\n\n    override fun <T : BlockEntity?> getTicker(\n        world: World,\n        state: BlockState,\n        type: BlockEntityType<T>\n    ): BlockEntityTicker<T>? {\n        return if (world.isClient) null\n        else BlockEntityTicker { _, _, state, blockEntity ->\n            BiomassComposterBlockEntity.tick(\n                state, blockEntity as? BiomassComposterBlockEntity ?: return@BlockEntityTicker\n            )\n        }\n    }\n\n    override fun appendProperties(builder: StateManager.Builder<Block, BlockState>?) {\n        builder?.add(CLOSED)\n    }\n\n    override fun onUse(\n        state: BlockState,\n        world: World,\n        pos: BlockPos,\n        player: PlayerEntity,\n        hand: Hand,\n        hit: BlockHitResult?\n    ): ActionResult {\n        val stack = player.getStackInHand(hand)\n        val blockEntity = world.getBlockEntity(pos) as? BiomassComposterBlockEntity ?: return ActionResult.PASS\n        val inHand = ContainerItemContext.ofPlayerHand(player, hand).find(FluidStorage.ITEM)\n        val result = StorageUtil.move(inHand, blockEntity.fluidInv, { true }, bucket, null)\n        if (result > 0) {\n            blockEntity.markDirty()\n            if (!world.isClient)\n                blockEntity.sync()\n            return ActionResult.success(world.isClient)\n        } else if (ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.contains(stack.item) || stack.isEmpty) {\n            compost(stack, player, blockEntity, world)\n        } else if (state[CLOSED]) {\n            world.setBlockState(pos, state.with(CLOSED, false))\n            if (!player.isCreative)\n                player.inventory.offerOrDrop(ItemStack(IRBlockRegistry.PLANKS))\n        } else if (stack.isOf(IRBlockRegistry.PLANKS.asItem())) {\n            world.setBlockState(pos, state.with(CLOSED, true))\n            if (!player.isCreative)\n                stack.decrement(1)\n        } else return ActionResult.FAIL\n\n        return ActionResult.success(world.isClient)\n    }\n\n    private fun compost(stack: ItemStack, player: PlayerEntity, blockEntity: BiomassComposterBlockEntity, world: World) {\n        if (world.isClient) return\n        if (blockEntity.itemInv.variant.isOf(IRItemRegistry.BIOMASS)) {\n            player.inventory.offerOrDrop(ItemStack(IRItemRegistry.BIOMASS))\n            blockEntity.itemInv.variant = ItemVariant.blank()\n            blockEntity.itemInv.amount = 0\n\n            blockEntity.markDirty()\n            blockEntity.sync()\n        } else if (blockEntity.level < 7 && !stack.isEmpty) {\n            val chance = ComposterBlock.ITEM_TO_LEVEL_INCREASE_CHANCE.getValue(stack.item)\n            if (!player.isCreative)\n                stack.decrement(1)\n            var leveledUp = false\n            if (world.random.nextInt() < chance) {\n                blockEntity.level++\n                blockEntity.markDirty()\n                blockEntity.sync()\n                leveledUp = true\n            }\n            world.syncWorldEvent(1500, blockEntity.pos, if (leveledUp) 1 else 0)\n        }\n    }\n\n    companion object {\n        val CLOSED: BooleanProperty = BooleanProperty.of(\"closed\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/misc/CabinetBlock.kt",
    "content": "package me.steven.indrev.blocks.misc\n\nimport me.steven.indrev.blockentities.storage.CabinetBlockEntity\nimport net.minecraft.block.BlockEntityProvider\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Hand\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\n\nclass CabinetBlock(settings: Settings) : HorizontalFacingBlock(settings), BlockEntityProvider {\n    override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = CabinetBlockEntity(pos, state)\n    override fun onUse(\n        state: BlockState?,\n        world: World,\n        pos: BlockPos?,\n        player: PlayerEntity,\n        hand: Hand?,\n        hit: BlockHitResult?\n    ): ActionResult {\n        if (!world.isClient) {\n            val blockEntity = world.getBlockEntity(pos) as? CabinetBlockEntity ?: return ActionResult.PASS\n            player.openHandledScreen(blockEntity)\n        }\n        return ActionResult.success(world.isClient)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/misc/DuctBlock.kt",
    "content": "package me.steven.indrev.blocks.misc\n\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.ShapeContext\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.shape.VoxelShape\nimport net.minecraft.world.BlockView\n\nclass DuctBlock(settings: Settings) : HorizontalFacingBlock(settings) {\n    override fun getOutlineShape(\n        state: BlockState,\n        world: BlockView?,\n        pos: BlockPos?,\n        context: ShapeContext?\n    ): VoxelShape = when (state[FACING]) {\n        Direction.NORTH -> FACING_NORTH\n        Direction.SOUTH -> FACING_SOUTH\n        Direction.WEST -> FACING_WEST\n        Direction.EAST -> FACING_EAST\n        else -> FACING_NORTH\n    }\n    companion object {\n        private val FACING_SOUTH: VoxelShape = createCuboidShape(2.0, 0.0, 0.0, 14.0, 14.0, 14.0)\n        private val FACING_NORTH: VoxelShape = createCuboidShape(2.0, 0.0, 2.0, 14.0, 14.0, 16.0)\n        private val FACING_EAST: VoxelShape = createCuboidShape(0.0, 0.0, 2.0, 14.0, 14.0, 14.0)\n        private val FACING_WEST: VoxelShape = createCuboidShape(2.0, 0.0, 2.0, 16.0, 14.0, 14.0)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/misc/HorizontalFacingBlock.kt",
    "content": "package me.steven.indrev.blocks.misc\n\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.HorizontalFacingBlock\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.state.StateManager\nimport net.minecraft.util.math.Direction\n\nopen class HorizontalFacingBlock(settings: Settings) : HorizontalFacingBlock(settings) {\n    init {\n        this.defaultState = stateManager.defaultState.with(FACING, Direction.NORTH)\n    }\n\n    override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? {\n        return defaultState.with(FACING, ctx?.playerFacing?.opposite)\n    }\n\n    override fun appendProperties(builder: StateManager.Builder<Block, BlockState>?) {\n        builder?.add(FACING)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/misc/NikoliteOreBlock.kt",
    "content": "package me.steven.indrev.blocks.misc\n\nimport net.minecraft.block.OreBlock\nimport net.minecraft.util.math.intprovider.UniformIntProvider\n\nclass NikoliteOreBlock(settings: Settings) : OreBlock(settings, UniformIntProvider.create(1, 6))"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/misc/PlankBlock.kt",
    "content": "package me.steven.indrev.blocks.misc\n\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.ShapeContext\nimport net.minecraft.block.SnowBlock\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.shape.VoxelShape\nimport net.minecraft.world.BlockView\nimport net.minecraft.world.WorldView\n\nclass PlankBlock(settings: Settings) : SnowBlock(settings) {\n    override fun canPlaceAt(state: BlockState, world: WorldView?, pos: BlockPos): Boolean = true\n\n    override fun getCollisionShape(\n        state: BlockState,\n        world: BlockView?,\n        pos: BlockPos?,\n        context: ShapeContext?\n    ): VoxelShape = LAYERS_TO_SHAPE[state.get(LAYERS)]\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/misc/SulfurCrystalBlock.kt",
    "content": "package me.steven.indrev.blocks.misc\n\nimport me.steven.indrev.registry.IRItemRegistry\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.ShapeContext\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.state.StateManager\nimport net.minecraft.state.property.DirectionProperty\nimport net.minecraft.state.property.Properties\nimport net.minecraft.util.function.BooleanBiFunction\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.shape.VoxelShape\nimport net.minecraft.util.shape.VoxelShapes\nimport net.minecraft.world.BlockView\nimport net.minecraft.world.World\nimport net.minecraft.world.WorldView\nimport java.util.stream.Stream\n\nclass SulfurCrystalBlock(settings: Settings) : Block(settings) {\n\n    init {\n        defaultState = stateManager.defaultState.with(FACING, Direction.UP)\n    }\n\n    override fun getOutlineShape(\n        state: BlockState,\n        world: BlockView?,\n        pos: BlockPos?,\n        context: ShapeContext?\n    ): VoxelShape = when (state[FACING]) {\n        Direction.DOWN -> DOWN_SHAPE\n        Direction.NORTH -> NORTH_SHAPE\n        Direction.SOUTH -> SOUTH_SHAPE\n        Direction.WEST -> WEST_SHAPE\n        Direction.EAST -> EAST_SHAPE\n        else -> UP_SHAPE\n    }\n\n    override fun neighborUpdate(\n        state: BlockState,\n        world: World?,\n        pos: BlockPos,\n        block: Block?,\n        fromPos: BlockPos,\n        notify: Boolean\n    ) {\n        val vec = pos.subtract(fromPos)\n        val dir = Direction.fromVector(vec.x, vec.y, vec.z)\n        if (state[FACING] == dir) {\n            world?.breakBlock(pos, true)\n        }\n    }\n\n    override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {\n        val direction = state.get(FACING)\n        val blockPos = pos.offset(direction.opposite)\n        val blockState = world.getBlockState(blockPos)\n        return blockState.isSideSolidFullSquare(world, blockPos, direction)\n    }\n\n    override fun appendProperties(builder: StateManager.Builder<Block, BlockState>?) {\n        builder?.add(FACING)\n    }\n\n    override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? {\n        return defaultState.with(FACING, ctx?.playerLookDirection?.opposite)\n    }\n\n    override fun asItem(): Item {\n        return IRItemRegistry.SULFUR_CRYSTAL_ITEM\n    }\n\n    companion object {\n        val FACING: DirectionProperty = Properties.FACING\n        private val UP_SHAPE = Stream.of(\n            createCuboidShape(2.0, 5.0, 4.0, 3.0, 6.0, 5.0),\n            createCuboidShape(2.0, 4.0, 4.0, 4.0, 5.0, 6.0),\n            createCuboidShape(1.0, 0.0, 3.0, 4.0, 4.0, 6.0),\n            createCuboidShape(3.0, 0.0, 2.0, 5.0, 1.0, 4.0),\n            createCuboidShape(0.0, 0.0, 5.0, 2.0, 2.0, 7.0),\n            createCuboidShape(10.0, 0.0, 1.0, 12.0, 1.0, 3.0),\n            createCuboidShape(11.0, 0.0, 2.0, 14.0, 4.0, 5.0),\n            createCuboidShape(11.0, 4.0, 2.0, 13.0, 5.0, 4.0),\n            createCuboidShape(12.0, 5.0, 3.0, 13.0, 6.0, 4.0),\n            createCuboidShape(13.0, 0.0, 4.0, 15.0, 2.0, 6.0),\n            createCuboidShape(6.0, 0.0, 7.0, 8.0, 2.0, 10.0),\n            createCuboidShape(7.0, 0.0, 8.0, 12.0, 5.0, 13.0),\n            createCuboidShape(8.0, 5.0, 9.0, 11.0, 8.0, 12.0),\n            createCuboidShape(9.0, 8.0, 10.0, 10.0, 10.0, 11.0),\n            createCuboidShape(11.0, 0.0, 12.0, 14.0, 3.0, 15.0)\n        ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null)\n\n        private val DOWN_SHAPE = Stream.of(\n            createCuboidShape(13.0, 11.0, 4.0, 14.0, 12.0, 5.0),\n            createCuboidShape(12.0, 12.0, 4.0, 14.0, 13.0, 6.0),\n            createCuboidShape(12.0, 13.0, 3.0, 15.0, 17.0, 6.0),\n            createCuboidShape(11.0, 16.0, 2.0, 13.0, 17.0, 4.0),\n            createCuboidShape(14.0, 15.0, 5.0, 16.0, 17.0, 7.0),\n            createCuboidShape(4.0, 16.0, 1.0, 6.0, 17.0, 3.0),\n            createCuboidShape(2.0, 13.0, 2.0, 5.0, 17.0, 5.0),\n            createCuboidShape(3.0, 12.0, 2.0, 5.0, 13.0, 4.0),\n            createCuboidShape(3.0, 11.0, 3.0, 4.0, 12.0, 4.0),\n            createCuboidShape(1.0, 15.0, 4.0, 3.0, 17.0, 6.0),\n            createCuboidShape(8.0, 15.0, 7.0, 10.0, 17.0, 10.0),\n            createCuboidShape(4.0, 12.0, 8.0, 9.0, 17.0, 13.0),\n            createCuboidShape(5.0, 9.0, 9.0, 8.0, 12.0, 12.0),\n            createCuboidShape(6.0, 7.0, 10.0, 7.0, 9.0, 11.0),\n            createCuboidShape(2.0, 14.0, 12.0, 5.0, 17.0, 15.0)\n        ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null)\n\n        private val EAST_SHAPE = Stream.of(\n            createCuboidShape(5.0, 11.0, 13.0, 6.0, 12.0, 14.0),\n            createCuboidShape(4.0, 10.0, 12.0, 5.0, 12.0, 14.0),\n            createCuboidShape(0.0, 10.0, 12.0, 4.0, 13.0, 15.0),\n            createCuboidShape(0.0, 12.0, 11.0, 1.0, 14.0, 13.0),\n            createCuboidShape(0.0, 9.0, 14.0, 2.0, 11.0, 16.0),\n            createCuboidShape(0.0, 13.0, 4.0, 1.0, 15.0, 6.0),\n            createCuboidShape(0.0, 11.0, 2.0, 4.0, 14.0, 5.0),\n            createCuboidShape(4.0, 12.0, 3.0, 5.0, 14.0, 5.0),\n            createCuboidShape(5.0, 12.0, 3.0, 6.0, 13.0, 4.0),\n            createCuboidShape(0.0, 10.0, 1.0, 2.0, 12.0, 3.0),\n            createCuboidShape(0.0, 6.0, 8.0, 2.0, 9.0, 10.0),\n            createCuboidShape(0.0, 3.0, 4.0, 5.0, 8.0, 9.0),\n            createCuboidShape(5.0, 4.0, 5.0, 8.0, 7.0, 8.0),\n            createCuboidShape(8.0, 5.0, 6.0, 10.0, 6.0, 7.0),\n            createCuboidShape(0.0, 1.0, 2.0, 3.0, 4.0, 5.0)\n        ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null)\n\n        private val SOUTH_SHAPE = Stream.of(\n            createCuboidShape(1.0, 4.0, 0.0, 3.0, 6.0, 2.0),\n            createCuboidShape(3.0, 3.0, 5.0, 4.0, 4.0, 6.0),\n            createCuboidShape(3.0, 2.0, 4.0, 5.0, 4.0, 5.0),\n            createCuboidShape(2.0, 2.0, 0.0, 5.0, 5.0, 4.0),\n            createCuboidShape(4.0, 1.0, 0.0, 6.0, 3.0, 1.0),\n            createCuboidShape(11.0, 2.0, 0.0, 13.0, 4.0, 1.0),\n            createCuboidShape(12.0, 3.0, 0.0, 15.0, 6.0, 4.0),\n            createCuboidShape(12.0, 4.0, 4.0, 14.0, 6.0, 5.0),\n            createCuboidShape(13.0, 4.0, 5.0, 14.0, 5.0, 6.0),\n            createCuboidShape(14.0, 5.0, 0.0, 16.0, 7.0, 2.0),\n            createCuboidShape(8.0, 7.0, 0.0, 10.0, 10.0, 2.0),\n            createCuboidShape(4.0, 8.0, 0.0, 9.0, 13.0, 5.0),\n            createCuboidShape(5.0, 9.0, 5.0, 8.0, 12.0, 8.0),\n            createCuboidShape(6.0, 10.0, 8.0, 7.0, 11.0, 10.0),\n            createCuboidShape(2.0, 12.0, 0.0, 5.0, 15.0, 3.0)\n        ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null)\n\n        private val NORTH_SHAPE = Stream.of(\n            createCuboidShape(13.0, 4.0, 14.0, 15.0, 6.0, 16.0),\n            createCuboidShape(12.0, 3.0, 10.0, 13.0, 4.0, 11.0),\n            createCuboidShape(11.0, 2.0, 11.0, 13.0, 4.0, 12.0),\n            createCuboidShape(11.0, 2.0, 12.0, 14.0, 5.0, 16.0),\n            createCuboidShape(10.0, 1.0, 15.0, 12.0, 3.0, 16.0),\n            createCuboidShape(3.0, 2.0, 15.0, 5.0, 4.0, 16.0),\n            createCuboidShape(1.0, 3.0, 12.0, 4.0, 6.0, 16.0),\n            createCuboidShape(2.0, 4.0, 11.0, 4.0, 6.0, 12.0),\n            createCuboidShape(2.0, 4.0, 10.0, 3.0, 5.0, 11.0),\n            createCuboidShape(0.0, 5.0, 14.0, 2.0, 7.0, 16.0),\n            createCuboidShape(6.0, 7.0, 14.0, 8.0, 10.0, 16.0),\n            createCuboidShape(7.0, 8.0, 11.0, 12.0, 13.0, 16.0),\n            createCuboidShape(8.0, 9.0, 8.0, 11.0, 12.0, 11.0),\n            createCuboidShape(9.0, 10.0, 6.0, 10.0, 11.0, 8.0),\n            createCuboidShape(11.0, 12.0, 13.0, 14.0, 15.0, 16.0)\n        ).reduce { v1: VoxelShape?, v2: VoxelShape? -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null)\n\n        private val WEST_SHAPE = Stream.of(\n            createCuboidShape(10.0, 11.0, 2.0, 11.0, 12.0, 3.0),\n            createCuboidShape(11.0, 10.0, 2.0, 12.0, 12.0, 4.0),\n            createCuboidShape(12.0, 10.0, 1.0, 16.0, 13.0, 4.0),\n            createCuboidShape(15.0, 12.0, 3.0, 16.0, 14.0, 5.0),\n            createCuboidShape(14.0, 9.0, 0.0, 16.0, 11.0, 2.0),\n            createCuboidShape(15.0, 13.0, 10.0, 16.0, 15.0, 12.0),\n            createCuboidShape(12.0, 11.0, 11.0, 16.0, 14.0, 14.0),\n            createCuboidShape(11.0, 12.0, 11.0, 12.0, 14.0, 13.0),\n            createCuboidShape(10.0, 12.0, 12.0, 11.0, 13.0, 13.0),\n            createCuboidShape(14.0, 10.0, 13.0, 16.0, 12.0, 15.0),\n            createCuboidShape(14.0, 6.0, 6.0, 16.0, 9.0, 8.0),\n            createCuboidShape(11.0, 3.0, 7.0, 16.0, 8.0, 12.0),\n            createCuboidShape(8.0, 4.0, 8.0, 11.0, 7.0, 11.0),\n            createCuboidShape(6.0, 5.0, 9.0, 8.0, 6.0, 10.0),\n            createCuboidShape(13.0, 1.0, 11.0, 16.0, 4.0, 14.0)\n        ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.orElse(null)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/misc/TankBlock.kt",
    "content": "package me.steven.indrev.blocks.misc\n\nimport alexiil.mc.lib.attributes.fluid.volume.FluidVolume\nimport me.steven.indrev.blockentities.Syncable\nimport me.steven.indrev.blockentities.storage.TankBlockEntity\nimport me.steven.indrev.components.FluidComponent\nimport me.steven.indrev.registry.IRBlockRegistry\nimport me.steven.indrev.utils.bucket\nimport me.steven.indrev.utils.fluidStorageOf\nimport net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage\nimport net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockEntityProvider\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.ShapeContext\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.block.entity.BlockEntityTicker\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.stat.Stats\nimport net.minecraft.state.StateManager\nimport net.minecraft.state.property.BooleanProperty\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Hand\nimport net.minecraft.util.function.BooleanBiFunction\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.shape.VoxelShape\nimport net.minecraft.util.shape.VoxelShapes\nimport net.minecraft.world.BlockView\nimport net.minecraft.world.World\nimport net.minecraft.world.WorldAccess\nimport net.minecraft.world.chunk.Chunk\nimport java.util.stream.Stream\n\nclass TankBlock(settings: Settings) : Block(settings), BlockEntityProvider {\n\n    init {\n        this.defaultState = stateManager.defaultState.with(UP, false).with(DOWN, false)\n    }\n\n    override fun appendProperties(builder: StateManager.Builder<Block, BlockState>?) {\n        builder?.add(UP, DOWN)\n    }\n\n    override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity = TankBlockEntity(pos, state)\n\n    override fun <T : BlockEntity?> getTicker(\n        world: World,\n        state: BlockState?,\n        type: BlockEntityType<T>?\n    ): BlockEntityTicker<T>? {\n        return if (world.isClient) null\n        else BlockEntityTicker { world, pos, state, blockEntity -> TankBlockEntity.tick(world, pos, state, blockEntity as TankBlockEntity) }\n    }\n\n    override fun getOutlineShape(\n        state: BlockState,\n        world: BlockView?,\n        pos: BlockPos?,\n        context: ShapeContext?\n    ): VoxelShape {\n        val isDown = state[DOWN]\n        val isUp = state[UP]\n        return when {\n            !isUp && !isDown -> SINGLE_TANK_SHAPE\n            isUp && !isDown -> TANK_UP_SHAPE\n            !isUp && isDown -> TANK_DOWN_SHAPE\n            else -> TANK_BOTH_SHAPE\n        }\n    }\n\n    override fun onUse(\n        state: BlockState?,\n        world: World,\n        pos: BlockPos,\n        player: PlayerEntity,\n        hand: Hand,\n        hit: BlockHitResult\n    ): ActionResult {\n        if (world is ServerWorld) {\n            val storage = fluidStorageOf(world, pos, hit.side)\n            val inHand = ContainerItemContext.ofPlayerHand(player, hand).find(FluidStorage.ITEM)\n            var res = StorageUtil.move(storage, inHand, { true }, Long.MAX_VALUE, null)\n            if (res == 0L)\n                res = StorageUtil.move(inHand, storage, { true }, Long.MAX_VALUE, null)\n            return if (res > 0) ActionResult.SUCCESS else ActionResult.PASS\n        }\n        return ActionResult.CONSUME\n    }\n\n    override fun afterBreak(\n        world: World?,\n        player: PlayerEntity?,\n        pos: BlockPos?,\n        state: BlockState?,\n        blockEntity: BlockEntity?,\n        toolStack: ItemStack?\n    ) {\n        if (world?.isClient == true) return\n        player?.incrementStat(Stats.MINED.getOrCreateStat(this))\n        player?.addExhaustion(0.005f)\n        writeNbtComponents(world, player, pos, state, blockEntity, toolStack)\n    }\n\n    override fun onStateReplaced(\n        state: BlockState?,\n        world: World,\n        pos: BlockPos,\n        newState: BlockState?,\n        moved: Boolean\n    ) {\n        super.onStateReplaced(state, world, pos, newState, moved)\n    }\n\n    fun writeNbtComponents(\n        world: World?,\n        player: PlayerEntity?,\n        pos: BlockPos?,\n        state: BlockState?,\n        blockEntity: BlockEntity?,\n        toolStack: ItemStack?\n    ) {\n        if (world is ServerWorld) {\n            getDroppedStacks(state, world, pos, blockEntity, player, toolStack).forEach { stack ->\n                if (blockEntity is TankBlockEntity) {\n                    val tag = stack.orCreateNbt\n                    blockEntity.fluidComponent.toTag(tag)\n                }\n                dropStack(world, pos, stack)\n            }\n            state!!.onStacksDropped(world, pos, toolStack)\n        }\n    }\n\n    override fun getStateForNeighborUpdate(\n        state: BlockState,\n        direction: Direction?,\n        newState: BlockState,\n        world: WorldAccess,\n        pos: BlockPos,\n        posFrom: BlockPos\n    ): BlockState {\n        if (world.isClient) return state\n        val connects = isConnectable(world as World, pos, posFrom)\n        return when (direction) {\n            Direction.UP -> state.with(UP, newState.isOf(this) && newState[DOWN] && connects)\n            Direction.DOWN -> state.with(DOWN, newState.isOf(this) && newState[UP] && connects)\n            else -> state\n        }\n    }\n\n    override fun onPlaced(\n        world: World,\n        pos: BlockPos,\n        state: BlockState,\n        placer: LivingEntity?,\n        itemStack: ItemStack?\n    ) {\n        val tag = itemStack?.nbt\n        if (world.isClient) return\n        val tankEntity = world.getBlockEntity(pos) as? TankBlockEntity ?: return\n        if (tag?.isEmpty == false) {\n            tankEntity.fluidComponent.fromTag(tag)\n        }\n    }\n\n    override fun appendTooltip(\n        stack: ItemStack?,\n        world: BlockView?,\n        tooltip: MutableList<Text>?,\n        options: TooltipContext?\n    ) {\n        val tag = stack?.nbt\n        val tanksTag = tag?.getCompound(\"tanks\") ?: return\n        val volume = tanksTag.keys?.map { key ->\n            val tankTag = tanksTag.getCompound(key)\n            FluidVolume.fromTag(tankTag.getCompound(\"fluids\"))\n        }?.firstOrNull() ?: return\n        val fluid = volume.amount().asInt(1000)\n        tooltip?.addAll(volume.fluidKey.fullTooltip.toTypedArray())\n        tooltip?.add(LiteralText(\"$fluid / 8000 mB\"))\n    }\n\n    override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {\n        val blockPos = ctx.blockPos\n        val world = ctx.world\n        val fluidComponent = FluidComponent({ object : Syncable{\n            override fun markForUpdate(condition: () -> Boolean) {\n                TODO(\"Not yet implemented\")\n            }\n        } }, bucket, 1)\n        if (ctx.stack.nbt != null && !ctx.stack.nbt!!.isEmpty)\n            fluidComponent.fromTag(ctx.stack.nbt)\n        val connectsUp = isConnectable(world, fluidComponent, blockPos.up())\n        val connectsDown = isConnectable(world, fluidComponent, blockPos.down())\n        return defaultState\n            .with(UP, connectsUp && (!connectsDown || isConnectable(world, blockPos.down(), blockPos.up())))\n            .with(DOWN, connectsDown && (!connectsUp || isConnectable(world, blockPos.up(), blockPos.down())))\n    }\n\n    private fun isConnectable(world: World, pos: BlockPos, other: BlockPos): Boolean {\n        if (world.isClient) return false\n        val firstInv = fluidStorageOf(world, pos, Direction.UP) as? TankBlockEntity.CombinedTankStorage ?: return false\n        val secondInv = fluidStorageOf(world, pos, Direction.DOWN)  as? TankBlockEntity.CombinedTankStorage ?: return false\n        return if (firstInv.initialFluid.isBlank || secondInv.initialFluid.isBlank) true\n        else firstInv.initialFluid == secondInv.initialFluid\n    }\n\n    private fun isConnectable(world: World, firstInv: FluidComponent, other: BlockPos): Boolean {\n        if (world.isClient) return false\n        val secondInv = fluidStorageOf(world, other, Direction.UP)  as? TankBlockEntity.CombinedTankStorage ?: return false\n        return if (firstInv[0].isEmpty || secondInv.initialFluid.isBlank) true\n        else firstInv[0].variant == secondInv.initialFluid\n    }\n\n    override fun getPickStack(world: BlockView?, pos: BlockPos?, state: BlockState?): ItemStack {\n        val stack = super.getPickStack(world, pos, state)\n        val blockEntity = world?.getBlockEntity(pos) as? TankBlockEntity ?: return stack\n        blockEntity.fluidComponent.toTag(stack.orCreateNbt)\n        return stack\n    }\n\n    companion object {\n\n        val UP: BooleanProperty = BooleanProperty.of(\"up\")\n        val DOWN: BooleanProperty = BooleanProperty.of(\"down\")\n        \n        val SINGLE_TANK_SHAPE = Stream.of(\n            createCuboidShape(1.0, 14.0, 1.0, 15.0, 15.0, 15.0),\n            createCuboidShape(1.0, 0.0, 1.0, 15.0, 1.0, 15.0),\n            createCuboidShape(14.0, 1.0, 14.0, 15.0, 14.0, 15.0),\n            createCuboidShape(1.0, 1.0, 14.0, 2.0, 14.0, 15.0),\n            createCuboidShape(1.0, 1.0, 1.0, 2.0, 14.0, 2.0),\n            createCuboidShape(14.0, 1.0, 1.0, 15.0, 14.0, 2.0),\n            createCuboidShape(14.5, 1.0, 1.5, 14.7, 14.0, 14.5),\n            createCuboidShape(1.5, 1.0, 1.5, 1.7, 14.0, 14.5),\n            createCuboidShape(1.5, 1.0, 1.5, 14.5, 14.0, 1.7),\n            createCuboidShape(1.5, 1.0, 14.5, 14.5, 14.0, 14.7)\n        ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.get()\n\n        val TANK_DOWN_SHAPE = Stream.of(\n            createCuboidShape(1.0, 14.0, 1.0, 15.0, 15.0, 15.0),\n            createCuboidShape(14.0, 0.0, 14.0, 15.0, 15.0, 15.0),\n            createCuboidShape(1.0, 0.0, 14.0, 2.0, 15.0, 15.0),\n            createCuboidShape(1.0, 0.0, 1.0, 2.0, 15.0, 2.0),\n            createCuboidShape(14.0, 0.0, 1.0, 15.0, 15.0, 2.0),\n            createCuboidShape(14.5, 0.0, 1.5, 14.7, 15.0, 14.5),\n            createCuboidShape(1.5, 0.0, 1.5, 1.7, 15.0, 14.5),\n            createCuboidShape(1.5, 0.0, 1.5, 14.5, 15.0, 1.7),\n            createCuboidShape(1.5, 0.0, 14.5, 14.5, 15.0, 14.7)\n        ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.get()\n\n        val TANK_UP_SHAPE = Stream.of(\n            createCuboidShape(1.0, 0.0, 1.0, 15.0, 1.0, 15.0),\n            createCuboidShape(14.0, 1.0, 14.0, 15.0, 16.0, 15.0),\n            createCuboidShape(1.0, 1.0, 14.0, 2.0, 16.0, 15.0),\n            createCuboidShape(1.0, 1.0, 1.0, 2.0, 16.0, 2.0),\n            createCuboidShape(1.04, 1.0, 1.0, 15.0, 16.0, 2.0),\n            createCuboidShape(14.5, 1.0, 1.5, 14.7, 16.0, 14.5),\n            createCuboidShape(1.5, 1.0, 1.5, 1.7, 16.0, 14.5),\n            createCuboidShape(1.5, 1.0, 1.5, 14.5, 16.0, 1.7),\n            createCuboidShape(1.5, 1.0, 14.5, 14.5, 16.0, 14.7)\n        ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.get()\n\n        val TANK_BOTH_SHAPE = Stream.of(\n            createCuboidShape(14.0, 0.0, 14.0, 15.0, 16.0, 15.0),\n            createCuboidShape(1.0, 0.0, 14.0, 2.0, 16.0, 15.0),\n            createCuboidShape(1.0, 0.0, 1.0, 2.0, 16.0, 2.0),\n            createCuboidShape(14.0, 0.0, 1.0, 15.0, 16.0, 2.0),\n            createCuboidShape(14.5, 0.0, 1.5, 14.7, 16.0, 14.5),\n            createCuboidShape(1.5, 0.0, 1.5, 1.7, 16.0, 14.5),\n            createCuboidShape(1.5, 0.0, 1.5, 14.5, 16.0, 1.7),\n            createCuboidShape(1.5, 0.0, 14.5, 14.5, 16.0, 14.7)\n        ).reduce { v1, v2 -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR) }.get()\n\n        fun findAllTanks(chunk: Chunk, blockState: BlockState, pos: BlockPos, scanned: MutableSet<BlockPos>, to: TankBlockEntity.CombinedTankStorage) {\n            if (blockState.isOf(IRBlockRegistry.TANK_BLOCK) && scanned.add(pos)) {\n                to.add((chunk.getBlockEntity(pos) as TankBlockEntity))\n\n                if (blockState[UP])\n                    findAllTanks(chunk, chunk.getBlockState(pos.up()), pos.up(), scanned, to)\n                if (blockState[DOWN])\n                    findAllTanks(chunk, chunk.getBlockState(pos.down()), pos.down(), scanned, to)\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/misc/VerticalFacingBlock.kt",
    "content": "package me.steven.indrev.blocks.misc\n\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.item.ItemPlacementContext\nimport net.minecraft.state.StateManager\nimport net.minecraft.state.property.EnumProperty\nimport net.minecraft.util.math.Direction\n\nopen class VerticalFacingBlock(settings: Settings) : Block(settings) {\n\n    init {\n        this.defaultState = stateManager.defaultState.with(FACING, Direction.UP)\n    }\n\n    override fun appendProperties(builder: StateManager.Builder<Block, BlockState>?) {\n        builder?.add(FACING)\n    }\n\n    override fun getPlacementState(ctx: ItemPlacementContext?): BlockState? {\n        return defaultState.with(FACING, Direction.getEntityFacingOrder(ctx?.player).firstOrNull { it.axis.isVertical }?.opposite ?: Direction.UP)\n    }\n\n    companion object {\n        val FACING = EnumProperty.of(\"facing\", Direction::class.java, Direction.UP, Direction.DOWN)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/misc/WarningStrobeBlock.kt",
    "content": "package me.steven.indrev.blocks.misc\n\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.Blocks\nimport net.minecraft.block.ShapeContext\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.shape.VoxelShape\nimport net.minecraft.world.BlockView\nimport net.minecraft.world.WorldAccess\nimport net.minecraft.world.WorldView\n\nclass WarningStrobeBlock(settings: Settings) : Block(settings) {\n    override fun getOutlineShape(\n        state: BlockState?,\n        world: BlockView?,\n        pos: BlockPos?,\n        context: ShapeContext?\n    ): VoxelShape = SHAPE\n\n    override fun canPlaceAt(state: BlockState?, world: WorldView, pos: BlockPos): Boolean {\n        val blockBelow = world.getBlockState(pos.down())\n        return isFaceFullSquare(blockBelow.getCollisionShape(world, pos.down()), Direction.UP)\n    }\n\n    override fun getStateForNeighborUpdate(\n        state: BlockState,\n        direction: Direction?,\n        newState: BlockState?,\n        world: WorldAccess?,\n        pos: BlockPos?,\n        posFrom: BlockPos?\n    ): BlockState? {\n        return if (!state.canPlaceAt(world, pos)) Blocks.AIR.defaultState else state\n    }\n\n    companion object {\n        private val SHAPE = createCuboidShape(5.0, 0.0, 5.0, 11.0, 6.0, 11.0)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/models/LazuliFluxContainerBakedModel.kt",
    "content": "package me.steven.indrev.blocks.models\n\nimport me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity\nimport me.steven.indrev.utils.blockSpriteId\nimport net.fabricmc.fabric.api.renderer.v1.Renderer\nimport net.fabricmc.fabric.api.renderer.v1.RendererAccess\nimport net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder\nimport net.fabricmc.fabric.api.renderer.v1.render.RenderContext\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.BlockRenderView\nimport java.util.*\nimport java.util.function.Supplier\n\nclass LazuliFluxContainerBakedModel(id: String) : MachineBakedModel(\"lazuli_flux_container\") {\n\n    init {\n        overlayIds.addAll(\n            arrayOf(\n                blockSpriteId(\"block/lazuli_flux_container\"),\n                blockSpriteId(\"block/lazuli_flux_container_input\"),\n                blockSpriteId(\"block/lazuli_flux_container_output\"),\n                blockSpriteId(\"block/lazuli_flux_container_item_lf_level\"),\n                blockSpriteId(\"block/${id}_overlay\")\n            )\n        )\n    }\n\n    private val color: Int = when (id.last()) {\n        '1' -> 0xffbb19\n        '2' -> 0x5d3dff\n        '3' -> 0xfd47ff\n        else -> 0xff4070\n    }\n\n    override fun buildDefaultMesh() {\n        val renderer: Renderer = RendererAccess.INSTANCE.renderer!!\n        val builder: MeshBuilder = renderer.meshBuilder()\n        val emitter = builder.emitter\n\n        for (direction in Direction.values()) {\n            emitter.draw(direction, baseSprite!!, -1)\n            emitter.draw(direction, overlays[4]!!, -1)\n        }\n        defaultMesh = builder.build()\n    }\n\n    override fun emitBlockQuads(\n        blockView: BlockRenderView,\n        state: BlockState,\n        pos: BlockPos,\n        randomSupplier: Supplier<Random>,\n        ctx: RenderContext\n    ) {\n        super.emitBlockQuads(blockView, state, pos, randomSupplier, ctx)\n        val blockEntity = blockView.getBlockEntity(pos) as? LazuliFluxContainerBlockEntity ?: return\n\n        val emitter = ctx.emitter\n        blockEntity.transferConfig.forEach { side, mode ->\n            if (mode.input) {\n                emitter.draw(side, overlays[1]!!)\n            } else if (mode.output) {\n                emitter.draw(side, overlays[2]!!)\n            }\n        }\n    }\n\n    private fun emitHorizontalQuads(sprite: Sprite, ctx: RenderContext) {\n        ctx.emitter.run {\n            draw(Direction.NORTH, sprite, 255 shl 24 or color)\n            draw(Direction.SOUTH, sprite, 255 shl 24 or color)\n            draw(Direction.EAST, sprite, 255 shl 24 or color)\n            draw(Direction.WEST, sprite, 255 shl 24 or color)\n        }\n    }\n\n    override fun emitItemQuads(stack: ItemStack?, randomSupplier: Supplier<Random>?, ctx: RenderContext) {\n        super.emitItemQuads(stack, randomSupplier, ctx)\n        emitHorizontalQuads(overlays[3]!!, ctx)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/models/MachineBakedModel.kt",
    "content": "package me.steven.indrev.blocks.models\n\nimport com.mojang.datafixers.util.Pair\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blocks.machine.MachineBlock\nimport me.steven.indrev.utils.blockSpriteId\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.renderer.v1.Renderer\nimport net.fabricmc.fabric.api.renderer.v1.RendererAccess\nimport net.fabricmc.fabric.api.renderer.v1.material.BlendMode\nimport net.fabricmc.fabric.api.renderer.v1.mesh.Mesh\nimport net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder\nimport net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView\nimport net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter\nimport net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel\nimport net.fabricmc.fabric.api.renderer.v1.render.RenderContext\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.model.*\nimport net.minecraft.client.render.model.json.ModelOverrideList\nimport net.minecraft.client.render.model.json.ModelTransformation\nimport net.minecraft.client.texture.MissingSprite\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.client.util.ModelIdentifier\nimport net.minecraft.client.util.SpriteIdentifier\nimport net.minecraft.item.ItemStack\nimport net.minecraft.screen.PlayerScreenHandler\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.math.Vec3f\nimport net.minecraft.world.BlockRenderView\nimport java.util.*\nimport java.util.function.Function\nimport java.util.function.Supplier\n\n\nopen class MachineBakedModel(val id: String) : UnbakedModel, BakedModel, FabricBakedModel {\n\n    var baseSpriteId = SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, identifier(\"block/${id.replace(Regex(\"_mk[0-4]\"), \"\")}\"))\n\n    var overlayIds: MutableList<SpriteIdentifier> = mutableListOf()\n    var workingOverlayIds: MutableList<SpriteIdentifier> = mutableListOf()\n\n    var baseSprite: Sprite? = null\n    val overlays: Array<Sprite?> by lazy { arrayOfNulls(overlayIds.size) }\n    val workingOverlays: Array<Sprite?> by lazy { arrayOfNulls(workingOverlayIds.size) }\n\n    val emissives = hashSetOf<Sprite>()\n\n    var defaultMesh: Mesh? = null\n    var workingStateMesh: Mesh? = null\n\n    fun factoryOverlay() {\n        overlayIds.add(blockSpriteId(\"block/factory_overlay\"))\n    }\n\n    fun tierOverlay(tier: Tier) {\n        overlayIds.add(blockSpriteId(\"block/${tier.toString().lowercase(Locale.getDefault())}_overlay\"))\n    }\n\n    override fun bake(\n        loader: ModelLoader,\n        textureGetter: Function<SpriteIdentifier, Sprite>,\n        rotationContainer: ModelBakeSettings?,\n        modelId: Identifier?\n    ): BakedModel {\n        baseSprite = textureGetter.apply(baseSpriteId)\n        overlayIds.processSprites(overlays, textureGetter)\n        workingOverlayIds.processSprites(workingOverlays, textureGetter)\n        if (isEmissive(baseSprite)) emissives.add(baseSprite!!)\n\n        buildDefaultMesh()\n        buildWorkingStateMesh()\n\n        return this\n    }\n\n    open fun buildDefaultMesh() {\n        val renderer: Renderer = RendererAccess.INSTANCE.renderer!!\n        val builder: MeshBuilder = renderer.meshBuilder()\n        val emitter = builder.emitter\n\n        for (direction in Direction.values()) {\n            emitter.draw(direction, baseSprite!!, -1)\n            overlays.forEach { overlay -> emitter.draw(direction, overlay!!, -1) }\n        }\n        defaultMesh = builder.build()\n    }\n\n     fun buildWorkingStateMesh() {\n        val renderer: Renderer = RendererAccess.INSTANCE.renderer!!\n        val builder: MeshBuilder = renderer.meshBuilder()\n        val emitter = builder.emitter\n\n        for (direction in Direction.values()) {\n            emitter.draw(direction, baseSprite!!, -1)\n            workingOverlays.forEach { overlay -> emitter.draw(direction, overlay!!, -1) }\n            overlays.forEach { overlay -> emitter.draw(direction, overlay!!, -1) }\n        }\n        workingStateMesh = builder.build()\n    }\n\n    private fun List<SpriteIdentifier>.processSprites(arr: Array<Sprite?>, textureGetter: Function<SpriteIdentifier, Sprite>) {\n        forEachIndexed { index, id ->\n            val sprite = textureGetter.apply(id)\n            if (sprite.id != MissingSprite.getMissingSpriteId()) {\n                arr[index] = sprite\n                if (isEmissive(sprite))\n                    emissives.add(sprite)\n            }\n        }\n    }\n\n    //don't judge me\n    fun isEmissive(sprite: Sprite?) = sprite?.id?.toString()?.contains(\"emissive\") == true\n\n    override fun getModelDependencies(): MutableCollection<Identifier> = mutableListOf()\n\n    override fun getTextureDependencies(\n        unbakedModelGetter: Function<Identifier, UnbakedModel>?,\n        unresolvedTextureReferences: MutableSet<Pair<String, String>>?\n    ): MutableCollection<SpriteIdentifier> {\n        val list = mutableListOf(baseSpriteId)\n        list.addAll(overlayIds)\n        list.addAll(workingOverlayIds)\n        return list\n    }\n\n    override fun getQuads(state: BlockState?, face: Direction?, random: Random?): MutableList<BakedQuad> =\n        mutableListOf()\n\n    override fun useAmbientOcclusion(): Boolean = true\n\n    override fun hasDepth(): Boolean = false\n\n    override fun isSideLit(): Boolean = true\n\n    override fun isBuiltin(): Boolean = false\n\n    override fun getParticleSprite(): Sprite? = baseSprite\n\n    override fun getTransformation(): ModelTransformation = TRANSFORM\n\n    override fun getOverrides(): ModelOverrideList = ModelOverrideList.EMPTY\n\n    override fun isVanillaAdapter(): Boolean = false\n\n    override fun emitBlockQuads(\n        blockView: BlockRenderView,\n        state: BlockState,\n        pos: BlockPos,\n        randomSupplier: Supplier<Random>,\n        ctx: RenderContext\n    ) {\n        val block = state.block as? MachineBlock ?: return\n        val direction = block.getFacing(state)\n        val blockEntity = blockView.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return\n        ctx.pushTransform(rotateQuads(direction))\n        val m = if (blockEntity.workingState) workingStateMesh else defaultMesh\n        ctx.meshConsumer().accept(m)\n        ctx.popTransform()\n    }\n\n    override fun emitItemQuads(stack: ItemStack?, randomSupplier: Supplier<Random>?, ctx: RenderContext) {\n        ctx.meshConsumer().accept(defaultMesh)\n    }\n\n    protected fun QuadEmitter.draw(\n        side: Direction,\n        sprite: Sprite,\n        color: Int = -1\n    ) {\n        square(side, 0f, 0f, 1f, 1f, 0f)\n        spriteBake(0, sprite, MutableQuadView.BAKE_LOCK_UV)\n        spriteColor(0, color, color, color, color)\n        if (emissives.contains(sprite))\n            material(MATERIAL)\n        val uv =\n            if (sprite.width == 16 && sprite.height == 16) MachineTextureUV.FULL\n            else MachineTextureUV.BY_DIRECTION[side]!!\n        sprite(0, 0, sprite.getFrameU(uv.u1.toDouble()), sprite.getFrameV(uv.v1.toDouble()))\n        sprite(1, 0, sprite.getFrameU(uv.u1.toDouble()), sprite.getFrameV(uv.v2.toDouble()))\n        sprite(2, 0, sprite.getFrameU(uv.u2.toDouble()), sprite.getFrameV(uv.v2.toDouble()))\n        sprite(3, 0, sprite.getFrameU(uv.u2.toDouble()), sprite.getFrameV(uv.v1.toDouble()))\n        emit()\n    }\n\n    /**\n     * Original code belongs to Haven-King.\n     * Source: https://github.com/Haven-King/Automotion\n     */\n    fun rotateQuads(direction: Direction): RenderContext.QuadTransform = RenderContext.QuadTransform { q ->\n        val rotate = Vec3f.POSITIVE_Y.getDegreesQuaternion(\n            when (direction) {\n                Direction.NORTH -> 0f\n                Direction.EAST -> 270f\n                Direction.SOUTH -> 180f\n                Direction.WEST -> 90f\n                else -> 0f\n            }\n        )\n\n        val tmp = Vec3f()\n        for (i in 0..3) {\n            q.copyPos(i, tmp)\n            tmp.add(-0.5f, -0.5f, -0.5f)\n            tmp.rotate(rotate)\n            tmp.add(0.5f, 0.5f, 0.5f)\n            q.pos(i, tmp)\n\n            if (q.hasNormal(i)) {\n                q.copyNormal(i, tmp)\n                tmp.rotate(rotate)\n                q.normal(i, tmp)\n            }\n        }\n        q.cullFace(null)\n        q.nominalFace(direction)\n        true\n    }\n\n    enum class MachineTextureUV(val direction: Direction?, val u1: Float, val v1: Float, val u2: Float, val v2: Float) {\n        FRONT(Direction.NORTH, 16f / 48f * 16f, 16f / 48f * 16f, 32f / 48f * 16f, 32f / 48f * 16f),\n        LEFT(Direction.EAST, 0.0f, 16f / 48f * 16f, 16f / 48f * 16f, 32f / 48f * 16f),\n        BACK(Direction.SOUTH, 32f / 48f * 16f, 32f / 48f * 16f, 16.0f, 16f),\n        RIGHT(Direction.WEST, 32f / 48f * 16f, 16f / 48f * 16f, 16.0f, 32f / 48f * 16f),\n        TOP(Direction.UP, 16f / 48f * 16f, 0.0f, 32f / 48f * 16f, 16f / 48f * 16f),\n        BOTTOM(Direction.DOWN, 16f / 48f * 16f, 32f / 48f * 16f, 32f / 48f * 16f, 16.0f),\n        FULL(null, 0f, 0f, 16f, 16f);\n\n        companion object {\n            val BY_DIRECTION = values().associateBy { it.direction }\n        }\n    }\n\n    companion object {\n        val MATERIAL by lazy {\n            RendererAccess.INSTANCE.renderer?.materialFinder()!!.clear()\n                .spriteDepth(1)\n                .blendMode(0, BlendMode.CUTOUT)\n                .disableAo(0, true)\n                .disableDiffuse(0, true)\n                .emissive(0, true)\n                ?.find()\n        }\n        val TRANSFORM: ModelTransformation by lazy {\n            MinecraftClient.getInstance().bakedModelManager.getModel(\n                ModelIdentifier(Identifier(\"stone\"), \"\")\n            ).transformation\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/models/MinerBakedModel.kt",
    "content": "package me.steven.indrev.blocks.models\n\nimport com.mojang.datafixers.util.Pair\nimport me.steven.indrev.blockentities.miningrig.MiningRigBlockEntity\nimport me.steven.indrev.blocks.machine.MachineBlock\nimport me.steven.indrev.utils.blockSpriteId\nimport net.fabricmc.fabric.api.renderer.v1.render.RenderContext\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.render.model.BakedModel\nimport net.minecraft.client.render.model.ModelBakeSettings\nimport net.minecraft.client.render.model.ModelLoader\nimport net.minecraft.client.render.model.UnbakedModel\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.client.util.SpriteIdentifier\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.BlockRenderView\nimport java.util.*\nimport java.util.function.Function\nimport java.util.function.Supplier\n\nclass MinerBakedModel(id: String) : MachineBakedModel(id) {\n\n    private val screenSpriteId = blockSpriteId(\"block/mining_rig_screen_emissive\")\n    var screenSprite: Sprite? = null\n\n    override fun bake(\n        loader: ModelLoader,\n        textureGetter: Function<SpriteIdentifier, Sprite>,\n        rotationContainer: ModelBakeSettings?,\n        modelId: Identifier?\n    ): BakedModel {\n        screenSprite = textureGetter.apply(screenSpriteId)\n        emissives.add(screenSprite!!)\n        return super.bake(loader, textureGetter, rotationContainer, modelId)\n    }\n\n    override fun getTextureDependencies(\n        unbakedModelGetter: Function<Identifier, UnbakedModel>?,\n        unresolvedTextureReferences: MutableSet<Pair<String, String>>?\n    ): MutableCollection<SpriteIdentifier> {\n        val deps = super.getTextureDependencies(unbakedModelGetter, unresolvedTextureReferences)\n        deps.add(screenSpriteId)\n        return deps\n    }\n\n    override fun emitBlockQuads(\n        blockView: BlockRenderView,\n        state: BlockState,\n        pos: BlockPos,\n        randomSupplier: Supplier<Random>,\n        ctx: RenderContext\n    ) {\n        super.emitBlockQuads(blockView, state, pos, randomSupplier, ctx)\n        val block = state.block as? MachineBlock ?: return\n        val direction = block.getFacing(state)\n        val blockEntity = blockView.getBlockEntity(pos) as? MiningRigBlockEntity ?: return\n\n        if (blockEntity.workingState)\n            ctx.emitter.draw(direction, screenSprite!!)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/models/PumpPipeBakedModel.kt",
    "content": "package me.steven.indrev.blocks.models\n\nimport com.mojang.datafixers.util.Pair\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.render.model.BakedModel\nimport net.minecraft.client.render.model.ModelBakeSettings\nimport net.minecraft.client.render.model.ModelLoader\nimport net.minecraft.client.render.model.UnbakedModel\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.client.util.SpriteIdentifier\nimport net.minecraft.screen.PlayerScreenHandler\nimport net.minecraft.util.Identifier\nimport java.util.function.Function\n\nclass PumpPipeBakedModel : UnbakedModel {\n\n    private val spriteIdCollection = mutableListOf(\n        SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, identifier(\"block/pump_pipe\"))\n    )\n\n    private val modelIdentifier = identifier(\"block/pump_pipe\")\n    private var bakedModel: BakedModel? = null\n\n    override fun bake(\n        loader: ModelLoader,\n        textureGetter: Function<SpriteIdentifier, Sprite>,\n        rotationContainer: ModelBakeSettings?,\n        modelId: Identifier?\n    ): BakedModel? {\n        bakedModel = loader.getOrLoadModel(modelIdentifier).bake(loader, textureGetter, rotationContainer, modelId)\n        return bakedModel\n    }\n\n    override fun getModelDependencies(): MutableCollection<Identifier> = mutableListOf()\n\n    override fun getTextureDependencies(\n        unbakedModelGetter: Function<Identifier, UnbakedModel>?,\n        unresolvedTextureReferences: MutableSet<Pair<String, String>>?\n    ): MutableCollection<SpriteIdentifier> {\n        return spriteIdCollection\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/models/pipes/BasePipeModel.kt",
    "content": "package me.steven.indrev.blocks.models.pipes\n\nimport com.mojang.datafixers.util.Pair\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.cables.BasePipeBlockEntity\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry\nimport net.fabricmc.fabric.api.renderer.v1.Renderer\nimport net.fabricmc.fabric.api.renderer.v1.RendererAccess\nimport net.fabricmc.fabric.api.renderer.v1.material.BlendMode\nimport net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial\nimport net.fabricmc.fabric.api.renderer.v1.mesh.Mesh\nimport net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder\nimport net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel\nimport net.fabricmc.fabric.api.renderer.v1.render.RenderContext\nimport net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.RenderLayers\nimport net.minecraft.client.render.model.*\nimport net.minecraft.client.render.model.json.ModelOverrideList\nimport net.minecraft.client.render.model.json.ModelTransformation\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.client.util.SpriteIdentifier\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.BlockRenderView\nimport java.util.*\nimport java.util.function.Function\nimport java.util.function.Supplier\n\nabstract class BasePipeModel(val tier: Tier, val type: String) : BakedModel, FabricBakedModel, UnbakedModel {\n\n    abstract val spriteIdCollection: MutableList<SpriteIdentifier>\n    private val modelIdCollection = mutableListOf(\n        identifier(\"block/${type}_center_${tier.toString().lowercase()}\"),\n        identifier(\"block/${type}_side_${tier.toString().lowercase()}\")\n    )\n    val modelArray = arrayOfNulls<BakedModel>(7)\n    val spriteArray = arrayOfNulls<Sprite>(4)\n    protected val meshArray = arrayOfNulls<Mesh>(7)\n    lateinit var transform: ModelTransformation\n\n    override fun bake(\n        loader: ModelLoader,\n        textureGetter: Function<SpriteIdentifier, Sprite>,\n        rotationContainer: ModelBakeSettings?,\n        modelId: Identifier?\n    ): BakedModel {\n        spriteIdCollection.forEachIndexed { idx, spriteIdentifier ->\n            spriteArray[idx] = textureGetter.apply(spriteIdentifier)\n        }\n\n        val center = loader.getOrLoadModel(modelIdCollection[0]).bake(loader, textureGetter, rotationContainer, modelId)!!\n        meshArray[0] = buildDefaultMesh(0, center)\n        transform = center.transformation\n        val sideModel = loader.getOrLoadModel(modelIdCollection[1])\n        meshArray[1] = buildDefaultMesh(1, sideModel.bake(loader, textureGetter, ModelRotation.X270_Y0, modelId)!!) // NORTH\n        meshArray[2] = buildDefaultMesh(2, sideModel.bake(loader, textureGetter, ModelRotation.X270_Y90, modelId)!!) // EAST\n        meshArray[3] = buildDefaultMesh(3, sideModel.bake(loader, textureGetter, ModelRotation.X270_Y180, modelId)!!)// SOUTH\n        meshArray[4] = buildDefaultMesh(4, sideModel.bake(loader, textureGetter, ModelRotation.X270_Y270, modelId)!!)// WEST\n        meshArray[5] = buildDefaultMesh(5, sideModel.bake(loader, textureGetter, ModelRotation.X180_Y0, modelId)!!) // UP\n        meshArray[6] = buildDefaultMesh(6, sideModel.bake(loader, textureGetter, ModelRotation.X0_Y0, modelId)!!) // DOWN\n\n        return this\n    }\n\n    open fun buildDefaultMesh(index: Int, model: BakedModel): Mesh {\n        modelArray[index] = model\n        val renderer: Renderer = RendererAccess.INSTANCE.renderer!!\n        val builder: MeshBuilder = renderer.meshBuilder()\n        val emitter = builder.emitter\n        model.getQuads(null, null, null).forEach { q ->\n            emitter.fromVanilla(q, null, null)\n            emitter.emit()\n        }\n        return builder.build()\n    }\n\n    /**\n     * Used for DashLoader compat\n     */\n    fun buildMeshes() {\n        modelArray.forEachIndexed { index, model -> meshArray[index] = buildDefaultMesh(index, model!!) }\n    }\n\n    override fun getModelDependencies(): MutableCollection<Identifier> = modelIdCollection\n\n    override fun getTextureDependencies(\n        unbakedModelGetter: Function<Identifier, UnbakedModel>?,\n        unresolvedTextureReferences: MutableSet<Pair<String, String>>?\n    ): MutableCollection<SpriteIdentifier> = spriteIdCollection\n\n    override fun getQuads(state: BlockState?, face: Direction?, random: Random?): MutableList<BakedQuad> = mutableListOf()\n\n    override fun useAmbientOcclusion(): Boolean = true\n\n    override fun hasDepth(): Boolean = false\n\n    override fun isSideLit(): Boolean = true\n\n    override fun isBuiltin(): Boolean = false\n\n    override fun getParticleSprite(): Sprite = spriteArray[0]!!\n\n    override fun getTransformation(): ModelTransformation = transform\n\n    override fun getOverrides(): ModelOverrideList = ModelOverrideList.EMPTY\n\n    override fun isVanillaAdapter(): Boolean = false\n\n    override fun emitBlockQuads(\n        world: BlockRenderView,\n        state: BlockState,\n        pos: BlockPos,\n        randSupplier: Supplier<Random>,\n        context: RenderContext\n    ) {\n        val renderData = (world as RenderAttachedBlockView).getBlockEntityRenderAttachment(pos) as? BasePipeBlockEntity.PipeRenderData ?: return\n        if (renderData.cover != null) {\n            val coverState = renderData.cover\n            val model = MinecraftClient.getInstance().bakedModelManager.blockModels.getModel(coverState)\n            val color =\n                255 shl 24 or (ColorProviderRegistry.BLOCK[coverState.block]?.getColor(coverState, world, pos, 0) ?: -1)\n\n            val emitter = context.emitter\n            val renderLayer = BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(coverState))\n            val material = RENDER_LAYER_MATERIAL_MAP.computeIfAbsent(renderLayer) {\n                RendererAccess.INSTANCE.renderer!!.materialFinder().blendMode(0, renderLayer).find()\n            }\n            DIRECTIONS.forEach { dir ->\n                model.getQuads(coverState, dir, randSupplier.get()).forEach { quad ->\n                    emitter.fromVanilla(quad, material, dir)\n                    if (quad.hasColor()) {\n                        emitter.spriteColor(0, color, color, color, color)\n                    }\n                    emitter.emit()\n                }\n            }\n            if (coverState.isOpaque) return\n        }\n\n        context.meshConsumer().accept(meshArray[0])\n        if (renderData.connections.contains(Direction.NORTH)) context.meshConsumer().accept(meshArray[1])\n        if (renderData.connections.contains(Direction.EAST)) context.meshConsumer().accept(meshArray[2])\n        if (renderData.connections.contains(Direction.SOUTH)) context.meshConsumer().accept(meshArray[3])\n        if (renderData.connections.contains(Direction.WEST)) context.meshConsumer().accept(meshArray[4])\n        if (renderData.connections.contains(Direction.UP)) context.meshConsumer().accept(meshArray[5])\n        if (renderData.connections.contains(Direction.DOWN)) context.meshConsumer().accept(meshArray[6])\n    }\n\n    override fun emitItemQuads(stack: ItemStack?, p1: Supplier<Random>, context: RenderContext) {\n        context.meshConsumer().accept(meshArray[0])\n    }\n\n    companion object {\n        val RENDER_LAYER_MATERIAL_MAP = hashMapOf<BlendMode, RenderMaterial>()\n        val DIRECTIONS = Direction.values()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/models/pipes/CableModel.kt",
    "content": "package me.steven.indrev.blocks.models.pipes\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.utils.blockSpriteId\nimport net.fabricmc.fabric.api.renderer.v1.Renderer\nimport net.fabricmc.fabric.api.renderer.v1.RendererAccess\nimport net.fabricmc.fabric.api.renderer.v1.material.BlendMode\nimport net.fabricmc.fabric.api.renderer.v1.mesh.Mesh\nimport net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder\nimport net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView\nimport net.minecraft.client.render.model.BakedModel\n\nclass CableModel(tier: Tier) : BasePipeModel(tier, \"cable\") {\n    override val spriteIdCollection = mutableListOf(\n        blockSpriteId(\"block/cable_center\"),\n        blockSpriteId(\"block/cable_center_emissive_${tier.toString().lowercase()}\"),\n        blockSpriteId(\"block/cable_wrap\"),\n        blockSpriteId(\"block/cable_wire_emissive_${tier.toString().lowercase()}\")\n    )\n\n    override fun buildDefaultMesh(index: Int, model: BakedModel): Mesh {\n        val renderer: Renderer = RendererAccess.INSTANCE.renderer!!\n        val builder: MeshBuilder = renderer.meshBuilder()\n        val emitter = builder.emitter\n        if (index == 0) {\n            modelArray[index] = model\n            val sprite = spriteArray[1]!!\n            model.getQuads(null, null, null).forEach { q ->\n                emitter.fromVanilla(q, null, null)\n                emitter.emit()\n                emitter.fromVanilla(q, null, null)\n                emitter.spriteBake(0, sprite, MutableQuadView.BAKE_LOCK_UV)\n                emitter.sprite(0, 0, sprite.getFrameU(3.0), sprite.getFrameV(3.0))\n                emitter.sprite(1, 0, sprite.getFrameU(3.0), sprite.getFrameV(13.0))\n                emitter.sprite(2, 0, sprite.getFrameU(13.0), sprite.getFrameV(13.0))\n                emitter.sprite(3, 0, sprite.getFrameU(13.0), sprite.getFrameV(3.0))\n                emitter.material(CENTER_MATERIAL)\n                emitter.emit()\n            }\n            return builder.build()\n        }\n        return super.buildDefaultMesh(index, model)\n    }\n\n    companion object {\n        val CENTER_MATERIAL by lazy {\n            RendererAccess.INSTANCE.renderer?.materialFinder()!!.clear()\n                .spriteDepth(1)\n                .blendMode(0, BlendMode.CUTOUT)\n                .disableAo(0, true)\n                .disableDiffuse(0, true)\n                .emissive(0, true)\n                ?.find()\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/models/pipes/FluidPipeModel.kt",
    "content": "package me.steven.indrev.blocks.models.pipes\n\nimport me.steven.indrev.IndustrialRevolutionClient\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.networks.EndpointData\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.client.node.ClientServoNodeInfo\nimport me.steven.indrev.networks.client.node.to\nimport me.steven.indrev.utils.blockSpriteId\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.renderer.v1.render.RenderContext\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.render.model.BakedModel\nimport net.minecraft.client.render.model.ModelBakeSettings\nimport net.minecraft.client.render.model.ModelLoader\nimport net.minecraft.client.render.model.ModelRotation\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.client.util.SpriteIdentifier\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.BlockRenderView\nimport java.util.*\nimport java.util.function.Function\nimport java.util.function.Supplier\n\nclass FluidPipeModel(tier: Tier) : BasePipeModel(tier, \"fluid_pipe\") {\n    override val spriteIdCollection: MutableList<SpriteIdentifier> = mutableListOf(\n        blockSpriteId(\"block/fluid_pipe_center_${tier.toString().lowercase()}\"),\n        blockSpriteId(\"block/fluid_pipe_side_${tier.toString().lowercase()}\"),\n        blockSpriteId(\"block/servo_retriever\"),\n        blockSpriteId(\"block/servo_output\")\n    )\n\n\n    val retrieverServoModels = arrayOfNulls<BakedModel>(6)\n    val outputServoModels = arrayOfNulls<BakedModel>(6)\n\n    override fun bake(\n        loader: ModelLoader,\n        textureGetter: Function<SpriteIdentifier, Sprite>,\n        rotationContainer: ModelBakeSettings?,\n        modelId: Identifier?\n    ): BakedModel {\n        super.bake(loader, textureGetter, rotationContainer, modelId)\n\n        val retrieverModel = loader.getOrLoadModel( identifier(\"block/servo_retriever\"))\n        retrieverServoModels[0] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y0, modelId) // NORTH\n        retrieverServoModels[1] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y90, modelId) // EAST\n        retrieverServoModels[2] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y180, modelId) // SOUTH\n        retrieverServoModels[3] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y270, modelId) // WEST\n        retrieverServoModels[4] = retrieverModel.bake(loader, textureGetter, ModelRotation.X180_Y0, modelId) // UP\n        retrieverServoModels[5] = retrieverModel.bake(loader, textureGetter, ModelRotation.X0_Y0, modelId) // DOWN\n\n        val outputModel = loader.getOrLoadModel( identifier(\"block/servo_output\"))\n        outputServoModels[0] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y0, modelId) // NORTH\n        outputServoModels[1] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y90, modelId) // EAST\n        outputServoModels[2] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y180, modelId) // SOUTH\n        outputServoModels[3] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y270, modelId) // WEST\n        outputServoModels[4] = outputModel.bake(loader, textureGetter, ModelRotation.X180_Y0, modelId) // UP\n        outputServoModels[5] = outputModel.bake(loader, textureGetter, ModelRotation.X0_Y0, modelId) // DOWN\n\n        return this\n    }\n\n    override fun emitBlockQuads(\n        world: BlockRenderView,\n        state: BlockState,\n        pos: BlockPos,\n        randSupplier: Supplier<Random>,\n        context: RenderContext\n    ) {\n        super.emitBlockQuads(world, state, pos, randSupplier, context)\n        IndustrialRevolutionClient.CLIENT_NETWORK_STATE[Network.Type.FLUID]?.get(pos)?.to<ClientServoNodeInfo>()?.servos?.forEach { (dir, type) ->\n            val index = when (dir!!) {\n                Direction.DOWN -> 5\n                Direction.UP -> 4\n                Direction.NORTH -> 0\n                Direction.SOUTH -> 2\n                Direction.WEST -> 3\n                Direction.EAST -> 1\n            }\n\n            val model = when (type) {\n                EndpointData.Type.RETRIEVER -> retrieverServoModels\n                EndpointData.Type.OUTPUT -> outputServoModels\n                else -> return\n            }[index]\n            context.fallbackConsumer().accept(model)\n        }\n    }\n\n    override fun emitItemQuads(stack: ItemStack?, p1: Supplier<Random>, context: RenderContext) {\n        super.emitItemQuads(stack, p1, context)\n\n        context.meshConsumer().accept(meshArray[1])\n        context.meshConsumer().accept(meshArray[3])\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/blocks/models/pipes/ItemPipeModel.kt",
    "content": "package me.steven.indrev.blocks.models.pipes\n\nimport me.steven.indrev.IndustrialRevolutionClient\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.networks.EndpointData\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.client.node.ClientServoNodeInfo\nimport me.steven.indrev.networks.client.node.to\nimport me.steven.indrev.utils.blockSpriteId\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.renderer.v1.render.RenderContext\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.render.model.BakedModel\nimport net.minecraft.client.render.model.ModelBakeSettings\nimport net.minecraft.client.render.model.ModelLoader\nimport net.minecraft.client.render.model.ModelRotation\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.client.util.SpriteIdentifier\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.BlockRenderView\nimport java.util.*\nimport java.util.function.Function\nimport java.util.function.Supplier\n\nclass ItemPipeModel(tier: Tier) : BasePipeModel(tier, \"item_pipe\") {\n    override val spriteIdCollection: MutableList<SpriteIdentifier> = mutableListOf(\n        blockSpriteId(\"block/item_pipe_center_${tier.toString().lowercase()}\"),\n        blockSpriteId(\"block/item_pipe_side_${tier.toString().lowercase()}\"),\n        blockSpriteId(\"block/servo_retriever\"),\n        blockSpriteId(\"block/servo_output\")\n    )\n\n    val retrieverServoModels = arrayOfNulls<BakedModel>(6)\n    val outputServoModels = arrayOfNulls<BakedModel>(6)\n\n    override fun bake(\n        loader: ModelLoader,\n        textureGetter: Function<SpriteIdentifier, Sprite>,\n        rotationContainer: ModelBakeSettings?,\n        modelId: Identifier?\n    ): BakedModel {\n        super.bake(loader, textureGetter, rotationContainer, modelId)\n\n        val retrieverModel = loader.getOrLoadModel(identifier(\"block/servo_retriever_item\"))\n        retrieverServoModels[0] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y0, modelId) // NORTH\n        retrieverServoModels[1] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y90, modelId) // EAST\n        retrieverServoModels[2] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y180, modelId) // SOUTH\n        retrieverServoModels[3] = retrieverModel.bake(loader, textureGetter, ModelRotation.X270_Y270, modelId) // WEST\n        retrieverServoModels[4] = retrieverModel.bake(loader, textureGetter, ModelRotation.X180_Y0, modelId) // UP\n        retrieverServoModels[5] = retrieverModel.bake(loader, textureGetter, ModelRotation.X0_Y0, modelId) // DOWN\n\n        val outputModel = loader.getOrLoadModel(identifier(\"block/servo_output_item\"))\n        outputServoModels[0] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y0, modelId) // NORTH\n        outputServoModels[1] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y90, modelId) // EAST\n        outputServoModels[2] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y180, modelId) // SOUTH\n        outputServoModels[3] = outputModel.bake(loader, textureGetter, ModelRotation.X270_Y270, modelId) // WEST\n        outputServoModels[4] = outputModel.bake(loader, textureGetter, ModelRotation.X180_Y0, modelId) // UP\n        outputServoModels[5] = outputModel.bake(loader, textureGetter, ModelRotation.X0_Y0, modelId) // DOWN\n\n        return this\n    }\n\n    override fun emitBlockQuads(\n        world: BlockRenderView,\n        state: BlockState,\n        pos: BlockPos,\n        randSupplier: Supplier<Random>,\n        context: RenderContext\n    ) {\n        super.emitBlockQuads(world, state, pos, randSupplier, context)\n\n        IndustrialRevolutionClient.CLIENT_NETWORK_STATE[Network.Type.ITEM]?.get(pos)?.to<ClientServoNodeInfo>()?.servos?.forEach { (dir, type) ->\n            val index = when (dir!!) {\n                Direction.DOWN -> 5\n                Direction.UP -> 4\n                Direction.NORTH -> 0\n                Direction.SOUTH -> 2\n                Direction.WEST -> 3\n                Direction.EAST -> 1\n            }\n\n            val model = when (type) {\n                EndpointData.Type.RETRIEVER -> retrieverServoModels\n                EndpointData.Type.OUTPUT -> outputServoModels\n                else -> return\n            }[index]\n            context.fallbackConsumer().accept(model)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashCableModel.kt",
    "content": "package me.steven.indrev.compat.dashloader.models\n\nimport io.activej.serializer.annotations.Deserialize\nimport io.activej.serializer.annotations.Serialize\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blocks.models.pipes.CableModel\nimport net.minecraft.client.render.model.BakedModel\nimport net.oskarstrom.dashloader.DashRegistry\nimport net.oskarstrom.dashloader.api.annotation.DashObject\nimport net.oskarstrom.dashloader.model.DashModel\n\n\n\n@DashObject(CableModel::class) class DashCableModel : DashModel {\n\n    var tier: Int @Serialize(order = 0) get\n    var models: IntArray @Serialize(order = 1) get\n    var sprites: IntArray @Serialize(order = 2) get\n\n    constructor(model: CableModel, registry: DashRegistry) {\n        this.tier = model.tier.ordinal\n        this.models = model.modelArray.map { m -> registry.createModelPointer(m) }.toIntArray()\n        this.sprites = model.spriteArray.map { s -> registry.createSpritePointer(s) }.toIntArray()\n    }\n\n    constructor(\n        @Deserialize(\"tier\") tier: Int,\n        @Deserialize(\"models\") models: IntArray,\n        @Deserialize(\"sprites\") sprites: IntArray\n    ) {\n        this.tier = tier\n        this.models = models\n        this.sprites = sprites\n    }\n\n    override fun toUndash(registry: DashRegistry): BakedModel {\n        val model = CableModel(Tier.ALL_VALUES[tier])\n        this.models.forEachIndexed { index, pointer -> model.modelArray[index] = registry.getModel(pointer) }\n        this.sprites.forEachIndexed { index, pointer -> model.spriteArray[index] = registry.getSprite(pointer) }\n        model.transform = model.modelArray[0]!!.transformation\n        model.buildMeshes()\n        return model\n    }\n\n    override fun getStage(): Int = 3\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashFluidPipeModel.kt",
    "content": "package me.steven.indrev.compat.dashloader.models\n\nimport io.activej.serializer.annotations.Deserialize\nimport io.activej.serializer.annotations.Serialize\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blocks.models.pipes.FluidPipeModel\nimport net.minecraft.client.render.model.BakedModel\nimport net.oskarstrom.dashloader.DashRegistry\nimport net.oskarstrom.dashloader.api.annotation.DashObject\nimport net.oskarstrom.dashloader.model.DashModel\n\n@DashObject(FluidPipeModel::class) class DashFluidPipeModel : DashModel {\n\n    var tier: Int @Serialize(order = 0) get\n    var models: IntArray @Serialize(order = 1) get\n    var sprites: IntArray @Serialize(order = 2) get\n    var retrieverServos: IntArray @Serialize(order = 3) get\n    var outputServos: IntArray @Serialize(order = 4) get\n\n    constructor(model: FluidPipeModel, registry: DashRegistry) {\n        this.tier = model.tier.ordinal\n        this.models = model.modelArray.map { m -> registry.createModelPointer(m) }.toIntArray()\n        this.sprites = model.spriteArray.map { s -> registry.createSpritePointer(s) }.toIntArray()\n        this.retrieverServos = model.retrieverServoModels.map { m -> registry.createModelPointer(m) }.toIntArray()\n        this.outputServos = model.outputServoModels.map { m -> registry.createModelPointer(m) }.toIntArray()\n    }\n\n    constructor(\n        @Deserialize(\"tier\") tier: Int,\n        @Deserialize(\"models\") models: IntArray,\n        @Deserialize(\"sprites\") sprites: IntArray,\n        @Deserialize(\"retrieverServos\") retrieverServos: IntArray,\n        @Deserialize(\"outputServos\") outputServos: IntArray\n    ) {\n        this.tier = tier\n        this.models = models\n        this.sprites = sprites\n        this.retrieverServos = retrieverServos\n        this.outputServos = outputServos\n    }\n\n    override fun toUndash(registry: DashRegistry): BakedModel {\n        val model = FluidPipeModel(Tier.ALL_VALUES[tier])\n        this.models.forEachIndexed { index, pointer -> model.modelArray[index] = registry.getModel(pointer) }\n        this.sprites.forEachIndexed { index, pointer -> model.spriteArray[index] = registry.getSprite(pointer) }\n        this.retrieverServos.forEachIndexed { index, pointer -> model.retrieverServoModels[index] = registry.getModel(pointer) }\n        this.outputServos.forEachIndexed { index, pointer -> model.outputServoModels[index] = registry.getModel(pointer) }\n        model.transform = model.modelArray[0]!!.transformation\n        model.buildMeshes()\n        return model\n    }\n\n    override fun getStage(): Int = 3\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashItemPipeModel.kt",
    "content": "package me.steven.indrev.compat.dashloader.models\n\nimport io.activej.serializer.annotations.Deserialize\nimport io.activej.serializer.annotations.Serialize\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blocks.models.pipes.ItemPipeModel\nimport net.minecraft.client.render.model.BakedModel\nimport net.oskarstrom.dashloader.DashRegistry\nimport net.oskarstrom.dashloader.api.annotation.DashObject\nimport net.oskarstrom.dashloader.model.DashModel\n\n\n@DashObject(ItemPipeModel::class) class DashItemPipeModel: DashModel {\n\n    var tier: Int @Serialize(order = 0) get\n    var models: IntArray @Serialize(order = 1) get\n    var sprites: IntArray @Serialize(order = 2) get\n    var retrieverServos: IntArray @Serialize(order = 3) get\n    var outputServos: IntArray @Serialize(order = 4) get\n\n    constructor(model: ItemPipeModel, registry: DashRegistry) {\n        this.tier = model.tier.ordinal\n        this.models = model.modelArray.map { m -> registry.createModelPointer(m) }.toIntArray()\n        this.sprites = model.spriteArray.map { s -> registry.createSpritePointer(s) }.toIntArray()\n        this.retrieverServos = model.retrieverServoModels.map { m -> registry.createModelPointer(m) }.toIntArray()\n        this.outputServos = model.outputServoModels.map { m -> registry.createModelPointer(m) }.toIntArray()\n    }\n\n    constructor(\n        @Deserialize(\"tier\") tier: Int,\n        @Deserialize(\"models\") models: IntArray,\n        @Deserialize(\"sprites\") sprites: IntArray,\n        @Deserialize(\"retrieverServos\") retrieverServos: IntArray,\n        @Deserialize(\"outputServos\") outputServos: IntArray\n    ) {\n        this.tier = tier\n        this.models = models\n        this.sprites = sprites\n        this.retrieverServos = retrieverServos\n        this.outputServos = outputServos\n    }\n\n    override fun toUndash(registry: DashRegistry): BakedModel {\n        val model = ItemPipeModel(Tier.ALL_VALUES[tier])\n        this.models.forEachIndexed { index, pointer -> model.modelArray[index] = registry.getModel(pointer) }\n        this.sprites.forEachIndexed { index, pointer -> model.spriteArray[index] = registry.getSprite(pointer) }\n        this.retrieverServos.forEachIndexed { index, pointer -> model.retrieverServoModels[index] = registry.getModel(pointer) }\n        this.outputServos.forEachIndexed { index, pointer -> model.outputServoModels[index] = registry.getModel(pointer) }\n        model.transform = model.modelArray[0]!!.transformation\n        model.buildMeshes()\n        return model\n    }\n\n    override fun getStage(): Int = 3\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashLazuliFluxContainerModel.kt",
    "content": "package me.steven.indrev.compat.dashloader.models\n\nimport io.activej.serializer.annotations.Deserialize\nimport io.activej.serializer.annotations.Serialize\nimport me.steven.indrev.blocks.models.LazuliFluxContainerBakedModel\nimport net.minecraft.client.render.model.BakedModel\nimport net.oskarstrom.dashloader.DashRegistry\nimport net.oskarstrom.dashloader.api.annotation.DashObject\nimport net.oskarstrom.dashloader.model.DashModel\n\n\n@DashObject(LazuliFluxContainerBakedModel::class) class DashLazuliFluxContainerModel : DashModel {\n    var id: String @Serialize(order = 0) get\n    var defaultSprite: Int @Serialize(order = 1) get\n    var overlays: IntArray @Serialize(order = 2) get\n\n    constructor(model: LazuliFluxContainerBakedModel, registry: DashRegistry) {\n        this.id = model.id\n        this.defaultSprite = registry.createSpritePointer(model.baseSprite)\n        this.overlays = model.overlays.map { registry.createSpritePointer(it) }.toIntArray()\n    }\n\n    constructor(\n        @Deserialize(\"id\") id: String,\n        @Deserialize(\"defaultSprite\") defaultSprite: Int,\n        @Deserialize(\"overlays\") overlays: IntArray\n    ) {\n        this.id = id\n        this.defaultSprite = defaultSprite\n        this.overlays = overlays\n    }\n\n\n    override fun toUndash(registry: DashRegistry): BakedModel {\n        val model = LazuliFluxContainerBakedModel(id)\n        model.baseSprite = registry.getSprite(defaultSprite)\n        model.overlays.indices.forEach { index ->\n            val sprite = registry.getSprite(overlays[index])\n            model.overlays[index] = sprite\n            if (model.isEmissive(sprite)) model.emissives.add(sprite)\n        }\n\n        model.buildDefaultMesh()\n        return model\n    }\n\n    override fun getStage(): Int = 0\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashMachineModel.kt",
    "content": "package me.steven.indrev.compat.dashloader.models\n\nimport io.activej.serializer.annotations.Deserialize\nimport io.activej.serializer.annotations.Serialize\nimport me.steven.indrev.blocks.models.MachineBakedModel\nimport me.steven.indrev.utils.blockSpriteId\nimport net.minecraft.client.render.model.BakedModel\nimport net.oskarstrom.dashloader.DashRegistry\nimport net.oskarstrom.dashloader.api.annotation.DashObject\nimport net.oskarstrom.dashloader.model.DashModel\n\n@DashObject(MachineBakedModel::class) class DashMachineModel : DashModel {\n\n\n    var id: String @Serialize(order = 0) get\n    var defaultSprite: Int @Serialize(order = 1) get\n    var overlays: IntArray @Serialize(order = 2) get\n    var workingOverlays: IntArray @Serialize(order = 3) get\n\n    constructor(model: MachineBakedModel, registry: DashRegistry) {\n        this.id = model.id\n        this.defaultSprite = registry.createSpritePointer(model.baseSprite)\n        this.overlays = model.overlays.map { registry.createSpritePointer(it) }.toIntArray()\n        this.workingOverlays = model.workingOverlays.map { registry.createSpritePointer(it) }.toIntArray()\n    }\n\n    constructor(\n        @Deserialize(\"id\") id: String,\n        @Deserialize(\"defaultSprite\") defaultSprite: Int,\n        @Deserialize(\"overlays\") overlays: IntArray,\n        @Deserialize(\"workingOverlays\") workingOverlays: IntArray\n    ) {\n        this.id = id\n        this.defaultSprite = defaultSprite\n        this.overlays = overlays\n        this.workingOverlays = workingOverlays\n    }\n\n\n    override fun toUndash(registry: DashRegistry): BakedModel {\n        val model = MachineBakedModel(id)\n        model.baseSprite = registry.getSprite(defaultSprite)\n        overlays.indices.forEach { _ -> model.overlayIds.add(blockSpriteId(\"\")) }\n        workingOverlays.indices.forEach { _ -> model.workingOverlayIds.add(blockSpriteId(\"\")) }\n        model.overlays.indices.forEach { index ->\n            val sprite = registry.getSprite(overlays[index])\n            model.overlays[index] = sprite\n            if (model.isEmissive(sprite)) model.emissives.add(sprite)\n        }\n        model.workingOverlays.indices.forEach { index ->\n            val sprite = registry.getSprite(workingOverlays[index])\n            model.workingOverlays[index] = sprite\n            if (model.isEmissive(sprite)) model.emissives.add(sprite)\n        }\n\n\n        model.buildDefaultMesh()\n        model.buildWorkingStateMesh()\n        return model\n    }\n\n    override fun getStage(): Int = 0\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashMinerModel.kt",
    "content": "package me.steven.indrev.compat.dashloader.models\n\nimport io.activej.serializer.annotations.Deserialize\nimport io.activej.serializer.annotations.Serialize\nimport me.steven.indrev.blocks.models.MinerBakedModel\nimport me.steven.indrev.utils.blockSpriteId\nimport net.minecraft.client.render.model.BakedModel\nimport net.oskarstrom.dashloader.DashRegistry\nimport net.oskarstrom.dashloader.api.annotation.DashObject\nimport net.oskarstrom.dashloader.model.DashModel\n\n\n@DashObject(MinerBakedModel::class) class DashMinerModel : DashModel {\n\n    var id: String @Serialize(order = 0) get\n    var defaultSprite: Int @Serialize(order = 1) get\n    var overlays: IntArray @Serialize(order = 2) get\n    var workingOverlays: IntArray @Serialize(order = 3) get\n    var screenSprite: Int @Serialize(order = 4) get\n\n    constructor(model: MinerBakedModel, registry: DashRegistry) {\n        this.id = model.id\n        this.defaultSprite = registry.createSpritePointer(model.baseSprite)\n        this.overlays = model.overlays.map { registry.createSpritePointer(it) }.toIntArray()\n        this.workingOverlays = model.workingOverlays.map { registry.createSpritePointer(it) }.toIntArray()\n        this.screenSprite = registry.createSpritePointer(model.screenSprite)\n    }\n\n    constructor(\n        @Deserialize(\"id\") id: String,\n        @Deserialize(\"defaultSprite\") defaultSprite: Int,\n        @Deserialize(\"overlays\") overlays: IntArray,\n        @Deserialize(\"workingOverlays\") workingOverlays: IntArray,\n        @Deserialize(\"screenSprite\") screenSprite: Int\n    ) {\n        this.id = id\n        this.defaultSprite = defaultSprite\n        this.overlays = overlays\n        this.workingOverlays = workingOverlays\n        this.screenSprite = screenSprite\n    }\n\n\n    override fun toUndash(registry: DashRegistry): BakedModel {\n        val model = MinerBakedModel(id)\n        model.baseSprite = registry.getSprite(defaultSprite)\n        model.screenSprite = registry.getSprite(screenSprite)\n        overlays.indices.forEach { _ -> model.overlayIds.add(blockSpriteId(\"\")) }\n        workingOverlays.indices.forEach { _ -> model.workingOverlayIds.add(blockSpriteId(\"\")) }\n        model.overlays.indices.forEach { index ->\n            val sprite = registry.getSprite(overlays[index])\n            model.overlays[index] = sprite\n            if (model.isEmissive(sprite)) model.emissives.add(sprite)\n        }\n        model.workingOverlays.indices.forEach { index ->\n            val sprite = registry.getSprite(workingOverlays[index])\n            model.workingOverlays[index] = sprite\n            if (model.isEmissive(sprite)) model.emissives.add(sprite)\n        }\n\n\n        model.buildDefaultMesh()\n        model.buildWorkingStateMesh()\n        return model\n    }\n\n    override fun getStage(): Int = 0\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/dashloader/models/DashTankModel.kt",
    "content": "package me.steven.indrev.compat.dashloader.models\n\nimport me.steven.indrev.items.models.TankItemBakedModel\nimport net.minecraft.client.render.model.BakedModel\nimport net.oskarstrom.dashloader.DashRegistry\nimport net.oskarstrom.dashloader.api.annotation.DashConstructor\nimport net.oskarstrom.dashloader.api.annotation.DashObject\nimport net.oskarstrom.dashloader.api.enums.ConstructorMode\nimport net.oskarstrom.dashloader.model.DashModel\n\n\n@DashObject(TankItemBakedModel::class) class DashTankModel @DashConstructor(ConstructorMode.EMPTY) constructor() : DashModel {\n\n\n    override fun toUndash(registry: DashRegistry): BakedModel {\n        return TankItemBakedModel()\n    }\n\n    override fun getStage(): Int = 3\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/rei/REIPlugin.kt",
    "content": "package me.steven.indrev.compat.rei\n\nimport me.shedaniel.rei.api.client.plugins.REIClientPlugin\nimport me.shedaniel.rei.api.client.registry.category.CategoryRegistry\nimport me.shedaniel.rei.api.client.registry.display.DisplayRegistry\nimport me.shedaniel.rei.api.client.registry.entry.EntryRegistry\nimport me.shedaniel.rei.api.common.util.EntryStacks\nimport me.shedaniel.rei.plugin.common.displays.DefaultInformationDisplay\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.compat.rei.categories.IRMachineRecipeCategory\nimport me.steven.indrev.compat.rei.categories.IRModuleCraftingRecipeCategory\nimport me.steven.indrev.compat.rei.categories.IRSawmillRecipeCategory\nimport me.steven.indrev.compat.rei.plugins.IRMachinePlugin\nimport me.steven.indrev.recipes.machines.*\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.energyOf\nimport me.steven.indrev.utils.hide\nimport net.minecraft.block.Block\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.recipe.AbstractCookingRecipe\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport java.util.*\nimport kotlin.collections.component1\nimport kotlin.collections.component2\nimport kotlin.collections.distinctBy\nimport kotlin.collections.forEach\nimport kotlin.collections.indexOf\nimport kotlin.collections.mutableMapOf\nimport kotlin.collections.sumOf\nimport kotlin.math.roundToInt\n\nobject REIPlugin : REIClientPlugin {\n\n    override fun registerEntries(entryRegistry: EntryRegistry?) {\n        fun registerCharged(vararg items: Item) {\n            items.forEach { item ->\n                entryRegistry?.addEntriesAfter(EntryStacks.of(item),\n                    EntryStacks.of(ItemStack(item).also { it.orCreateNbt.putLong(\"energy\", energyOf(it)!!.capacity) }))\n            }\n        }\n\n        registerCharged(\n            IRItemRegistry.MINING_DRILL_MK1,\n            IRItemRegistry.MINING_DRILL_MK2,\n            IRItemRegistry.MINING_DRILL_MK3,\n            IRItemRegistry.MINING_DRILL_MK4,\n            IRItemRegistry.MODULAR_ARMOR_HELMET,\n            IRItemRegistry.MODULAR_ARMOR_CHEST,\n            IRItemRegistry.MODULAR_ARMOR_LEGGINGS,\n            IRItemRegistry.MODULAR_ARMOR_BOOTS,\n            IRItemRegistry.PORTABLE_CHARGER_ITEM,\n            IRItemRegistry.BATTERY\n        )\n\n        entryRegistry?.addEntriesAfter(EntryStacks.of(IRItemRegistry.GAMER_AXE_ITEM),\n            EntryStacks.of(ItemStack(IRItemRegistry.GAMER_AXE_ITEM).also {\n                val tag = it.orCreateNbt\n                tag.putLong(\"energy\", energyOf(it)!!.capacity)\n                tag.putBoolean(\"Active\", true)\n                tag.putFloat(\"Progress\", 1f)\n            }))\n\n        entryRegistry?.removeEntryIf { e -> hide(e.identifier ?: return@removeEntryIf false) }\n    }\n\n    override fun registerCategories(registry: CategoryRegistry) {\n        registry.add(\n            IRMachineRecipeCategory(\n                PulverizerRecipe.IDENTIFIER,\n                EntryStacks.of(MachineRegistry.PULVERIZER_REGISTRY.block(Tier.MK1)),\n                \"indrev.category.rei.pulverizing\"\n            )\n        )\n\n        registry.add(\n            IRMachineRecipeCategory(\n                InfuserRecipe.IDENTIFIER,\n                EntryStacks.of(MachineRegistry.SOLID_INFUSER_REGISTRY.block(Tier.MK1)),\n                \"indrev.category.rei.infusing\"\n            )\n        )\n\n        registry.add(\n            IRMachineRecipeCategory(\n                CompressorRecipe.IDENTIFIER,\n                EntryStacks.of(MachineRegistry.COMPRESSOR_REGISTRY.block(Tier.MK1)),\n                \"indrev.category.rei.compressing\"\n            )\n        )\n\n        registry.add(\n            IRMachineRecipeCategory(\n                RecyclerRecipe.IDENTIFIER,\n                EntryStacks.of(MachineRegistry.RECYCLER_REGISTRY.block(Tier.MK2)),\n                \"indrev.category.rei.recycling\"\n            )\n        )\n\n        registry.add(\n            IRMachineRecipeCategory(\n                FluidInfuserRecipe.IDENTIFIER,\n                EntryStacks.of(MachineRegistry.FLUID_INFUSER_REGISTRY.block(Tier.MK1)),\n                \"indrev.category.rei.fluid_infusing\"\n            )\n        )\n\n        registry.add(\n            IRMachineRecipeCategory(\n                CondenserRecipe.IDENTIFIER,\n                EntryStacks.of(MachineRegistry.CONDENSER_REGISTRY.block(Tier.MK4)),\n                \"indrev.category.rei.condensing\"\n            )\n        )\n\n        registry.add(\n            IRMachineRecipeCategory(\n                SmelterRecipe.IDENTIFIER,\n                EntryStacks.of(MachineRegistry.SMELTER_REGISTRY.block(Tier.MK4)),\n                \"indrev.category.rei.smelting\"\n            )\n        )\n\n        registry.add(\n            IRSawmillRecipeCategory(\n                SawmillRecipe.IDENTIFIER,\n                EntryStacks.of(MachineRegistry.SAWMILL_REGISTRY.block(Tier.MK4)),\n                \"indrev.category.rei.sawmill\"\n            )\n        )\n\n        registry.add(\n            IRModuleCraftingRecipeCategory(\n                ModuleRecipe.IDENTIFIER,\n                EntryStacks.of(MachineRegistry.MODULAR_WORKBENCH_REGISTRY.block(Tier.MK4)),\n                \"indrev.category.rei.module\"\n            )\n        )\n    }\n\n    override fun registerDisplays(registry: DisplayRegistry) {\n        registry.recipeManager.recipes.keys.forEach { type ->\n            if (type is IRRecipeType<*> && type.id.namespace == IndustrialRevolution.MOD_ID)\n                registry.registerFiller(IRRecipe::class.java, { r -> r is IRRecipe && r !is AbstractCookingRecipe && r.type == type }) { recipe -> IRMachinePlugin(recipe) }\n        }\n\n        MachineRegistry.MAP.entries.distinctBy { (_, v) -> v }.forEach { (_, machineRegistry) ->\n            if (machineRegistry.upgradeable && machineRegistry.tiers.size > 1) {\n                machineRegistry.forEachBlock { tier, block ->\n                    val entryStack = EntryStacks.of(block)\n                    if (tier != Tier.CREATIVE && tier != Tier.MK1) {\n                        val info = DefaultInformationDisplay.createFromEntry(entryStack, TranslatableText(block.translationKey))\n                        info.lines(TranslatableText(\"indrev.category.rei.upgrading\",\n                            TranslatableText(\"item.indrev.tier_upgrade_\" + tier.toString()\n                                .lowercase(Locale.getDefault())).formatted(Formatting.DARK_GRAY),\n                            TranslatableText(machineRegistry.block(machineRegistry.tiers[machineRegistry.tiers.indexOf(tier) - 1]).translationKey).formatted(Formatting.DARK_GRAY),\n                            tier.toString()))\n                        registry.add(info)\n                    }\n                }\n            }\n        }\n\n    }\n\n    /*override fun registerOthers(recipeHelper: RecipeHelper?) {\n        MachineRegistry.PULVERIZER_REGISTRY.forEachBlock { _, block ->\n            recipeHelper?.registerWorkingStations(\n                PULVERIZING,\n                EntryStacks.of(block)\n            )\n        }\n        MachineRegistry.SOLID_INFUSER_REGISTRY.forEachBlock { _, block ->\n            recipeHelper?.registerWorkingStations(\n                INFUSING,\n                EntryStacks.of(block)\n            )\n        }\n        MachineRegistry.COMPRESSOR_REGISTRY.forEachBlock { _, block ->\n            recipeHelper?.registerWorkingStations(\n                COMPRESSING,\n                EntryStacks.of(block)\n            )\n        }\n        MachineRegistry.RECYCLER_REGISTRY.forEachBlock { _, block ->\n            recipeHelper?.registerWorkingStations(\n                RECYCLING,\n                EntryStacks.of(block)\n            )\n        }\n        MachineRegistry.FLUID_INFUSER_REGISTRY.forEachBlock { _, block ->\n            recipeHelper?.registerWorkingStations(\n                FLUID_INFUSER,\n                EntryStacks.of(block)\n            )\n        }\n        MachineRegistry.CONDENSER_REGISTRY.forEachBlock { _, block ->\n            recipeHelper?.registerWorkingStations(\n                CONDENSER,\n                EntryStacks.of(block)\n            )\n        }\n        MachineRegistry.SMELTER_REGISTRY.forEachBlock { _, block ->\n            recipeHelper?.registerWorkingStations(\n                SMELTER,\n                EntryStacks.of(block)\n            )\n        }\n        MachineRegistry.SAWMILL_REGISTRY.forEachBlock { _, block ->\n            recipeHelper?.registerWorkingStations(\n                SAWMILL,\n                EntryStacks.of(block)\n            )\n        }\n        MachineRegistry.MODULAR_WORKBENCH_REGISTRY.forEachBlock { _, block ->\n            recipeHelper?.registerWorkingStations(\n                MODULE,\n                EntryStacks.of(block)\n            )\n        }\n    }*/\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/rei/categories/IRMachineRecipeCategory.kt",
    "content": "package me.steven.indrev.compat.rei.categories\n\nimport me.shedaniel.math.Point\nimport me.shedaniel.math.Rectangle\nimport me.shedaniel.rei.api.client.gui.DisplayRenderer\nimport me.shedaniel.rei.api.client.gui.Renderer\nimport me.shedaniel.rei.api.client.gui.SimpleDisplayRenderer\nimport me.shedaniel.rei.api.client.gui.widgets.Widget\nimport me.shedaniel.rei.api.client.gui.widgets.Widgets\nimport me.shedaniel.rei.api.client.registry.display.DisplayCategory\nimport me.shedaniel.rei.api.common.category.CategoryIdentifier\nimport me.shedaniel.rei.api.common.entry.EntryStack\nimport me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes\nimport me.steven.indrev.compat.rei.plugins.IRMachinePlugin\nimport me.steven.indrev.recipes.machines.IRFluidRecipe\nimport me.steven.indrev.utils.createREIFluidWidget\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Identifier\n\nopen class IRMachineRecipeCategory(\n    private val identifier: Identifier,\n    private val logo: EntryStack<*>,\n    private val categoryName: String\n) : DisplayCategory<IRMachinePlugin> {\n\n    override fun setupDisplay(recipeDisplay: IRMachinePlugin, bounds: Rectangle): MutableList<Widget> {\n        val recipe = recipeDisplay.recipe\n        val startPoint = Point(bounds.centerX - 41, bounds.centerY - 27)\n        val widgets = super.setupDisplay(recipeDisplay, bounds).toMutableList()\n        widgets.add(Widgets.createArrow(Point(startPoint.x + 24, startPoint.y + 18)))\n        if (recipe.input.isNotEmpty()) {\n            val input = recipeDisplay.inputEntries.filter { it.all { it.type == VanillaEntryTypes.ITEM } }\n            widgets.add(Widgets.createSlot(Point(startPoint.x + 1, startPoint.y + 19)).entries(input[0]))\n            if (recipe.input.size > 1)\n                widgets.add(\n                    Widgets.createSlot(Point(startPoint.x - 17, startPoint.y + 19)).entries(input[1])\n                )\n        }\n        if (recipe.outputs.isNotEmpty()) {\n            widgets.add(Widgets.createResultSlotBackground(Point(startPoint.x + 61, startPoint.y + 19)))\n            widgets.add(\n                Widgets.createSlot(Point(startPoint.x + 61, startPoint.y + 19)).entries(recipeDisplay.outputEntries.filter { it.all { it.type == VanillaEntryTypes.ITEM } }[0]).disableBackground().markOutput()\n            )\n        }\n\n        if (recipe is IRFluidRecipe) {\n            if (recipe.fluidOutput.isNotEmpty() && recipe.fluidOutput[0].amount > 0) {\n                val outputFluidPoint = Point(startPoint.x + 83, startPoint.y)\n                createREIFluidWidget(widgets, outputFluidPoint, recipe.fluidOutput[0])\n            }\n            if (recipe.fluidInput.isNotEmpty() && recipe.fluidInput[0].amount > 0) {\n                val inputFluidPoint = Point(startPoint.x - 20, startPoint.y)\n                createREIFluidWidget(widgets, inputFluidPoint, recipe.fluidInput[0])\n            }\n        }\n        return widgets\n    }\n\n\n    override fun getDisplayRenderer(display: IRMachinePlugin): DisplayRenderer {\n        return SimpleDisplayRenderer.from(listOf(display.inputEntries[0]), display.outputEntries)\n    }\n\n    override fun getDisplayHeight(): Int = 66\n\n    override fun getIdentifier(): Identifier = identifier\n\n    override fun getIcon(): Renderer {\n        return logo\n    }\n\n    override fun getTitle(): Text = TranslatableText(categoryName)\n\n    override fun getCategoryIdentifier(): CategoryIdentifier<IRMachinePlugin> {\n        return CategoryIdentifier.of(identifier)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/rei/categories/IRModuleCraftingRecipeCategory.kt",
    "content": "package me.steven.indrev.compat.rei.categories\n\nimport me.shedaniel.math.Point\nimport me.shedaniel.math.Rectangle\nimport me.shedaniel.rei.api.client.gui.widgets.Widget\nimport me.shedaniel.rei.api.client.gui.widgets.Widgets\nimport me.shedaniel.rei.api.common.entry.EntryStack\nimport me.shedaniel.rei.api.common.util.EntryStacks\nimport me.steven.indrev.compat.rei.plugins.IRMachinePlugin\nimport net.minecraft.util.Identifier\n\nclass IRModuleCraftingRecipeCategory(\n    identifier: Identifier,\n    logo: EntryStack<*>,\n    categoryName: String\n) : IRMachineRecipeCategory(identifier, logo, categoryName) {\n\n    private val slotLayout = hashMapOf<Int, Array<Point>>()\n\n    init {\n\n        slotLayout[1] = arrayOf(Point(2 * 18, 0))\n        slotLayout[2] = arrayOf(\n            Point(0, 2 * 18),\n            Point(4 * 18, 2 * 18)\n        )\n        slotLayout[3] = arrayOf(\n            Point(2 * 18, 0),\n            Point(0, 4 * 17),\n            Point(4 * 18, 4 * 17)\n        )\n        slotLayout[4] = arrayOf(\n            Point(2 * 18, 0),\n            Point(0, 2 * 18),\n            Point(4 * 18, 2 * 18),\n            Point(2 * 18, 4 * 18)\n        )\n        slotLayout[5] = arrayOf(\n            Point(2 * 18, 0),\n            Point(0, 2 * 18),\n            Point(4 * 18, 2 * 18),\n            Point(1 * 14, 4 * 18),\n            Point(3 * 20, 4 * 18)\n        )\n        slotLayout[6] = arrayOf(\n            Point(2 * 18, 0),\n            Point(0 * 18, 1 * 18),\n            Point(4 * 18, 1 * 18),\n            Point(0 * 14, 3 * 18),\n            Point(4 * 18, 3 * 18),\n            Point(2 * 18, 4 * 18)\n        )\n    }\n\n    override fun setupDisplay(recipeDisplay: IRMachinePlugin, bounds: Rectangle): MutableList<Widget> {\n        val recipe = recipeDisplay.recipe\n        val startPoint = Point(bounds.centerX - 43, bounds.centerY - 45)\n        val widgets: MutableList<Widget> = listOf(Widgets.createCategoryBase(bounds)).toMutableList()\n        val input = recipeDisplay.inputEntries\n        val outputs = recipe.outputs.map { EntryStacks.of(it.stack) }\n        slotLayout[input.size]?.forEachIndexed { index, point ->\n            widgets.add(Widgets.createSlot(startPoint + point).entries(input[index]))\n        }\n        widgets.add(Widgets.createResultSlotBackground(startPoint + Point(2 * 18, 2 * 18)))\n        widgets.add(Widgets.createSlot(startPoint + Point(2 * 18, 2 * 18)).entry(outputs[0]).disableBackground().markOutput())\n        return widgets\n    }\n\n    private operator fun Point.plus(other: Point) = Point(x + other.x, y + other.y)\n\n    override fun getDisplayHeight(): Int = 120\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/rei/categories/IRSawmillRecipeCategory.kt",
    "content": "package me.steven.indrev.compat.rei.categories\n\nimport me.shedaniel.math.Point\nimport me.shedaniel.math.Rectangle\nimport me.shedaniel.rei.api.client.gui.widgets.Widget\nimport me.shedaniel.rei.api.client.gui.widgets.Widgets\nimport me.shedaniel.rei.api.common.entry.EntryStack\nimport me.shedaniel.rei.api.common.util.EntryStacks\nimport me.steven.indrev.compat.rei.plugins.IRMachinePlugin\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.Identifier\n\nclass IRSawmillRecipeCategory(\n    identifier: Identifier,\n    logo: EntryStack<*>,\n    categoryName: String\n) : IRMachineRecipeCategory(identifier, logo, categoryName) {\n\n    override fun setupDisplay(recipeDisplay: IRMachinePlugin, bounds: Rectangle): MutableList<Widget> {\n        val recipe = recipeDisplay.recipe\n        val startPoint = Point(bounds.centerX - 41, bounds.centerY - 27)\n        val widgets: MutableList<Widget> = listOf(Widgets.createCategoryBase(bounds)).toMutableList()\n        widgets.add(Widgets.createArrow(Point(startPoint.x + 24, startPoint.y + 18)))\n        val input = recipeDisplay.inputEntries\n        widgets.add(Widgets.createSlot(Point(startPoint.x + 1, startPoint.y + 19)).entries(input[0]))\n        val outputs = recipe.outputs.map { EntryStacks.of(it.stack) }\n        widgets.add(\n            Widgets.createSlot(Point(startPoint.x + 61, startPoint.y + 6))\n                .entry(outputs.getOrElse(0) { EntryStacks.of(ItemStack.EMPTY) })\n        )\n        widgets.add(\n            Widgets.createSlot(Point(startPoint.x + 61, startPoint.y + 24))\n                .entry(outputs.getOrElse(1) { EntryStacks.of(ItemStack.EMPTY) })\n        )\n        widgets.add(\n            Widgets.createSlot(Point(startPoint.x + 79, startPoint.y + 6))\n                .entry(outputs.getOrElse(2) { EntryStacks.of(ItemStack.EMPTY) })\n        )\n        widgets.add(\n            Widgets.createSlot(Point(startPoint.x + 79, startPoint.y + 24))\n                .entry(outputs.getOrElse(3) { EntryStacks.of(ItemStack.EMPTY) })\n        )\n        return widgets\n    }\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/compat/rei/plugins/IRMachinePlugin.kt",
    "content": "package me.steven.indrev.compat.rei.plugins\n\nimport me.shedaniel.rei.api.common.category.CategoryIdentifier\nimport me.shedaniel.rei.api.common.display.Display\nimport me.shedaniel.rei.api.common.entry.EntryIngredient\nimport me.shedaniel.rei.api.common.util.EntryIngredients\nimport me.shedaniel.rei.api.common.util.EntryStacks\nimport me.steven.indrev.recipes.machines.IRFluidRecipe\nimport me.steven.indrev.recipes.machines.IRRecipe\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.Identifier\nimport java.util.*\n\nopen class IRMachinePlugin(val recipe: IRRecipe) : Display {\n\n    override fun getInputEntries(): List<EntryIngredient> = getInputs(recipe)\n\n    override fun getOutputEntries(): List<EntryIngredient> = getOutputs(recipe)\n\n    override fun getCategoryIdentifier(): CategoryIdentifier<*> {\n        return CategoryIdentifier.of<IRMachinePlugin>(recipe.type.id)\n    }\n\n    override fun getDisplayLocation(): Optional<Identifier> {\n        return Optional.of(recipe.id)\n    }\n\n    companion object {\n        fun getInputs(recipe: IRRecipe): List<EntryIngredient> {\n            val list = mutableListOf<EntryIngredient>()\n            if (recipe is IRFluidRecipe) {\n                val fluidInput = recipe.fluidInput\n                //TODO properly support multiple inputs\n                if (fluidInput.isNotEmpty())\n                    list.addAll(mutableListOf(EntryIngredients.of(fluidInput[0].resource.fluid, 81000)))\n            }\n            list.addAll(recipe.input.map { (ingredient, count) ->\n                val builder = EntryIngredient.builder()\n                ingredient.matchingStacks.map { stack -> builder.add(EntryStacks.of(ItemStack(stack.item, count))) }\n                builder.build()\n            })\n            return list\n        }\n\n        fun getOutputs(recipe: IRRecipe): List<EntryIngredient> {\n            val list = mutableListOf<EntryIngredient>()\n            list.addAll(recipe.outputs.map { (stack, _) -> EntryIngredients.of(stack) }.toMutableList())\n            if (recipe is IRFluidRecipe) {\n                val fluidOutput = recipe.fluidOutput\n\n                //TODO properly support multiple outputs\n                if (fluidOutput.isNotEmpty())\n                    list.add(EntryIngredients.of(fluidOutput[0].resource.fluid, 81000))\n            }\n\n            return list\n        }\n    }\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/CraftingComponent.kt",
    "content": "package me.steven.indrev.components\n\nimport me.steven.indrev.blockentities.crafters.CraftingMachineBlockEntity\nimport me.steven.indrev.inventories.IRInventory\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.recipes.IRecipeGetter\nimport me.steven.indrev.recipes.machines.IRFluidRecipe\nimport me.steven.indrev.recipes.machines.IRRecipe\nimport me.steven.indrev.utils.IRFluidAmount\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.world.World\nimport kotlin.math.ceil\n\nopen class CraftingComponent<T : IRRecipe>(private val index: Int, val machine: CraftingMachineBlockEntity<T>) : DefaultSyncableObject() {\n    var processTime: Int = 0\n    var totalProcessTime: Int = 0\n    val fluidComponent: FluidComponent? get() = machine.fluidComponent\n    val inventoryComponent: InventoryComponent get() = machine.inventoryComponent!!\n    val temperatureComponent: TemperatureComponent? get() = machine.temperatureComponent\n    val type: IRecipeGetter<T> get() = machine.type\n    val world: World? get() = machine.world\n    var isCrafting: Boolean = false\n\n    var inputSlots: IntArray? = null\n        get() = if (field == null) inventoryComponent.inventory.inputSlots else field\n    var outputSlots: IntArray? = null\n        get() = if (field == null) inventoryComponent.inventory.outputSlots else field\n\n    private var currentRecipe: T? = null\n\n    fun tick() {\n        val inventory = inventoryComponent.inventory\n        val inputInventory = inputSlots!!.map { inventory.getStack(it) }\n        val inputTanks = fluidComponent?.inputTanks?.map { fluidComponent!![it] } ?: emptyList()\n        when {\n            isProcessing() -> {\n                val recipe = currentRecipe\n                val upgrades = machine.enhancerComponent!!.enhancers\n                if (recipe?.matches(inputInventory, inputTanks) != true) {\n                    tryStartRecipe(inventory) ?: reset().also { markDirty() }\n                }\n                else if (machine.use(machine.getEnergyCost())) {\n                    isCrafting = true\n                    processTime = (processTime + ceil(machine.getProcessingSpeed())).coerceAtLeast(0.0).toInt()\n                    markDirty()\n                    if (processTime >= totalProcessTime) {\n                        handleInventories(inventory, inputInventory, recipe)\n                        machine.usedRecipes.addTo(recipe.id, 1)\n                        reset()\n                    }\n                } else isCrafting = false\n            }\n            machine.energy > 0 -> {\n                reset()\n                if (tryStartRecipe(inventory) == null) isCrafting = false\n            }\n            else -> {\n                reset()\n                isCrafting = false\n            }\n        }\n        temperatureComponent?.tick(isProcessing() && isCrafting)\n    }\n\n    protected open fun handleInventories(inventory: IRInventory, inputInventory: List<ItemStack>, recipe: IRRecipe) {\n        val output = recipe.craft(machine.world!!.random)\n        inputSlots!!.forEachIndexed { index, _ ->\n            recipe.input.forEach { (ingredient, count) ->\n                val stack = inputInventory[index]\n                if (!ingredient.test(stack)) return@forEach\n                stack.decrement(count)\n                return@forEachIndexed\n            }\n        }\n\n        output.forEach { stack -> craft(stack) }\n\n        if (recipe is IRFluidRecipe) {\n            fluidComponent!!.inputTanks.forEach outer@{ slot ->\n                recipe.fluidInput.forEach { volume ->\n                    val tank = fluidComponent!![slot]\n                    if (tank.resource != volume.resource) return@forEach\n                    val amount = tank.amount - volume.amount\n                    if (amount <= 0)\n                        tank.variant = FluidVariant.blank()\n                    tank.amount = amount\n                    tank.markDirty()\n                    return@forEach\n                }\n            }\n            recipe.fluidOutput.forEach { craft(it) }\n\n        }\n    }\n\n    fun craft(fluid: IRFluidAmount) {\n        val fluidComponent = fluidComponent!!\n        for (outputSlot in fluidComponent.outputTanks) {\n            val tank = fluidComponent[outputSlot]\n            if (tank.isEmpty) {\n                tank.variant = fluid.resource\n                tank.amount = tank.amount + fluid.amount\n                tank.markDirty()\n            } else if (fluid.resource == tank.resource && fluid.amount() + tank.amount <= tank.capacity) {\n                tank.amount = tank.amount + fluid.amount\n                tank.markDirty()\n            }\n            else continue\n            break\n        }\n    }\n\n    fun craft(stack: ItemStack) {\n        val inventory = inventoryComponent.inventory\n        for (outputSlot in outputSlots!!) {\n            val outStack = inventory.getStack(outputSlot)\n            if (stack.item == outStack.item && stack.nbt == outStack.nbt && stack.count + outStack.count <= stack.maxCount)\n                outStack.increment(stack.count)\n            else if (outStack.isEmpty)\n                inventory.setStack(outputSlot, stack)\n            else continue\n            break\n        }\n    }\n\n    fun fits(stack: ItemStack): Boolean {\n        for (outputSlot in outputSlots!!) {\n            val outStack = inventoryComponent.inventory.getStack(outputSlot)\n            if (outStack.isEmpty || (stack.item == outStack.item && stack.nbt == outStack.nbt && stack.count + outStack.count <= stack.maxCount))\n                return true\n        }\n        return false\n    }\n\n    private fun tryStartRecipe(inventory: IRInventory): T? {\n        val inputStacks = inputSlots!!.map { inventory.getStack(it) }.filter { !it.isEmpty }\n        val inputFluids = fluidComponent?.inputTanks?.map { fluidComponent!![it] }?.filter { !it.isEmpty } ?: emptyList()\n        val recipe =\n            type.getMatchingRecipe(world as ServerWorld, inputStacks, inputFluids)\n                .firstOrNull { it.matches(inputStacks, inputFluids) } ?: return null\n        if (!recipe.canStart(this)) return null\n        processTime = 0\n        totalProcessTime = recipe.ticks\n        currentRecipe = recipe\n        return recipe\n    }\n\n    private fun reset() {\n        processTime = 0\n        totalProcessTime = 0\n    }\n\n    private fun isProcessing() = totalProcessTime > 0 && processTime < totalProcessTime\n\n    fun readNbt(tag: NbtCompound?) {\n        processTime = tag?.getInt(\"ProcessTime\") ?: 0\n        totalProcessTime = tag?.getInt(\"MaxProcessTime\") ?: 0\n    }\n\n    fun writeNbt(tag: NbtCompound): NbtCompound {\n        tag.putInt(\"ProcessTime\", processTime)\n        tag.putInt(\"MaxProcessTime\", totalProcessTime)\n        return tag\n    }\n\n    override fun toPacket(buf: PacketByteBuf) {\n        buf.writeInt(processTime)\n        buf.writeInt(totalProcessTime)\n    }\n\n    @Environment(EnvType.CLIENT)\n    override fun fromPacket(buf: PacketByteBuf) {\n        this.processTime = buf.readInt()\n        this.totalProcessTime = buf.readInt()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/EnhancerComponent.kt",
    "content": "package me.steven.indrev.components\n\nimport it.unimi.dsi.fastutil.ints.IntBinaryOperator\nimport it.unimi.dsi.fastutil.objects.Object2IntMap\nimport it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.items.upgrade.IREnhancerItem\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport net.minecraft.inventory.Inventory\n\nopen class EnhancerComponent(\n    val slots: IntArray,\n    val compatible: Array<Enhancer>,\n    val maxSlotCount: (Enhancer) -> Int\n) {\n\n    val enhancers: Object2IntMap<Enhancer> = Object2IntOpenHashMap()\n\n    fun updateEnhancers(inventory: Inventory) {\n        enhancers.clear()\n        slots\n            .forEach { slot ->\n                val (stack, item) = inventory.getStack(slot)\n                if (item is IREnhancerItem && compatible.contains(item.enhancer))\n                    enhancers.mergeInt(item.enhancer, stack.count, IntBinaryOperator { i, j -> i + j })\n            }\n    }\n\n    fun getCount(enhancer: Enhancer) = enhancers.getInt(enhancer)\n\n    open fun isLocked(slot: Int, tier: Tier) = slots.indexOf(slot) > tier.ordinal\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/FluidComponent.kt",
    "content": "package me.steven.indrev.components\n\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.api.sideconfigs.SideConfiguration\nimport me.steven.indrev.blockentities.Syncable\nimport me.steven.indrev.utils.IRFluidTank\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.StorageView\nimport net.fabricmc.fabric.api.transfer.v1.storage.base.CombinedStorage\nimport net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.Direction\nimport java.util.*\n\nopen class FluidComponent(val syncable: () -> Syncable, val limit: Long, val tankCount: Int = 1) :\n    CombinedStorage<FluidVariant, IRFluidTank>(mutableListOf()) {\n\n    init {\n        parts.addAll((0 until tankCount).map { IRFluidTank(it) { this } })\n    }\n\n    var inputTanks = intArrayOf()\n    var outputTanks = intArrayOf()\n\n    var unsided = false\n    val transferConfig: SideConfiguration = SideConfiguration(ConfigurationType.FLUID)\n\n    private val exposedSides = EnumMap<Direction, ExposedFluidComponent>(Direction::class.java)\n\n\n    fun getCachedSide(dir: Direction): ExposedFluidComponent {\n        val exposed = exposedSides[dir]\n        if (exposed == null || (!unsided && exposed.mode != transferConfig[dir])) {\n            exposedSides[dir] = ExposedFluidComponent(dir, transferConfig[dir]!!)\n        }\n        return exposedSides[dir]!!\n    }\n\n    open fun getValidTanks(dir: Direction): IntArray =\n        if (unsided) IntArray(tankCount) { it }\n        else if (transferConfig[dir]!!.input) inputTanks\n        else if (transferConfig[dir]!!.output) outputTanks else IntArray(0)\n\n    open fun getTankCapacity(index: Int): Long = limit\n\n    open fun isFluidValidForTank(index: Int, variant: FluidVariant): Boolean = true\n\n    operator fun set(tank: Int, volume: Long) {\n        this.parts[tank].amount = volume\n    }\n\n    operator fun get(tank: Int): IRFluidTank = this.parts[tank]\n\n    fun toTag(tag: NbtCompound): NbtCompound {\n        val tanksTag = NbtCompound()\n        parts.forEachIndexed { index, tank ->\n            val tankTag = NbtCompound()\n            tankTag.put(\"fluids\", tank.toTag())\n            tanksTag.put(index.toString(), tankTag)\n        }\n        tag.put(\"tanks\", tanksTag)\n        transferConfig.writeNbt(tag)\n        return tag\n    }\n\n    fun fromTag(tag: NbtCompound?) {\n        val tanksTag = tag?.getCompound(\"tanks\")\n\n        tanksTag?.keys?.forEach { key ->\n            val index = key.toInt()\n            val tankTag = tanksTag.getCompound(key)\n            parts[index].fromTag(tankTag.getCompound(\"fluids\"))\n        }\n\n        transferConfig.readNbt(tag)\n    }\n\n    inner class ExposedFluidComponent(val dir: Direction, val mode: TransferMode) : CombinedStorage<FluidVariant, IRFluidTank.ExposedIRFluidTank>(mutableListOf()) {\n\n        init {\n            parts.addAll(getValidTanks(dir).map { this@FluidComponent.parts[it].exposed })\n        }\n\n        override fun insert(resource: FluidVariant?, maxAmount: Long, transaction: TransactionContext?): Long {\n            return if (unsided || transferConfig[dir]!!.input) {\n                super.insert(resource, maxAmount, transaction)\n            }\n            else 0\n        }\n\n        override fun extract(resource: FluidVariant?, maxAmount: Long, transaction: TransactionContext?): Long {\n            return if (unsided || transferConfig[dir]!!.output)\n                super.extract(resource, maxAmount, transaction)\n            else 0\n        }\n\n        override fun supportsExtraction(): Boolean = this@FluidComponent.supportsExtraction()\n\n        override fun supportsInsertion(): Boolean = this@FluidComponent.supportsInsertion()\n\n        override fun getVersion(): Long = this@FluidComponent.version\n\n        override fun exactView(transaction: TransactionContext?, resource: FluidVariant?): StorageView<FluidVariant>? {\n            return super.exactView(transaction, resource)\n        }\n\n        override fun iterable(transaction: TransactionContext?): MutableIterable<StorageView<FluidVariant>> {\n            return super.iterable(transaction)\n        }\n\n        override fun iterator(transaction: TransactionContext?): MutableIterator<StorageView<FluidVariant>> {\n            return super.iterator(transaction)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/GuiSyncableComponent.kt",
    "content": "package me.steven.indrev.components\n\nimport me.steven.indrev.blockentities.BaseBlockEntity\nimport me.steven.indrev.gui.properties.*\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.minecraft.network.PacketByteBuf\n\nclass GuiSyncableComponent  {\n\n    val properties = mutableListOf<SyncableProperty<*>>()\n\n    @Suppress(\"UNCHECKED_CAST\")\n    operator fun <T> get(index: Int) = properties[index].value as T\n\n    fun add(index: Int, prop: SyncableProperty<*>) {\n        val r = properties.size until index + 1\n        for (x in r) {\n            properties.add(NullSyncableProperty)\n        }\n        properties[index] = prop\n    }\n}\n\ninterface SyncableObject {\n\n    var isDirty: Boolean\n\n    fun markDirty(condition: () -> Boolean = { true })\n\n    fun toPacket(buf: PacketByteBuf)\n\n    @Environment(EnvType.CLIENT)\n    fun fromPacket(buf: PacketByteBuf)\n}\n\nopen class DefaultSyncableObject : SyncableObject {\n\n    override var isDirty: Boolean = false\n\n    override fun markDirty(condition: () -> Boolean) {\n        if (isDirty || condition()) isDirty = true\n    }\n\n    override fun toPacket(buf: PacketByteBuf) {\n    }\n\n    override fun fromPacket(buf: PacketByteBuf) {\n    }\n}\n\nfun <T : SyncableObject> wrapTrackedObject(index: Int, obj: T): SyncableProperty<T> {\n    return object : SyncableProperty<T>(index, obj) {\n\n        override var isDirty: Boolean\n            get() = obj.isDirty\n            set(value) { obj.isDirty = value }\n\n        override fun toPacket(buf: PacketByteBuf) = obj.toPacket(buf)\n\n        override fun fromPacket(buf: PacketByteBuf) = obj.fromPacket(buf)\n    }\n}\n\nfun BaseBlockEntity.autosync(index: Int, value: Int, setter: (Int) -> Int = { it }): IntSyncableProperty {\n    val component = this.guiSyncableComponent ?: error(\"$this does not provide gui_syncable component\")\n    return IntSyncableProperty(index, value, setter)\n        .also { component.add(index, it) }\n}\n\nfun BaseBlockEntity.trackInt(index: Int, provider: () -> Int) {\n    val component = this.guiSyncableComponent ?: error(\"$this does not provide gui_syncable component\")\n    component.add(index, object : IntSyncableProperty(index, provider()) {\n        override var value: Int\n            get() = if (world!!.isClient) clientValue else provider()\n            set(value) {\n                clientValue = value\n            }\n\n        var clientValue: Int = 0\n\n        override var isDirty: Boolean\n            get() = clientValue != value\n            set(value) {\n                clientValue = if (!value) this.value else -1\n            }\n    })\n}\n\nfun BaseBlockEntity.autosync(index: Int, value: Long, setter: (Long) -> Long = { it }) : LongSyncableProperty {\n    val component = this.guiSyncableComponent ?: error(\"$this does not provide gui_syncable component\")\n    return LongSyncableProperty(index, value, setter)\n        .also { component.add(index, it) }\n}\n\nfun BaseBlockEntity.trackLong(index: Int, provider: () -> Long) {\n    val component = this.guiSyncableComponent ?: error(\"$this does not provide gui_syncable component\")\n    component.add(index, object : LongSyncableProperty(index, provider()) {\n        override var value: Long\n            get() = if (world!!.isClient) clientValue else provider()\n            set(value) {\n                clientValue = value\n            }\n\n        private var clientValue = 0L\n\n        override var isDirty: Boolean\n            get() = clientValue != value\n            set(value) {\n                clientValue = if (!value) this.value else -1\n            }\n    })\n}\n\nfun BaseBlockEntity.autosync(index: Int, value: Double, setter: (Double) -> Double = { it }) : DoubleSyncableProperty {\n    val component = this.guiSyncableComponent ?: error(\"$this does not provide gui_syncable component\")\n    return DoubleSyncableProperty(index, value, setter)\n        .also { component.add(index, it) }\n}\n\nfun BaseBlockEntity.trackDouble(index: Int, provider: () -> Double) {\n    val component = this.guiSyncableComponent ?: error(\"$this does not provide gui_syncable component\")\n    component.add(index, object : DoubleSyncableProperty(index, provider()) {\n        override var value: Double\n            get() = if (world!!.isClient) clientValue else provider()\n            set(value) {\n                clientValue = value\n            }\n\n        private var clientValue = 0.0\n\n        override var isDirty: Boolean\n            get() = clientValue != value\n            set(value) {\n                clientValue = if (!value) this.value else -1.0\n            }\n    })\n}\n\nfun BaseBlockEntity.autosync(index: Int, value: Boolean, setter: (Boolean) -> Boolean = { it }) : BooleanSyncableProperty {\n    val component = this.guiSyncableComponent ?: error(\"$this does not provide gui_syncable component\")\n    return BooleanSyncableProperty(index, value, setter)\n        .also { component.add(index, it) }\n}\n\nfun BaseBlockEntity.trackBoolean(index: Int, provider: () -> Boolean) {\n    val component = this.guiSyncableComponent ?: error(\"$this does not provide gui_syncable component\")\n    component.add(index, object : BooleanSyncableProperty(index, provider()) {\n        override var value: Boolean\n            get() = if (world!!.isClient) clientValue else provider()\n            set(value) {\n                clientValue = value\n            }\n\n        private var clientValue = false\n\n        override var isDirty: Boolean\n            get() = clientValue != value\n            set(value) {\n                clientValue = if (!value) this.value else !clientValue\n            }\n    })\n}\n\nfun <T : Enum<T>> BaseBlockEntity.autosync(index: Int, value: T, values: Array<T>, setter: (T) -> T = { it }) : EnumSyncableProperty<T> {\n    val component = this.guiSyncableComponent ?: error(\"$this does not provide gui_syncable component\")\n    return EnumSyncableProperty(index, value, values, setter)\n        .also { component.add(index, it) }\n}\n\nfun <T : Enum<T>> BaseBlockEntity.trackEnum(index: Int, values: Array<T>, provider: () -> T) {\n    val component = this.guiSyncableComponent ?: error(\"$this does not provide gui_syncable component\")\n    component.add(index, object : EnumSyncableProperty<T>(index, provider(), values) {\n        override var value: T\n            get() = if (world!!.isClient) clientValue else provider()\n            set(value) {\n                clientValue = value\n            }\n\n        private var clientValue = defaultValue\n\n        override var isDirty: Boolean\n            get() = clientValue != value\n            set(value) {\n                clientValue = if (!value) this.value else values[0]\n            }\n    })\n}\n\n\nfun <T : SyncableObject> BaseBlockEntity.trackObject(index: Int, value: T) {\n    val component = this.guiSyncableComponent ?: error(\"$this does not provide gui_syncable component\")\n    component.add(index, wrapTrackedObject(index, value))\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/InventoryComponent.kt",
    "content": "package me.steven.indrev.components\n\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.api.sideconfigs.SideConfiguration\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.inventories.IRInventory\nimport net.minecraft.inventory.Inventory\nimport net.minecraft.inventory.InventoryChangedListener\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.nbt.NbtList\n\nclass InventoryComponent(val syncable: MachineBlockEntity<*>, supplier: InventoryComponent.() -> IRInventory) : InventoryChangedListener {\n    val inventory: IRInventory = supplier()\n\n    init {\n        inventory.addListener(this)\n        inventory.component = this\n    }\n\n    val itemConfig: SideConfiguration = SideConfiguration(ConfigurationType.ITEM)\n\n    override fun onInventoryChanged(sender: Inventory?) {\n        syncable.markForUpdate()\n    }\n\n    fun readNbt(tag: NbtCompound?) {\n        val tagList = tag?.get(\"Inventory\") as NbtList? ?: NbtList()\n        tagList.indices.forEach { i ->\n            val stackTag = tagList.getCompound(i)\n            val slot = stackTag.getInt(\"Slot\")\n            inventory.setStack(slot, ItemStack.fromNbt(stackTag))\n        }\n        itemConfig.readNbt(tag)\n    }\n\n    fun writeNbt(tag: NbtCompound): NbtCompound {\n        val tagList = NbtList()\n        for (i in 0 until inventory.size()) {\n            val stackTag = NbtCompound()\n            stackTag.putInt(\"Slot\", i)\n            tagList.add(inventory.getStack(i).writeNbt(stackTag))\n        }\n        tag.put(\"Inventory\", tagList)\n        itemConfig.writeNbt(tag)\n        return tag\n    }\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/TemperatureComponent.kt",
    "content": "package me.steven.indrev.components\n\nimport me.steven.indrev.blockentities.BaseBlockEntity\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blockentities.crafters.CraftingMachineBlockEntity\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\n\nclass TemperatureComponent(\n    private val blockEntity: BaseBlockEntity,\n    private val heatingSpeed: Double,\n    val optimalRange: IntRange,\n    val limit: Int\n) {\n\n    var cooling = true\n\n    var temperature: Double = 25.0\n\n    init {\n        blockEntity.trackInt(MachineBlockEntity.TEMPERATURE_ID) { temperature.toInt() }\n        blockEntity.trackInt(MachineBlockEntity.MAX_TEMPERATURE_ID) { limit }\n    }\n\n    private var ticks = 0\n\n    fun readNbt(tag: NbtCompound?) {\n        temperature = tag?.getDouble(\"Temperature\") ?: 0.0\n        cooling = tag?.getBoolean(\"Cooling\") ?: false\n    }\n\n    fun writeNbt(tag: NbtCompound): NbtCompound {\n        tag.putDouble(\"Temperature\", temperature)\n        tag.putBoolean(\"Cooling\", cooling)\n        return tag\n    }\n\n    fun isFullEfficiency(): Boolean {\n        val machine = blockEntity as? MachineBlockEntity<*>\n        val inventoryComponent = machine?.inventoryComponent\n\n        return (!cooling || inventoryComponent?.inventory?.coolerStack?.isEmpty != true)\n                && temperature.toInt() in optimalRange\n    }\n\n    fun tick(shouldHeatUp: Boolean) {\n        ticks++\n        val machine = blockEntity as? MachineBlockEntity<*>\n        val random = blockEntity.world!!.random\n        val inv = machine?.inventoryComponent?.inventory\n        val (coolerStack, coolerItem) = inv?.coolerStack ?: ItemStack.EMPTY\n        val isHeatingUp = shouldHeatUp || (machine != null && coolerItem == IRItemRegistry.HEAT_COIL && machine.use(16))\n\n        if (cooling) {\n            val modifier = (blockEntity as? CraftingMachineBlockEntity<*>)?.craftingComponents?.size ?: 0\n            temperature -= heatingSpeed / if (isHeatingUp) 3 + modifier else 1\n\n            if (coolerStack.isDamageable && ticks % 120 == 0)\n                coolerStack.damage(1, random, null)\n            if (coolerStack.damage >= coolerStack.maxDamage) {\n                coolerStack.decrement(1)\n            }\n\n            if (temperature <= optimalRange.first + (2 * random.nextFloat() - 1) * 10) {\n                cooling = false\n            }\n        } else if (isHeatingUp) {\n            temperature += heatingSpeed\n            val n = (optimalRange.last + optimalRange.first) / 2.0\n            if (temperature >= n + (2 * random.nextFloat() - 1) * 15) {\n                cooling = true\n            }\n        } else if (temperature > 35.0) {\n            temperature -= heatingSpeed / 1.5\n        } else if (ticks % 15 == 0) {\n            temperature = (temperature + (2 * random.nextFloat() - 1) / 2).coerceIn(20.0, 35.0)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/multiblock/BlockStateFilter.kt",
    "content": "package me.steven.indrev.components.multiblock\n\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.Blocks\n\nclass BlockStateFilter(private val filter: (BlockState) -> Boolean, val display: BlockState = Blocks.AIR.defaultState) {\n\n    constructor(blockState: BlockState) : this({ b -> b == blockState }, blockState)\n\n    operator fun invoke(state: BlockState) = filter(state)\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/multiblock/MultiBlockComponent.kt",
    "content": "package me.steven.indrev.components.multiblock\n\nimport net.minecraft.block.BlockState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\n\nopen class MultiBlockComponent(\n    val structureDecider: (BlockState, World, BlockPos) -> StructureDefinition\n) {\n    var shouldRenderHologram = false\n    var variant = 0\n    private var ticks = 0\n    private var cachedMatchers: MutableMap<String, MultiblockMatcher> = hashMapOf()\n\n    open fun tick(world: World, pos: BlockPos, blockState: BlockState) {\n        ticks++\n        if (ticks % 15 != 0) return\n        getSelectedMatcher(world, pos, blockState).update(world, pos, blockState)\n    }\n\n    fun getSelectedMatcher(world: World, pos: BlockPos, blockState: BlockState): MultiblockMatcher {\n        val selected = structureDecider(blockState, world, pos)\n        return cachedMatchers.computeIfAbsent(selected.identifier) { selected.toMatcher() }\n    }\n\n    fun isBuilt(world: World, pos: BlockPos, blockState: BlockState, forceUpdate: Boolean = false): Boolean {\n        if (forceUpdate) {\n            getSelectedMatcher(world, pos, blockState).update(world, pos, blockState)\n        }\n        return getSelectedMatcher(world, pos, blockState).matches\n    }\n\n    fun toggleRender(isSneaking: Boolean) {\n        if (!isSneaking)\n            shouldRenderHologram = !shouldRenderHologram\n        else\n            variant++\n    }\n\n    fun readNbt(tag: NbtCompound?) {\n        shouldRenderHologram = tag?.getBoolean(\"ShouldRenderHologram\") ?: false\n        variant = tag?.getInt(\"Variant\") ?: 0\n    }\n\n    fun writeNbt(tag: NbtCompound): NbtCompound {\n        tag.putBoolean(\"ShouldRenderHologram\", shouldRenderHologram)\n        tag.putInt(\"Variant\", variant)\n        return tag\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/multiblock/MultiblockBlockEntityRenderer.kt",
    "content": "package me.steven.indrev.components.multiblock\n\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.BlockRotation\n\nclass MultiblockBlockEntityRenderer<T : BlockEntity>(val provider: (T) -> MultiBlockComponent) : BlockEntityRenderer<T> {\n    override fun render(\n        entity: T,\n        tickDelta: Float,\n        matrices: MatrixStack,\n        vertexConsumers: VertexConsumerProvider,\n        light: Int,\n        overlay: Int\n    ) {\n        val multiblock = provider(entity)\n        if (!multiblock.shouldRenderHologram) return\n        val rotation = MultiblockMatcher.rotateBlock(entity.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING].opposite)\n        val def = multiblock.getSelectedMatcher(entity.world!!, entity.pos, entity.cachedState).definition\n        def.holder.variants.values.toList()[multiblock.variant % def.holder.variants.size].forEach { (offset, state) ->\n            matrices.push()\n            val rotated = offset.rotate(rotation)\n            val blockPos = entity.pos.subtract(rotated)\n            val blockState = entity.world!!.getBlockState(blockPos)\n            if (blockState.material.isReplaceable) {\n                matrices.translate(-rotated.x.toDouble() + 0.25, -rotated.y.toDouble() + 0.25, -rotated.z.toDouble() + 0.25)\n                matrices.scale(0.5f, 0.5f, 0.5f)\n                MinecraftClient.getInstance().blockRenderManager\n                    .renderBlockAsEntity(state.display.rotate(rotation.rotate(BlockRotation.CLOCKWISE_180)), matrices, vertexConsumers, 15728880, overlay)\n            }\n            matrices.pop()\n        }\n    }\n\n    override fun rendersOutsideBoundingBox(blockEntity: T): Boolean = true\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/multiblock/MultiblockMatcher.kt",
    "content": "package me.steven.indrev.components.multiblock\n\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.BlockRotation\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.World\nimport net.minecraft.world.chunk.Chunk\n\nclass MultiblockMatcher(val definition: StructureDefinition) {\n\n    var builtId: StructureIdentifier? = null\n    val matches get() = builtId != null\n\n    fun update(world: World, pos: BlockPos, state: BlockState) {\n        if (builtId == null || !isBuilt(world, pos, state, definition.holder.variants[builtId]!!)) {\n            builtId = getBuiltStructureId(world, pos, state)\n        }\n    }\n\n    fun getBuiltStructureId(world: World, pos: BlockPos, state: BlockState): StructureIdentifier? {\n        return definition.holder.variants\n            .filter { (_, structure) -> isBuilt(world, pos, state, structure) }\n            .map { it.key }\n            .firstOrNull()\n    }\n\n    fun isBuilt(world: World, pos: BlockPos, state: BlockState, map: Map<BlockPos, BlockStateFilter>): Boolean {\n        var currentChunk: Chunk = world.getChunk(pos)\n        if (world.isOutOfHeightLimit(pos))\n            return false\n        val rotation =\n            rotateBlock(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING])\n        return map.all { (offset, statePredicate) ->\n            val statePos = pos.subtract(offset.rotate(rotation).rotate(BlockRotation.CLOCKWISE_180))\n            if (currentChunk.pos.startX < statePos.x || currentChunk.pos.endX > statePos.x || currentChunk.pos.startZ < statePos.z || currentChunk.pos.endZ > statePos.z) {\n                currentChunk = world.getChunk(statePos)\n            }\n            val blockState = currentChunk.getBlockState(statePos)\n                .rotate(rotateBlock0(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING]))\n            statePredicate(blockState)\n        }\n    }\n\n    fun getBuiltStructure(): Map<BlockPos, BlockStateFilter> {\n        return if (builtId == null) emptyMap() else definition.holder.variants[builtId] ?: emptyMap()\n    }\n\n    companion object {\n        fun rotateBlock(direction: Direction): BlockRotation {\n            return when (direction) {\n                Direction.NORTH -> BlockRotation.NONE\n                Direction.SOUTH -> BlockRotation.CLOCKWISE_180\n                Direction.WEST -> BlockRotation.COUNTERCLOCKWISE_90\n                Direction.EAST -> BlockRotation.CLOCKWISE_90\n                else -> return BlockRotation.NONE\n            }\n        }\n        // this is because there is something fundamentally wrong in the rotation/position of the multiblock structures\n        // I don't want to fix it now so this is a workaround and yes I am aware of the problems.\n        fun rotateBlock0(direction: Direction): BlockRotation {\n            return when (direction) {\n                Direction.NORTH -> BlockRotation.NONE\n                Direction.SOUTH -> BlockRotation.CLOCKWISE_180\n                Direction.WEST -> BlockRotation.CLOCKWISE_90\n                Direction.EAST -> BlockRotation.COUNTERCLOCKWISE_90\n                else -> return BlockRotation.NONE\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/multiblock/StructureDefinition.kt",
    "content": "package me.steven.indrev.components.multiblock\n\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.BlockRotation\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\nimport net.minecraft.world.chunk.Chunk\n\ninterface StructureDefinition {\n    val identifier: String\n    val isOptional: Boolean\n    val holder: StructureHolder\n\n    fun toMatcher(): MultiblockMatcher = MultiblockMatcher(this)\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/multiblock/StructureHolder.kt",
    "content": "package me.steven.indrev.components.multiblock\n\nimport com.google.common.collect.ImmutableMap\nimport net.minecraft.block.BlockState\nimport net.minecraft.util.BlockRotation\nimport net.minecraft.util.math.BlockPos\n\nclass StructureHolder(val variants: Map<StructureIdentifier, Map<BlockPos, BlockStateFilter>> = hashMapOf()) {\n\n    class Builder(private val definition: StructureDefinition, private val structure: MutableMap<BlockPos, BlockStateFilter> = HashMap()) {\n\n        private val createdStructures: MutableMap<StructureIdentifier, Map<BlockPos, BlockStateFilter>> = hashMapOf()\n\n        fun add(blockPos: BlockPos, blockState: (BlockState) -> Boolean): Builder {\n            structure[blockPos] = BlockStateFilter(blockState)\n            return this\n        }\n\n        fun from(structure: Map<BlockPos, BlockStateFilter>): Builder {\n            this.structure.clear()\n            this.structure.putAll(structure)\n            return this\n        }\n\n        fun corners(\n            center: BlockPos,\n            radius: Int,\n            state: (BlockState) -> Boolean,\n            rotation: BlockRotation = BlockRotation.NONE\n        ): Builder {\n            add(center.add(radius, radius, 0).rotate(rotation), state)\n            add(center.add(-radius, radius, 0).rotate(rotation), state)\n            add(center.add(radius, -radius, 0).rotate(rotation), state)\n            add(center.add(-radius, -radius, 0).rotate(rotation), state)\n            return this\n        }\n\n        fun horizontalCorners(\n            center: BlockPos,\n            radius: Int,\n            state: (BlockState) -> Boolean,\n            rotation: BlockRotation = BlockRotation.NONE\n        ): Builder {\n            add(center.add(radius, 0, radius).rotate(rotation), state)\n            add(center.add(-radius, 0, radius).rotate(rotation), state)\n            add(center.add(radius, 0, -radius).rotate(rotation), state)\n            add(center.add(-radius, 0, -radius).rotate(rotation), state)\n            return this\n        }\n\n        fun diamond(center: BlockPos, radius: Int, state: (BlockState) -> Boolean): Builder {\n            add(center.add(0, radius, 0), state)\n            add(center.add(0, -radius, 0), state)\n            add(center.add(radius, 0, 0), state)\n            add(center.add(-radius, 0, 0), state)\n            return this\n        }\n\n        fun cube(start: BlockPos, width: Int, depth: Int, height: Int, state: (BlockState) -> Boolean): Builder {\n            for (x in 0 until width) {\n                for (y in 0 until height) {\n                    for (z in 0 until depth) {\n                        add(start.add(BlockPos(x, y, z)), state)\n                    }\n                }\n            }\n            return this\n        }\n\n        fun add(blockPos: BlockPos, blockState: BlockState): Builder {\n            structure[blockPos] = BlockStateFilter(blockState)\n            return this\n        }\n\n        fun corners(\n            center: BlockPos,\n            radius: Int,\n            state: BlockState,\n            rotation: BlockRotation = BlockRotation.NONE\n        ): Builder {\n            add(center.add(radius, radius, 0).rotate(rotation), state)\n            add(center.add(-radius, radius, 0).rotate(rotation), state)\n            add(center.add(radius, -radius, 0).rotate(rotation), state)\n            add(center.add(-radius, -radius, 0).rotate(rotation), state)\n            return this\n        }\n\n        fun horizontalCorners(\n            center: BlockPos,\n            radius: Int,\n            state: BlockState,\n            rotation: BlockRotation = BlockRotation.NONE\n        ): Builder {\n            add(center.add(radius, 0, radius).rotate(rotation), state)\n            add(center.add(-radius, 0, radius).rotate(rotation), state)\n            add(center.add(radius, 0, -radius).rotate(rotation), state)\n            add(center.add(-radius, 0, -radius).rotate(rotation), state)\n            return this\n        }\n\n        fun diamond(center: BlockPos, radius: Int, state: BlockState): Builder {\n            add(center.add(0, radius, 0), state)\n            add(center.add(0, -radius, 0), state)\n            add(center.add(radius, 0, 0), state)\n            add(center.add(-radius, 0, 0), state)\n            return this\n        }\n\n        fun cube(start: BlockPos, width: Int, depth: Int, height: Int, state: BlockState): Builder {\n            for (x in 0 until width) {\n                for (y in 0 until height) {\n                    for (z in 0 until depth) {\n                        add(start.add(BlockPos(x, y, z)), state)\n                    }\n                }\n            }\n            return this\n        }\n\n\n        fun remove(pos: BlockPos): Builder {\n            structure.remove(pos)\n            return this\n        }\n\n        fun create(variant: String): Builder {\n            createdStructures[StructureIdentifier(\"indrev\", definition.identifier, variant)] = ImmutableMap.copyOf(structure)\n            structure.clear()\n            return this\n        }\n\n        fun build(): StructureHolder = StructureHolder(ImmutableMap.copyOf(createdStructures))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/multiblock/StructureIdentifier.kt",
    "content": "package me.steven.indrev.components.multiblock\n\nimport net.minecraft.util.Identifier\n\nclass StructureIdentifier(val modId: String, val structure: String, val variant: String): Identifier(modId, \"$structure/$variant\")"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/multiblock/definitions/FactoryStructureDefinition.kt",
    "content": "package me.steven.indrev.components.multiblock.definitions\n\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport me.steven.indrev.components.multiblock.MultiblockMatcher\nimport me.steven.indrev.components.multiblock.StructureDefinition\nimport me.steven.indrev.components.multiblock.StructureHolder\nimport me.steven.indrev.registry.IRBlockRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.state.property.Properties\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.World\n\n/**\n * Multiple definitions rather than Structure variations were used here to avoid useless block state calls\n */\nobject FactoryStructureDefinition : StructureDefinition {\n\n    private val CONTROLLER_STATE = IRBlockRegistry.CONTROLLER.defaultState.with(Properties.HORIZONTAL_FACING, Direction.NORTH)\n    private val DUCT_STATE = IRBlockRegistry.DUCT.defaultState.with(Properties.HORIZONTAL_FACING, Direction.WEST)\n    private val CABINET_STATE = IRBlockRegistry.CABINET.defaultState.with(Properties.HORIZONTAL_FACING, Direction.WEST)\n    private val INTAKE_STATE = IRBlockRegistry.INTAKE.defaultState.with(Properties.HORIZONTAL_FACING, Direction.WEST)\n\n    override val identifier: String = \"factory\"\n    override val isOptional: Boolean = false\n    override val holder: StructureHolder =\n        StructureHolder.Builder(this)\n            .cube(BlockPos(-1, 1, 1), 3, 3, 1, IRBlockRegistry.FRAME.defaultState)\n            .cube(BlockPos(0, 0, 1), 2, 3, 1, IRBlockRegistry.SILO.defaultState)\n            .cube(BlockPos(1, -1, 1), 1, 3, 1, INTAKE_STATE)\n            .cube(BlockPos(0, -1, 1), 1, 3, 1, DUCT_STATE)\n            .cube(BlockPos(-1, 0, 2), 1, 2, 1, CABINET_STATE)\n            .add(BlockPos(-1, 0, 1), CONTROLLER_STATE)\n            .add(BlockPos(-1, -1, 1), IRBlockRegistry.WARNING_STROBE.defaultState)\n            .create(\"factory\")\n            .build()\n\n    object Inverted : StructureDefinition {\n\n        private val DUCT_STATE_INVERTED = IRBlockRegistry.DUCT.defaultState.with(Properties.HORIZONTAL_FACING, Direction.EAST)\n        private val CABINET_STATE_INVERTED = IRBlockRegistry.CABINET.defaultState.with(Properties.HORIZONTAL_FACING, Direction.EAST)\n        private val INTAKE_STATE_INVERTED = IRBlockRegistry.INTAKE.defaultState.with(Properties.HORIZONTAL_FACING, Direction.EAST)\n\n        override val identifier: String = \"factory_inverted\"\n        override val isOptional: Boolean = false\n        override val holder: StructureHolder =\n            StructureHolder.Builder(this)\n                .cube(BlockPos(-1, 1, 1), 3, 3, 1, IRBlockRegistry.FRAME.defaultState)\n                .cube(BlockPos(-1, 0, 1), 2, 3, 1, IRBlockRegistry.SILO.defaultState)\n                .cube(BlockPos(-1, -1, 1), 1, 3, 1, INTAKE_STATE_INVERTED)\n                .cube(BlockPos(0, -1, 1), 1, 3, 1, DUCT_STATE_INVERTED)\n                .cube(BlockPos(1, 0, 2), 1, 2, 1, CABINET_STATE_INVERTED)\n                .add(BlockPos(1, 0, 1), CONTROLLER_STATE)\n                .add(BlockPos(1, -1, 1), IRBlockRegistry.WARNING_STROBE.defaultState)\n                .create(\"factory\")\n                .build()\n    }\n\n    val SELECTOR: (BlockState, World, BlockPos) -> StructureDefinition = { state, world, pos ->\n        val rotation =\n            MultiblockMatcher.rotateBlock(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING].opposite)\n        val ductState = world.getBlockState(pos.subtract(BlockPos(0, -1, 1).rotate(rotation)))\n        if (!ductState.isOf(IRBlockRegistry.DUCT)) FactoryStructureDefinition\n        else {\n            val ductFacing = ductState[HorizontalFacingMachineBlock.HORIZONTAL_FACING]\n            if (ductFacing == DUCT_STATE.rotate(rotation)[HorizontalFacingMachineBlock.HORIZONTAL_FACING].opposite)\n                FactoryStructureDefinition\n            else\n                Inverted\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/multiblock/definitions/SolarPowerPlantTowerStructureDefinition.kt",
    "content": "package me.steven.indrev.components.multiblock.definitions\n\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport me.steven.indrev.components.multiblock.*\nimport me.steven.indrev.registry.IRBlockRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.Blocks\nimport net.minecraft.block.FacingBlock\nimport net.minecraft.util.BlockRotation\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport kotlin.math.abs\n\nobject SolarPowerPlantTowerStructureDefinition : StructureDefinition {\n\n    private val CASING = IRBlockRegistry.STEAM_TURBINE_CASING_BLOCK\n    private val RESISTANT_GLASS = Blocks.GLASS.defaultState\n    private val FLUID_INPUT = IRBlockRegistry.FLUID_VALVE.defaultState\n    private val FLUID_OUTPUT = IRBlockRegistry.FLUID_VALVE.defaultState.with(FacingBlock.FACING, Direction.NORTH)\n\n    override val identifier: String = \"solar_power_plant\"\n    override val isOptional: Boolean = false\n    override val holder: StructureHolder = StructureHolder.Builder(this)\n        .from(createStructureMap())\n        .create(\"default\")\n        .build()\n\n    fun getFluidInputPositions(pos: BlockPos, state: BlockState): List<BlockPos> {\n        val rotation =\n            MultiblockMatcher.rotateBlock(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING])\n\n        val radius = 3\n        val positions = hashSetOf<BlockPos>()\n        for (x in -radius..radius) {\n            for (z in -radius..radius) {\n                if ((abs(z) == radius) || (abs(x) == radius))\n                    positions.add(BlockPos(x, 0, z + radius))\n            }\n        }\n        return positions\n            .map { offset -> pos.subtract(offset.rotate(rotation).rotate(BlockRotation.CLOCKWISE_180)) }.toList()\n    }\n\n    fun getSolarReceiverPositions(pos: BlockPos, state: BlockState): List<BlockPos> {\n        val rotation =\n            MultiblockMatcher.rotateBlock(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING])\n\n        val radius = 3\n        val positions = hashSetOf<BlockPos>()\n        for (x in -radius + 1 until radius) {\n            for (y in -10..-4) {\n                for (z in -radius + 1 until radius) {\n                    if (y == -8 && (abs(x) == radius-1 || abs(z) == radius-1))\n                        positions.add(BlockPos(x, y, z + 3))\n                }\n            }\n        }\n\n        return positions\n            .map { offset -> pos.subtract(offset.rotate(rotation).rotate(BlockRotation.CLOCKWISE_180)) }.toList()\n    }\n\n    private fun createStructureMap(): Map<BlockPos, BlockStateFilter> {\n        val map = hashMapOf<BlockPos, BlockStateFilter>()\n        val radius = 3\n        for (x in -radius..radius) {\n            for (y in -3..1) {\n                for (z in -radius..radius) {\n                    if (arrayOf(abs(x), abs(y), abs(z)).count { it == radius } >= 2 || y == -3 || y == 1)\n                        map[BlockPos(x, y, z + radius)] = BlockStateFilter(Blocks.IRON_BLOCK.defaultState)\n                    else if ((y == 0 && abs(z) == radius) || (y == 0 && abs(x) == radius))\n                        map[BlockPos(x, y, z + radius)] = BlockStateFilter({state -> state == Blocks.IRON_BLOCK.defaultState || state == FLUID_INPUT }, FLUID_INPUT)\n                    else if (arrayOf(abs(x), abs(y), abs(z)).count { it == radius } != 0)\n                        map[BlockPos(x, y, z + radius)] = BlockStateFilter({ state -> state == CASING || state == RESISTANT_GLASS }, RESISTANT_GLASS)\n                }\n            }\n        }\n        map[BlockPos(0, 1, 0)] = BlockStateFilter(FLUID_OUTPUT)\n\n        for (x in -radius + 1 until radius) {\n            for (y in -10..-4) {\n                for (z in -radius + 1 until radius) {\n                    if (y == -8 && (abs(x) == radius-1 || abs(z) == radius-1))\n                        map[BlockPos(x, y, z + 3)] = BlockStateFilter(IRBlockRegistry.SOLAR_RECEIVER_BLOCK.defaultState)\n                    else if (abs(x) == radius-1 || abs(z) == radius-1 || y == -10)\n                        map[BlockPos(x, y, z + 3)] = BlockStateFilter(Blocks.IRON_BLOCK.defaultState)\n                }\n            }\n        }\n\n        map.remove(BlockPos.ORIGIN)\n\n        return map\n    }\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/components/multiblock/definitions/SteamTurbineStructureDefinition.kt",
    "content": "package me.steven.indrev.components.multiblock.definitions\n\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport me.steven.indrev.blocks.misc.VerticalFacingBlock\nimport me.steven.indrev.components.multiblock.*\nimport me.steven.indrev.registry.IRBlockRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.Blocks\nimport net.minecraft.block.FacingBlock\nimport net.minecraft.block.HorizontalFacingBlock\nimport net.minecraft.util.BlockRotation\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport kotlin.math.abs\n\nobject SteamTurbineStructureDefinition : StructureDefinition {\n\n    private val ROTOR_UP = IRBlockRegistry.STEAM_TURBINE_ROTOR_BLOCK.defaultState.with(VerticalFacingBlock.FACING, Direction.UP)\n    private val ROTOR_DOWN = IRBlockRegistry.STEAM_TURBINE_ROTOR_BLOCK.defaultState.with(VerticalFacingBlock.FACING, Direction.DOWN)\n    private val PRESSURE_VALVE_SOUTH = IRBlockRegistry.STEAM_TURBINE_PRESSURE_VALVE_BLOCK.defaultState.with(HorizontalFacingBlock.FACING, Direction.SOUTH)\n    private val STEAM_INPUT_VALVE_NORTH = IRBlockRegistry.STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK.defaultState.with(FacingBlock.FACING, Direction.NORTH)\n    private val ENERGY_OUTPUT_NORTH = IRBlockRegistry.STEAM_TURBINE_ENERGY_OUTPUT.defaultState.with(HorizontalFacingBlock.FACING, Direction.NORTH)\n    private val CASING = IRBlockRegistry.STEAM_TURBINE_CASING_BLOCK\n    private val RESISTANT_GLASS = Blocks.GLASS.defaultState\n\n    override val identifier: String = \"steam_turbine\"\n    override val isOptional: Boolean = false\n    override val holder: StructureHolder = StructureHolder.Builder(this)\n        .from(createStructureMap(2))\n        .create(\"5x5x5\")\n        .from(createStructureMap(3))\n        .create(\"7x7x7\")\n        .from(createStructureMap(4))\n        .create(\"9x9x9\")\n        .from(createStructureMap(5))\n        .create(\"11x11x11\")\n        .from(createStructureMap(6))\n        .create(\"13x13x13\")\n        .from(createStructureMap(7))\n        .create(\"15x15x15\")\n        .build()\n\n    fun getInputValvePositions(pos: BlockPos, state: BlockState, matcher: MultiblockMatcher): List<BlockPos> {\n        val rotation =\n            MultiblockMatcher.rotateBlock(state[HorizontalFacingMachineBlock.HORIZONTAL_FACING])\n\n        matcher.builtId?.also { id ->\n            val radius = getRadius(id)\n            return arrayOf(\n                BlockPos(-radius + 1, 0, 0),\n                BlockPos(radius - 1, 0, 0),\n                BlockPos(0, -radius + 1, 0),\n                BlockPos(0, radius - 1, 0)\n            )\n                .map { offset -> pos.subtract(offset.rotate(rotation).rotate(BlockRotation.CLOCKWISE_180)) }.toList()\n        }\n        return emptyList()\n    }\n\n    fun getRadius(id: StructureIdentifier) = (id.variant.substring(0, id.variant.indexOf(\"x\")).toInt() - 1) / 2\n\n    private fun createStructureMap(radius: Int): Map<BlockPos, BlockStateFilter> {\n        val map = hashMapOf<BlockPos, BlockStateFilter>()\n        for (x in -radius..radius) {\n            for (y in -radius..radius) {\n                for (z in -radius..radius) {\n                    if (arrayOf(abs(x), abs(y), abs(z)).count { it == radius } >= 2 || (y == 0 && abs(z) == radius) || (x == 0 && abs(z) == radius) || abs(y) == radius)\n                        map[BlockPos(x, y, z + radius)] = BlockStateFilter(Blocks.IRON_BLOCK.defaultState)\n                    else if (arrayOf(abs(x), abs(y), abs(z)).count { it == radius } != 0)\n                        map[BlockPos(x, y, z + radius)] = BlockStateFilter({ state -> state == CASING || state == RESISTANT_GLASS }, RESISTANT_GLASS)\n                }\n            }\n        }\n\n        map[BlockPos(0, -radius, radius)] = BlockStateFilter(ROTOR_UP)\n        map[BlockPos(0, radius, radius)] = BlockStateFilter(ROTOR_DOWN)\n\n        map[BlockPos(-radius + 1, 0, 0)] = BlockStateFilter(STEAM_INPUT_VALVE_NORTH)\n        map[BlockPos(radius - 1, 0, 0)] = BlockStateFilter(STEAM_INPUT_VALVE_NORTH)\n        map[BlockPos(0, -radius + 1, 0)] = BlockStateFilter(STEAM_INPUT_VALVE_NORTH)\n        map[BlockPos(0, radius - 1, 0)] = BlockStateFilter(STEAM_INPUT_VALVE_NORTH)\n\n        map[BlockPos(-radius + 1, 0, radius * 2)] = BlockStateFilter(PRESSURE_VALVE_SOUTH)\n        map[BlockPos(radius - 1, 0, radius * 2)] = BlockStateFilter(PRESSURE_VALVE_SOUTH)\n        map[BlockPos(0, -radius + 1, radius * 2)] = BlockStateFilter(PRESSURE_VALVE_SOUTH)\n        map[BlockPos(0, radius - 1, radius * 2)] = BlockStateFilter(PRESSURE_VALVE_SOUTH)\n\n        for (i in -radius + 1 until radius)\n            map[BlockPos(0, i, radius)] = BlockStateFilter(Blocks.ACACIA_FENCE.defaultState)\n\n        map[BlockPos(0, 1, 0)] = BlockStateFilter(ENERGY_OUTPUT_NORTH)\n\n        map.remove(BlockPos(0, 0, 0))\n        return map\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/config/IRConfig.kt",
    "content": "package me.steven.indrev.config\n\nimport com.google.gson.GsonBuilder\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.registry.IRItemRegistry\nimport net.fabricmc.loader.api.FabricLoader\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.tag.ItemTags\nimport net.minecraft.tag.TagKey\nimport net.minecraft.util.Identifier\nimport java.io.File\n\nobject IRConfig {\n\n    private val gson = GsonBuilder().setPrettyPrinting().create()\n\n    lateinit var generators: Generators\n    lateinit var machines: Machines\n    lateinit var cables: Cables\n    lateinit var upgrades: Upgrades\n    lateinit var oregen: OreGen\n    lateinit var hud: Hud\n    lateinit var miningRigConfig: MiningRigConfig\n\n    init {\n       readConfigs()\n    }\n\n    fun readConfigs() {\n        generators = readOrCreate(\"generators.json\") { Generators() }\n        machines = readOrCreate(\"machines.json\") { Machines() }\n        cables = readOrCreate(\"cables.json\") { Cables() }\n        upgrades = readOrCreate(\"upgrades.json\") { Upgrades() }\n        oregen = readOrCreate(\"oregen.json\") { OreGen() }\n        hud = readOrCreate(\"hud.json\") { Hud() }\n        miningRigConfig = readOrCreate(\"mining_rig_config.json\") { MiningRigConfig() }\n    }\n\n    private inline fun <reified T> readOrCreate(file: String, default: () -> T): T {\n        val dir = File(FabricLoader.getInstance().configDir.toFile(), \"indrev\")\n        if (!dir.exists() && !dir.mkdirs()) {\n            IndustrialRevolution.LOGGER.error(\"Could not create directory, using default configs.\")\n            return default()\n        }\n        val f = File(dir, file)\n        try {\n            if (f.exists())\n                return gson.fromJson(f.readLines().joinToString(\"\"), T::class.java)\n            else if (!f.createNewFile())\n                IndustrialRevolution.LOGGER.error(\"Failed to create default config file ($file), using default config.\")\n            else\n                f.writeText(gson.toJson(default()))\n            return default()\n        } catch (e: Exception) {\n            IndustrialRevolution.LOGGER.error(\"Failed to read config file! Using default values.\", e)\n            return default()\n        }\n    }\n    \n    fun writeToClient(buf: PacketByteBuf) {\n        val gson = GsonBuilder().create()\n        buf.writeString(gson.toJson(generators))\n        buf.writeString(gson.toJson(machines))\n        buf.writeString(gson.toJson(cables))\n        buf.writeString(gson.toJson(upgrades))\n        buf.writeString(gson.toJson(oregen))\n        buf.writeString(gson.toJson(miningRigConfig))\n    }\n    \n    fun readFromServer(buf: PacketByteBuf) {\n        generators = gson.fromJson(buf.readString(), Generators::class.java)\n        machines = gson.fromJson(buf.readString(), Machines::class.java)\n        cables = gson.fromJson(buf.readString(), Cables::class.java)\n        upgrades = gson.fromJson(buf.readString(), Upgrades::class.java)\n        oregen = gson.fromJson(buf.readString(), OreGen::class.java)\n        miningRigConfig = gson.fromJson(buf.readString(), MiningRigConfig::class.java)\n    }\n}\n\nclass Generators {\n    val coalGenerator: GeneratorConfig = GeneratorConfig(16.0, 1.5, 1000, Tier.MK1.io)\n\n    val solarGeneratorMk1: GeneratorConfig = GeneratorConfig(8.0, 1.5, 500, Tier.MK1.io)\n\n    val solarGeneratorMk3: GeneratorConfig = GeneratorConfig(12.0, 1.5, 1500, Tier.MK3.io)\n\n    val heatGenerator: GeneratorConfig = GeneratorConfig(128.0, -1.0, 10000, Tier.MK4.io)\n\n    val gasGenerator: GeneratorConfig = GeneratorConfig(1.0, 1.5, 10000, Tier.MK4.io)\n\n    val biomassGenerator: GeneratorConfig = GeneratorConfig(64.0, 1.5, 10000, Tier.MK3.io)\n\n    val steamTurbine: GeneratorConfig = GeneratorConfig(4.0, -1.0, 20000, Tier.MK4.io)\n}\n\nclass GeneratorConfig(\n    val ratio: Double = 16.0,\n    val temperatureBoost: Double = 1.5,\n    override val maxEnergyStored: Long,\n    val maxOutput: Long\n) : IConfig\n\nclass Machines {\n    val electricFurnaceMk1: HeatMachineConfig = HeatMachineConfig(4, 1.0, 2.0, 1000, Tier.MK1.io)\n\n    val electricFurnaceMk2: HeatMachineConfig = HeatMachineConfig(8, 2.0, 2.0, 5000, Tier.MK2.io)\n\n    val electricFurnaceMk3: HeatMachineConfig = HeatMachineConfig(16, 3.0, 2.0, 10000, Tier.MK3.io)\n\n    val electricFurnaceMk4: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, Tier.MK4.io)\n\n    val electricFurnaceFactory: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, 16384)\n\n    val pulverizerMk1: HeatMachineConfig = HeatMachineConfig(4, 1.0, 2.0, 1000, Tier.MK1.io)\n\n    val pulverizerMk2: HeatMachineConfig = HeatMachineConfig(8, 2.0, 2.0, 5000, Tier.MK2.io)\n\n    val pulverizerMk3: HeatMachineConfig = HeatMachineConfig(16, 3.0, 2.0, 10000, Tier.MK3.io)\n\n    val pulverizerMk4: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, Tier.MK4.io)\n\n    val pulverizerFactory: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, 16384)\n\n    val compressorMk1: HeatMachineConfig = HeatMachineConfig(4, 1.0, 2.0, 1000, Tier.MK1.io)\n\n    val compressorMk2: HeatMachineConfig = HeatMachineConfig(8, 2.0, 2.0, 5000, Tier.MK2.io)\n\n    val compressorMk3: HeatMachineConfig = HeatMachineConfig(16, 3.0, 2.0, 10000, Tier.MK3.io)\n\n    val compressorMk4: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, Tier.MK4.io)\n\n    val compressorFactory: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, 16384)\n\n    val sawmillMk1: HeatMachineConfig = HeatMachineConfig(4, 1.0, 2.0, 1000, Tier.MK1.io)\n\n    val sawmillMk2: HeatMachineConfig = HeatMachineConfig(8, 2.0, 2.0, 5000, Tier.MK2.io)\n\n    val sawmillMk3: HeatMachineConfig = HeatMachineConfig(16, 3.0, 2.0, 10000, Tier.MK3.io)\n\n    val sawmillMk4: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0, 100000, Tier.MK4.io)\n\n    val infuserMk1: HeatMachineConfig = HeatMachineConfig(4, 1.0, 2.0, 1000, Tier.MK1.io)\n\n    val infuserMk2: HeatMachineConfig = HeatMachineConfig(8, 2.0, 2.0,5000, Tier.MK2.io)\n\n    val infuserMk3: HeatMachineConfig = HeatMachineConfig(16, 3.0, 2.0,10000, Tier.MK3.io)\n\n    val infuserMk4: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0,100000, Tier.MK4.io)\n\n    val infuserFactory: HeatMachineConfig = HeatMachineConfig(64, 4.5, 2.0, 100000, 16384)\n\n    val recycler: MachineConfig = MachineConfig(8, 2.5, 50000, Tier.MK2.io)\n\n    val chopperMk1: MachineConfig = MachineConfig(16, 50.0, 1000, Tier.MK1.io)\n\n    val chopperMk2: MachineConfig = MachineConfig(32, 40.0, 5000, Tier.MK2.io)\n\n    val chopperMk3: MachineConfig = MachineConfig(64, 30.0, 25000, Tier.MK3.io)\n\n    val chopperMk4: MachineConfig = MachineConfig(128, 20.0, 50000, Tier.MK4.io)\n\n    val farmerMk1: MachineConfig = MachineConfig(16, 50.0, 1000, Tier.MK1.io)\n\n    val farmerMk2: MachineConfig = MachineConfig(32, 40.0, 5000, Tier.MK2.io)\n\n    val farmerMk3: MachineConfig = MachineConfig(64, 30.0, 25000, Tier.MK3.io)\n\n    val farmerMk4: MachineConfig = MachineConfig(128, 20.0, 50000, Tier.MK4.io)\n\n    val slaughterMk1: MachineConfig = MachineConfig(16, 50.0, 1000, Tier.MK1.io)\n\n    val slaughterMk2: MachineConfig = MachineConfig(32, 40.0, 5000, Tier.MK2.io)\n\n    val slaughterMk3: MachineConfig = MachineConfig(64, 30.0, 25000, Tier.MK3.io)\n\n    val slaughterMk4: MachineConfig = MachineConfig(128, 20.0, 50000, Tier.MK4.io)\n\n    val rancherMk1: MachineConfig = MachineConfig(16, 50.0, 1000, Tier.MK1.io)\n\n    val rancherMk2: MachineConfig = MachineConfig(32, 40.0, 5000, Tier.MK2.io)\n\n    val rancherMk3: MachineConfig = MachineConfig(64, 30.0, 25000, Tier.MK3.io)\n\n    val rancherMk4: MachineConfig = MachineConfig(128, 20.0, 50000, Tier.MK4.io)\n\n    val miner: MachineConfig = MachineConfig(64, 100.0, 50000, Tier.MK4.io)\n\n    val dataCardWriter: MachineConfig = MachineConfig(128, 100.0, 2500, Tier.MK4.io)\n\n    val drill: Long = 256\n\n    val fishingMk2: MachineConfig = MachineConfig(8, 400.0, 50000, Tier.MK2.io)\n\n    val fishingMk3: MachineConfig = MachineConfig(16, 400.0, 50000, Tier.MK3.io)\n\n    val fishingMk4: MachineConfig = MachineConfig(64, 500.0, 50000, Tier.MK4.io)\n\n    val dirtOxygenator: MachineConfig = MachineConfig(16, 50.0, 1000, Tier.MK1.io)\n\n    val drain: MachineConfig = MachineConfig(4, 20.0, 1000, Tier.MK1.io)\n\n    val pump: MachineConfig = MachineConfig(32, 50.0, 50, Tier.MK1.io)\n\n    val pumpMaxRange: Int = 64\n\n    val smelter: HeatMachineConfig = HeatMachineConfig(64, 4.0, 2.0,50000, Tier.MK4.io)\n\n    val condenser: MachineConfig = MachineConfig(64, 4.0, 50000, Tier.MK4.io)\n\n    val fluidInfuserMk1: MachineConfig = MachineConfig(4, 1.0, 1000, Tier.MK1.io)\n\n    val fluidInfuserMk2: MachineConfig = MachineConfig(8, 2.0, 5000, Tier.MK2.io)\n\n    val fluidInfuserMk3: MachineConfig = MachineConfig(16, 3.0, 10000, Tier.MK3.io)\n\n    val fluidInfuserMk4: MachineConfig = MachineConfig(64, 4.0, 100000, Tier.MK4.io)\n\n    val electrolyticSeparatorMk1: MachineConfig = MachineConfig(4, 1.0, 1000, Tier.MK1.io)\n\n    val electrolyticSeparatorMk2: MachineConfig = MachineConfig(8, 2.0, 5000, Tier.MK2.io)\n\n    val electrolyticSeparatorMk3: MachineConfig = MachineConfig(16, 3.0, 10000, Tier.MK3.io)\n\n    val electrolyticSeparatorMk4: MachineConfig = MachineConfig(64, 4.0, 100000, Tier.MK4.io)\n\n    val modularWorkbench: MachineConfig = MachineConfig(64, 1.0, 5000, Tier.MK4.io)\n\n    val laser: MachineConfig = MachineConfig(4096, 1.0, 2500000, 16384)\n\n    val distiller: MachineConfig = MachineConfig(64, 4.0, 100000, Tier.MK4.io)\n\n    val lazuliFluxContainerMk1: LFCConfig = LFCConfig(10000, 128, 128)\n\n    val lazuliFluxContainerMk2: LFCConfig = LFCConfig(100000, 512, 512)\n\n    val lazuliFluxContainerMk3: LFCConfig = LFCConfig(1000000, 4096, 4096)\n\n    val lazuliFluxContainerMk4: LFCConfig = LFCConfig(10000000, 16384, 16384)\n}\n\nclass HeatMachineConfig(\n    override val energyCost: Long,\n    override val processSpeed: Double,\n    val processTemperatureBoost: Double,\n    override val maxEnergyStored: Long,\n    override val maxInput: Long\n) : BasicMachineConfig\n\nopen class MachineConfig(\n    override val energyCost: Long,\n    override val processSpeed: Double,\n    override val maxEnergyStored: Long,\n    override val maxInput: Long\n) : BasicMachineConfig\n\ninterface BasicMachineConfig : IConfig {\n    val energyCost: Long\n    val processSpeed: Double\n    val maxInput: Long\n}\n\nclass LFCConfig(\n    override val maxEnergyStored: Long,\n    val maxInput: Long,\n    val maxOutput: Long\n): IConfig\n\ninterface IConfig {\n    val maxEnergyStored: Long\n}\n\nclass Cables {\n    val cableMk1 = 128\n    val cableMk2 = 512\n    val cableMk3 = 4096\n    val cableMk4 = 16384\n\n    val itemPipeMk1 = 32\n    val itemPipeMk2 = 64\n    val itemPipeMk3 = 128\n    val itemPipeMk4 = 256\n\n    val fluidPipeMk1 = 1\n    val fluidPipeMk2 = 2\n    val fluidPipeMk3 = 4\n    val fluidPipeMk4 = 8\n}\n\nclass Upgrades  {\n    val speedUpgradeModifier = 6.5\n    val energyUpgradeModifier = 1.02\n    val bufferUpgradeModifier = 25000\n    val damageUpgradeModifier = 3.0\n}\n\nclass OreGen  {\n    val tin = true\n    val nikolite = true\n    val lead = true\n    val silver = true\n    val tungsten = true\n    val sulfuricAcidLake = true\n    val sulfurCrystals = true\n}\n\nclass MiningRigConfig {\n    val allowedTags = mutableMapOf(\n        ItemTags.COPPER_ORES.id.toString() to 1,\n        ItemTags.COAL_ORES.id.toString() to 1,\n        ItemTags.GOLD_ORES.id.toString() to 2,\n        ItemTags.IRON_ORES.id.toString() to 2,\n        IndustrialRevolution.NIKOLITE_ORES.id.toString() to 2,\n        IndustrialRevolution.TIN_ORES.id.toString() to 2,\n        ItemTags.REDSTONE_ORES.id.toString() to 2,\n        IndustrialRevolution.LEAD_ORES.id.toString() to 3,\n        IndustrialRevolution.SILVER_ORES.id.toString() to 3,\n        IndustrialRevolution.TUNGSTEN_ORES.id.toString() to 3,\n        ItemTags.DIAMOND_ORES.id.toString() to 4,\n        ItemTags.EMERALD_ORES.id.toString() to 4,\n        IndustrialRevolution.ANCIENT_DEBRIS_ORES.id.toString() to 4\n    )\n}\n\nclass Hud {\n    val renderPosX = 0\n    val renderPosY = 0\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/DataFactory.kt",
    "content": "package me.steven.indrev.datagen\n\nimport net.minecraft.util.Identifier\nimport java.io.File\n\ninterface DataFactory<T, P> {\n\n    val extension: String\n\n    fun getFileName(t: T, id: Identifier): String = id.path\n\n    fun generate(): P?\n\n    fun write(file: File, t: P)\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/DataGenerator.kt",
    "content": "package me.steven.indrev.datagen\n\nimport net.minecraft.util.Identifier\nimport java.io.File\n\nabstract class DataGenerator<T, P>(val dir: File, val namespace: String, val fallback: (T) -> DataFactory<T, P>) {\n\n    protected val generators = HashMap<T, DataFactory<T, P>>()\n\n    init {\n        dir.mkdirs()\n    }\n\n    operator fun get(obj: T): DataFactory<T, P> = generators.getOrDefault(obj, fallback(obj))\n\n    fun register(obj: T, factory: DataFactory<T, P>) {\n        generators[obj] = factory\n    }\n\n    fun generate(identifier: Identifier, obj: T): Boolean {\n        val jsonFactory = this[obj]\n        return generate(identifier, obj, jsonFactory)\n    }\n\n    fun generate(identifier: Identifier, obj: T, factory: DataFactory<T, P>): Boolean {\n        val file = File(dir, \"${factory.getFileName(obj, identifier)}.${factory.extension}\")\n        file.parentFile.mkdirs()\n        val output = factory.generate()\n        if (output != null) {\n            file.createNewFile()\n            factory.write(file, output)\n            return true\n        }\n        return false\n    }\n\n    abstract fun generate(): Int\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/DataGeneratorManager.kt",
    "content": "package me.steven.indrev.datagen\n\nimport com.google.gson.JsonArray\nimport com.google.gson.JsonObject\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.datagen.generators.*\nimport me.steven.indrev.datagen.utils.MetalModel\nimport me.steven.indrev.datagen.utils.MetalSpriteRegistry\nimport me.steven.indrev.items.misc.IRMachineUpgradeItem\nimport me.steven.indrev.registry.IRBlockRegistry\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.registry.MachineRegistry\nimport net.minecraft.item.BlockItem\nimport net.minecraft.item.Items\nimport net.minecraft.util.DyeColor\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.registry.Registry\nimport java.io.File\nimport kotlin.system.exitProcess\n\nclass DataGeneratorManager(namespace: String) {\n\n    val root = File(\"generated\")\n\n    val lootTableGenerator = LootTableGenerator(root, namespace, LootTableGenerator.SELF_DROP)\n    val itemModelGenerator = ItemModelGenerator(root, namespace, ItemModelGenerator.DEFAULT_ITEM)\n    val blockModelGenerator = BlockModelGenerator(root, namespace) { JsonFactory.nullFactory() }\n    val materialRecipeGenerator = MaterialRecipeGenerator(root, namespace) { JsonFactory.nullFactory() }\n    val materialTagGenerator = MaterialTagGenerator(root, namespace) { JsonFactory.nullFactory() }\n    val metalSpriteGenerator = MetalSpriteGenerator(root, namespace) { ImageFactory.nullFactory() }\n\n    init {\n        root.mkdir()\n\n        arrayOf(\"tin\", \"lead\", \"tungsten\", \"silver\").forEach { material ->\n            materialRecipeGenerator.register(\"raw_${material}\", rawOreIntoBlock(material))\n            materialRecipeGenerator.register(\"raw_${material}_block\", rawOreBlockIntoRawItem(material))\n        }\n\n        arrayOf(\"copper\", \"tin\", \"lead\", \"tungsten\", \"silver\").forEach { material ->\n            materialRecipeGenerator.register(\n                \"${material}_ore\",\n                pulverizeOre(\"c:${material}_ores\", \"indrev:${material}_dust\")\n            )\n            materialRecipeGenerator.register(\n                \"${material}_ingot\",\n                pulverizeIngot(\"c:${material}_ingots\", \"indrev:${material}_dust\")\n            )\n            arrayOf(\"ore\", \"plate\", \"dust\", \"ingot\").forEach { suffix ->\n                materialTagGenerator.register(\"${material}_$suffix\", createTag(\"indrev:${material}_$suffix\"))\n            }\n        }\n\n        arrayOf(\"iron\", \"gold\").forEach { material ->\n            materialRecipeGenerator.register(\n                \"${material}_ore\",\n                pulverizeOre(\"c:${material}_ores\", \"indrev:${material}_dust\")\n            )\n            materialRecipeGenerator.register(\n                \"${material}_ingot\",\n                pulverizeIngot(\"c:${material}_ingots\", \"indrev:${material}_dust\")\n            )\n            materialTagGenerator.register(\"${material}_ore\", createTag(\"minecraft:${material}_ore\"))\n        }\n        arrayOf(\"diamond\", \"coal\").forEach { material ->\n            materialRecipeGenerator.register(\n                \"${material}_ore\",\n                pulverizeOre(\"c:${material}_ores\", \"minecraft:${material}\")\n            )\n            materialRecipeGenerator.register(\n                material,\n                pulverizeIngot(\"minecraft:$material\", \"indrev:${material}_dust\", fileSuffix = \"dust\")\n            )\n            materialTagGenerator.register(\"${material}_dust\", createTag(\"indrev:${material}_dust\"))\n            materialTagGenerator.register(\"${material}_ore\", createTag(\"minecraft:${material}_ore\"))\n        }\n\n        DyeColor.values().forEach {\n            val name = it.getName()\n            materialRecipeGenerator.register(\"harden_${name}_concrete_powder\", hardenConcretePowder(name))\n        }\n\n        MachineRegistry.MAP.values.distinct().forEach { registry ->\n            if (registry.upgradeable) {\n                arrayOf(Tier.MK1, Tier.MK2, Tier.MK3).forEach { tier ->\n                    materialRecipeGenerator.register(\"${Registry.ITEM.getId(registry.block(Tier.MK1).asItem()).toString().replace(\"_mk1\", \"\")}_${tier.id}_to_${tier.next().id}\", upgradeMachineRecipe(registry, tier, tier.next()))\n                }\n            }\n        }\n\n        itemModelGenerator.register(IRItemRegistry.GAMER_AXE_ITEM, JsonFactory.nullFactory())\n        itemModelGenerator.register(IRBlockRegistry.DRILL_BOTTOM.asItem(), JsonFactory.nullFactory())\n\n        Registry.BLOCK.forEach { block ->\n            val id = Registry.BLOCK.getId(block)\n            if (id.namespace == namespace && id.path.contains(\"ore\") && !id.path.contains(\"purified\"))\n                lootTableGenerator.register(block, LootTableGenerator.ORE_DROP(block))\n        }\n\n        MetalSpriteRegistry.MATERIAL_PROVIDERS.forEach { (id, model) ->\n            val itemId = Identifier(id.namespace, id.path)\n            val item = Registry.ITEM.get(itemId)\n            if (item != Items.AIR) {\n                if (item is BlockItem) {\n                    blockModelGenerator.register(item.block, BlockModelGenerator.CUBE_ALL(item.block))\n                }\n                if (id.toString().contains(\"sword\"))\n                    metalSpriteGenerator.register(id, ImageFactory.simpleFactory0<Identifier>()(id))\n                else\n                    metalSpriteGenerator.register(id, ImageFactory.simpleFactory<Identifier>()(id))\n                val factory =\n                    if (model.type == MetalModel.TransformationType.HANDHELD) ItemModelGenerator.HANDHELD\n                    else ItemModelGenerator.DEFAULT_ITEM\n                itemModelGenerator.register(item, factory(item))\n            }\n        }\n    }\n    \n    fun generate() {\n        val lootTablesGenerated = lootTableGenerator.generate()\n        IndustrialRevolution.LOGGER.info(\"Generated $lootTablesGenerated loot tables.\")\n        val itemModelsGenerated = itemModelGenerator.generate()\n        IndustrialRevolution.LOGGER.info(\"Generated $itemModelsGenerated item models.\")\n        val blockModelsGenerated = blockModelGenerator.generate()\n        IndustrialRevolution.LOGGER.info(\"Generated $blockModelsGenerated block models.\")\n        val recipesGenerated = materialRecipeGenerator.generate()\n        IndustrialRevolution.LOGGER.info(\"Generated $recipesGenerated recipes.\")\n        val tagsGenerated = materialTagGenerator.generate()\n        IndustrialRevolution.LOGGER.info(\"Generated $tagsGenerated tags.\")\n        val spritesGenerated = metalSpriteGenerator.generate()\n        IndustrialRevolution.LOGGER.info(\"Generated $spritesGenerated sprites.\")\n\n        IndustrialRevolution.LOGGER.info(\"Generated ${lootTablesGenerated + itemModelsGenerated + blockModelsGenerated + recipesGenerated + tagsGenerated + spritesGenerated } files in total.\")\n        exitProcess(0)\n    }\n\n    operator fun Int.plus(boolean: Boolean): Int {\n        return if (boolean) this + 1 else this\n    }\n\n    private fun pulverizeTagged(inputId: String, outputId: String, count: Int, time: Int, fileSuffix: String) : JsonFactory<String> {\n        return object : JsonFactory<String> {\n            override fun generate(): JsonObject {\n                val json = JsonObject()\n                json.addProperty(\"type\", \"indrev:pulverize\")\n                val ingredients = JsonObject()\n                ingredients.addProperty(\"tag\", inputId)\n                json.add(\"ingredients\", ingredients)\n                val output = JsonObject()\n                output.addProperty(\"item\", outputId)\n                output.addProperty(\"count\", count)\n                json.add(\"output\", output)\n                json.addProperty(\"processTime\", time)\n                return json\n            }\n\n            override fun getFileName(t: String, id: Identifier): String {\n                var fileName = super.getFileName(t, id)\n                val index = fileName.indexOf(\"_\")\n                if (index != -1) fileName = fileName.substring(0, index)\n                return \"pulverizer/${fileName}_$fileSuffix\"\n            }\n        }\n    }\n\n    private fun pulverizeItem(inputId: String, outputId: String, count: Int, time: Int, fileSuffix: String) : JsonFactory<String> {\n        return object : JsonFactory<String> {\n            override fun generate(): JsonObject {\n                val json = JsonObject()\n                json.addProperty(\"type\", \"indrev:pulverize\")\n                val ingredients = JsonObject()\n                ingredients.addProperty(\"item\", inputId)\n                json.add(\"ingredients\", ingredients)\n                val output = JsonObject()\n                output.addProperty(\"item\", outputId)\n                output.addProperty(\"count\", count)\n                json.add(\"output\", output)\n                json.addProperty(\"processTime\", time)\n                return json\n            }\n\n            override fun getFileName(t: String, id: Identifier): String = \"pulverizer/${super.getFileName(t, id)}_$fileSuffix\"\n        }\n    }\n\n    private fun pulverizeOre(inputId: String, outputId: String, count: Int = 2, time: Int = 200, fileSuffix: String = \"dust_from_ore\") : JsonFactory<String> {\n        return pulverizeTagged(inputId, outputId, count, time, fileSuffix)\n    }\n\n    private fun pulverizeIngot(inputId: String, outputId: String, count: Int = 1, time: Int = 150, fileSuffix: String = \"dust_from_ingot\") : JsonFactory<String> {\n        return pulverizeTagged(inputId, outputId, count, time, fileSuffix)\n    }\n\n    private fun rawOreIntoBlock(ore: String): JsonFactory<String> {\n        return object : JsonFactory<String> {\n            override fun generate(): JsonObject {\n                val json = JsonObject()\n                json.addProperty(\"type\", \"crafting_shaped\")\n                val pattern = JsonArray()\n                repeat(3) { pattern.add(\"###\") }\n                json.add(\"pattern\", pattern)\n                val key = JsonObject()\n                key.add(\"#\", JsonObject().also { it.addProperty(\"item\", \"indrev:raw_${ore}\") })\n                json.add(\"key\", key)\n                val result = JsonObject()\n                result.addProperty(\"item\", \"indrev:raw_${ore}_block\")\n                json.add(\"result\", result)\n                return json\n            }\n\n            override fun getFileName(t: String, id: Identifier): String = \"shaped/raw_${ore}_block\"\n        }\n    }\n\n    private fun rawOreBlockIntoRawItem(ore: String): JsonFactory<String> {\n        return object : JsonFactory<String> {\n            override fun generate(): JsonObject {\n                val json = JsonObject()\n                json.addProperty(\"type\", \"crafting_shapeless\")\n                val ingredients = JsonObject()\n                ingredients.addProperty(\"item\", \"indrev:raw_${ore}_block\")\n                json.add(\"ingredients\", ingredients)\n                val output = JsonObject()\n                output.addProperty(\"item\", \"indrev:raw_${ore}\")\n                output.addProperty(\"count\", 9)\n                json.add(\"result\", output)\n                return json\n            }\n\n            override fun getFileName(t: String, id: Identifier): String = \"shapeless/raw_${ore}\"\n        }\n    }\n    private fun createTag(item: String): JsonFactory<String> {\n        return object : JsonFactory<String> {\n            override fun generate(): JsonObject {\n                val obj = JsonObject()\n                obj.addProperty(\"replace\", false)\n                val values = JsonArray()\n                values.add(item)\n                obj.add(\"values\", values)\n                return obj\n            }\n\n            override fun getFileName(t: String, id: Identifier): String = \"${super.getFileName(t, id)}s\"\n        }\n    }\n\n    private fun hardenConcretePowder(color: String): JsonFactory<String> {\n        return object : JsonFactory<String> {\n            override fun generate(): JsonObject {\n                val json = JsonObject()\n                json.addProperty(\"type\", \"indrev:fluid_infuse\")\n                val ingredients = JsonObject()\n                ingredients.addProperty(\"item\", \"minecraft:${color}_concrete_powder\")\n                json.add(\"ingredients\", ingredients)\n\n                val fluidInput = JsonObject()\n                fluidInput.addProperty(\"fluid\", \"minecraft:water\")\n                fluidInput.addProperty(\"type\", \"mb\")\n                fluidInput.addProperty(\"count\", 100)\n                json.add(\"fluidInput\", fluidInput)\n                \n                val output = JsonObject()\n                output.addProperty(\"item\", \"minecraft:${color}_concrete\")\n                output.addProperty(\"count\", 1)\n                json.add(\"output\", output)\n\n                json.addProperty(\"processTime\", 200)\n                return json\n            }\n\n            override fun getFileName(t: String, id: Identifier): String = \"fluid_infusing/harden_${color}_concrete_powder\"\n        }\n    }\n\n    private fun upgradeMachineRecipe(registry: MachineRegistry, from: Tier, to: Tier) : JsonFactory<String> {\n        return object : JsonFactory<String> {\n            override fun generate(): JsonObject {\n                val json = JsonObject()\n                json.addProperty(\"type\", \"crafting_shapeless\")\n                val ingredients = JsonArray()\n                ingredients.add(JsonObject().also { it.addProperty(\"item\", Registry.ITEM.getId(registry.block(from).asItem()).toString()) })\n                ingredients.add(JsonObject().also { it.addProperty(\"item\", Registry.ITEM.getId(IRMachineUpgradeItem.fromTier(from)).toString()) })\n                json.add(\"ingredients\", ingredients)\n                val output = JsonObject()\n                output.addProperty(\"item\", Registry.ITEM.getId(registry.block(to).asItem()).toString())\n                json.add(\"result\", output)\n                return json\n            }\n\n            override fun getFileName(t: String, id: Identifier): String = \"upgrade/${super.getFileName(t, id)}\"\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/ImageFactory.kt",
    "content": "package me.steven.indrev.datagen\n\nimport me.steven.indrev.datagen.utils.MetalModel\nimport me.steven.indrev.datagen.utils.MetalSpriteRegistry\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.util.ModelIdentifier\nimport net.minecraft.util.Identifier\nimport java.awt.image.BufferedImage\nimport java.io.File\nimport javax.imageio.ImageIO\n\ninterface ImageFactory<T> : DataFactory<T, BufferedImage> {\n\n    override val extension: String get() = \"png\"\n\n    override fun write(file: File, t: BufferedImage) {\n        ImageIO.write(t, \"png\", file)\n    }\n\n    companion object {\n\n        fun <T> simpleFactory(): (ModelIdentifier) -> ImageFactory<T> = { string ->\n            object : ImageFactory<T> {\n                override fun generate(): BufferedImage {\n                    val img = BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB)\n                    val baseGraphics = img.createGraphics()\n                    val model = MetalSpriteRegistry.MATERIAL_PROVIDERS[string]\n                    (model as MetalModel).holders.forEach { modelWithColor ->\n                        val type = if (model.type == MetalModel.TransformationType.BLOCK) \"block\" else \"item\"\n                        val resourceId = Identifier(modelWithColor.id.namespace, \"textures/$type/${modelWithColor.id.path}.png\")\n                        val inputStream = MinecraftClient.getInstance().resourceManager.getResource(resourceId).inputStream\n                        val overlay = ImageIO.read(inputStream)\n                        for (x in 0 until 16) {\n                            for (y in 0 until 16) {\n                                val overlayHex = overlay.getRGB(x, y)\n                                val a = ((overlayHex shr 24) and 255).toFloat() / 255f\n                                val r = ((overlayHex shr 16) and 255).toFloat() / 255f\n                                val g = ((overlayHex shr 8) and 255).toFloat() / 255f\n                                val b = ((overlayHex shr 0) and 255).toFloat() / 255f\n\n                                val hex = modelWithColor.color\n                                val oA = ((hex shr 24) and 255).toFloat() / 255f\n                                val oR = ((hex shr 16) and 255).toFloat() / 255f\n                                val oG = ((hex shr 8) and 255).toFloat() / 255f\n                                val oB = ((hex shr 0) and 255).toFloat() / 255f\n\n                                val nR = mix(r, r * oR, oA)\n                                val nG = mix(g, g * oG, oA)\n                                val nB = mix(b, b * oB, oA)\n\n                                val nHex = toHex(a, nR, nG, nB)\n\n                                overlay.setRGB(x, y, nHex)\n                            }\n                        }\n                        baseGraphics.drawImage(overlay, 0, 0, null)\n                    }\n                    baseGraphics.dispose()\n                    return img\n                }\n            }\n        }\n\n        fun <T> simpleFactory0(): (ModelIdentifier) -> ImageFactory<T> = { string ->\n            object : ImageFactory<T> {\n                override fun generate(): BufferedImage {\n                    val img = BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB)\n                    val baseGraphics = img.createGraphics()\n                    val model = MetalSpriteRegistry.MATERIAL_PROVIDERS[string]\n                    (model as MetalModel).holders.forEach { modelWithColor ->\n                        val type = if (model.type == MetalModel.TransformationType.BLOCK) \"block\" else \"item\"\n                        val resourceId = Identifier(modelWithColor.id.namespace, \"textures/$type/${modelWithColor.id.path}.png\")\n                        val inputStream = MinecraftClient.getInstance().resourceManager.getResource(resourceId).inputStream\n                        val overlay = ImageIO.read(inputStream)\n                        for (x in 0 until 16) {\n                            for (y in 0 until 16) {\n                                val overlayHex = overlay.getRGB(x, y)\n                                val a = ((overlayHex shr 24) and 255).toFloat() / 255f\n                                val r = ((overlayHex shr 16) and 255).toFloat() / 255f\n                                val g = ((overlayHex shr 8) and 255).toFloat() / 255f\n                                val b = ((overlayHex shr 0) and 255).toFloat() / 255f\n\n                                val hex = modelWithColor.color\n                                val oA = ((hex shr 24) and 255).toFloat() / 255f\n                                val oR = ((hex shr 16) and 255).toFloat() / 255f\n                                val oG = ((hex shr 8) and 255).toFloat() / 255f\n                                val oB = ((hex shr 0) and 255).toFloat() / 255f\n\n                                val nR = mix(r, r * oR, oA)\n                                val nG = mix(g, g * oG, oA)\n                                val nB = mix(b, b * oB, oA)\n\n                                val nHex = toHex(a, nR, nG, nB)\n\n                                overlay.setRGB(x, y, nHex)\n                            }\n                        }\n                        if (modelWithColor.id.path == \"tool_stick\")\n                            baseGraphics.drawImage(overlay, 0, -1, null)\n                        else\n                            baseGraphics.drawImage(overlay, 0, 0, null)\n                    }\n                    baseGraphics.dispose()\n                    return img\n                }\n            }\n        }\n\n        private fun mix(x: Float, y: Float, a: Float): Float = x + (y - x) * a\n\n        private fun toHex(a: Float, r: Float, g: Float, b: Float): Int =\n            (a * 255).toInt() and 0xFF shl 24 or\n                    ((r * 255).toInt() and 0xFF shl 16) or\n                    ((g * 255).toInt() shl 8) or\n                    ((b * 255).toInt() shl 0)\n\n        fun <T> nullFactory(): ImageFactory<T> = object : ImageFactory<T> {\n            override fun generate(): BufferedImage? = null\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/IndustrialRevolutionDatagen.kt",
    "content": "package me.steven.indrev.datagen\n\nimport me.steven.indrev.IndustrialRevolution\nimport net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint\nimport net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator\nimport net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider\nimport net.minecraft.block.Material\nimport net.minecraft.tag.BlockTags\nimport net.minecraft.util.registry.Registry\n\nclass IndustrialRevolutionDatagen : DataGeneratorEntrypoint {\n    override fun onInitializeDataGenerator(datagen: FabricDataGenerator) {\n        datagen.addProvider(IndustrialRevolutionDatagen::IRBlockTagProvider)\n    }\n\n    class IRBlockTagProvider(datagen: FabricDataGenerator) : FabricTagProvider.BlockTagProvider(datagen) {\n        override fun generateTags() {\n            Registry.BLOCK.ids.forEach { id ->\n                if (id.namespace == IndustrialRevolution.MOD_ID) {\n                    val block = Registry.BLOCK.get(id)\n                    if (block.defaultState.material == Material.METAL || block.defaultState.material == Material.STONE || block.defaultState.material == Material.GLASS) {\n                        getOrCreateTagBuilder(BlockTags.PICKAXE_MINEABLE).add(block)\n                    }\n                    if (block.defaultState.material == Material.WOOD) {\n                        getOrCreateTagBuilder(BlockTags.AXE_MINEABLE).add(block)\n                    }\n                }\n            }\n        }\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/JsonFactory.kt",
    "content": "package me.steven.indrev.datagen\n\nimport com.google.gson.GsonBuilder\nimport com.google.gson.JsonObject\nimport java.io.File\n\ninterface JsonFactory<T> : DataFactory<T, JsonObject?> {\n\n    override val extension: String get() = \"json\"\n\n    override fun write(file: File, t: JsonObject?) {\n        file.writeText(GsonBuilder().setPrettyPrinting().create().toJson(t))\n    }\n\n    companion object {\n        fun <T> nullFactory(): JsonFactory<T> = object : JsonFactory<T> {\n            override fun generate(): JsonObject? = null\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/generators/BlockModelGenerator.kt",
    "content": "package me.steven.indrev.datagen.generators\n\nimport com.google.gson.JsonObject\nimport me.steven.indrev.datagen.DataGenerator\nimport me.steven.indrev.datagen.JsonFactory\nimport net.minecraft.block.Block\nimport net.minecraft.util.registry.Registry\nimport java.io.File\n\nclass BlockModelGenerator(val root: File, namespace: String, fallback: (Block) -> JsonFactory<Block>)\n    : DataGenerator<Block, JsonObject?>(File(root, \"models/block\"), namespace, fallback) {\n\n    override fun generate(): Int {\n        var count = 0\n        generators.forEach { (block, _) ->\n            if (generate(Registry.BLOCK.getId(block), block))\n                count++\n        }\n        return count\n    }\n\n    companion object {\n        val CUBE_ALL: (Block) -> JsonFactory<Block> = { item ->\n            object : JsonFactory<Block> {\n                override fun generate(): JsonObject {\n                    val id = Registry.BLOCK.getId(item)\n                    val obj = JsonObject()\n                    obj.addProperty(\"parent\", \"block/cube_all\")\n                    val texturesObj = JsonObject()\n                    texturesObj.addProperty(\"all\", \"${id.namespace}:block/${id.path}\")\n                    obj.add(\"textures\", texturesObj)\n                    return obj\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/generators/ItemModelGenerator.kt",
    "content": "package me.steven.indrev.datagen.generators\n\nimport com.google.gson.JsonObject\nimport me.steven.indrev.datagen.DataGenerator\nimport me.steven.indrev.datagen.JsonFactory\nimport net.minecraft.item.BlockItem\nimport net.minecraft.item.Item\nimport net.minecraft.util.registry.Registry\nimport java.io.File\n\nclass ItemModelGenerator(val root: File, namespace: String, fallback: (Item) -> JsonFactory<Item>)\n    : DataGenerator<Item, JsonObject?>(File(root, \"models/item\"), namespace, fallback) {\n\n    override fun generate(): Int {\n        var count = 0\n        Registry.ITEM.ids.filter { id -> id.namespace == namespace }.forEach {\n            val item = Registry.ITEM.get(it)\n            if (item.asItem() != null && generate(it, item)) {\n                count++\n            }\n        }\n        return count\n    }\n\n    companion object {\n        val DEFAULT_ITEM: (Item) -> JsonFactory<Item> = { item ->\n            object : JsonFactory<Item> {\n                override fun generate(): JsonObject {\n                    val id = Registry.ITEM.getId(item)\n                    val obj = JsonObject()\n                    if (item is BlockItem) {\n                        obj.addProperty(\"parent\", \"${id.namespace}:block/${id.path}\")\n                    } else {\n                        obj.addProperty(\"parent\", \"item/generated\")\n                        val texturesObj = JsonObject()\n                        texturesObj.addProperty(\"layer0\", \"${id.namespace}:item/${id.path}\")\n                        obj.add(\"textures\", texturesObj)\n                    }\n                    return obj\n                }\n\n            }\n        }\n        val HANDHELD: (Item) -> JsonFactory<Item> = { item ->\n            object : JsonFactory<Item> {\n                override fun generate(): JsonObject {\n                    val id = Registry.ITEM.getId(item)\n                    val obj = JsonObject()\n                    obj.addProperty(\"parent\", \"item/handheld\")\n                    val texturesObj = JsonObject()\n                    texturesObj.addProperty(\"layer0\", \"${id.namespace}:item/${id.path}\")\n                    obj.add(\"textures\", texturesObj)\n                    return obj\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/generators/LootTableGenerator.kt",
    "content": "package me.steven.indrev.datagen.generators\n\nimport com.google.gson.JsonObject\nimport me.steven.indrev.datagen.DataGenerator\nimport me.steven.indrev.datagen.JsonFactory\nimport net.minecraft.block.Block\nimport net.minecraft.enchantment.Enchantments\nimport net.minecraft.item.Items\nimport net.minecraft.loot.LootManager\nimport net.minecraft.loot.LootPool\nimport net.minecraft.loot.LootTable\nimport net.minecraft.loot.condition.MatchToolLootCondition\nimport net.minecraft.loot.condition.SurvivesExplosionLootCondition\nimport net.minecraft.loot.context.LootContextTypes\nimport net.minecraft.loot.entry.AlternativeEntry\nimport net.minecraft.loot.entry.ItemEntry\nimport net.minecraft.loot.function.ApplyBonusLootFunction\nimport net.minecraft.loot.function.ExplosionDecayLootFunction\nimport net.minecraft.loot.provider.number.ConstantLootNumberProvider\nimport net.minecraft.predicate.NumberRange\nimport net.minecraft.predicate.item.EnchantmentPredicate\nimport net.minecraft.predicate.item.ItemPredicate\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.registry.Registry\nimport java.io.File\n\nclass LootTableGenerator(val root: File, namespace: String, fallback: (Block) -> JsonFactory<Block>)\n    : DataGenerator<Block, JsonObject?>(File(root, \"loot_tables/blocks\"), namespace, fallback) {\n\n    override fun generate(): Int {\n        var count = 0\n        Registry.BLOCK.ids.filter { id -> id.namespace == namespace }.forEach {\n            val block = Registry.BLOCK.get(it)\n            if (block.asItem() != null && block.asItem() != Items.AIR && generate(it, block)) {\n                count++\n            }\n        }\n        return count\n    }\n\n    companion object {\n        val SELF_DROP: (Block) -> JsonFactory<Block> = { block ->\n            object : JsonFactory<Block> {\n                override fun generate(): JsonObject? {\n                    val lootTable = LootTable.builder()\n                        .pool(\n                            LootPool.builder().rolls(ConstantLootNumberProvider.create(1f)).with(ItemEntry.builder(block))\n                                .conditionally(SurvivesExplosionLootCondition.builder())\n                        )\n                        .type(LootContextTypes.BLOCK)\n                        .build()\n                    return LootManager.toJson(lootTable).asJsonObject\n                }\n\n            }\n        }\n\n        val ORE_DROP: (Block) -> JsonFactory<Block> = { block ->\n            object : JsonFactory<Block> {\n                override fun generate(): JsonObject? {\n                    val blockId = Registry.BLOCK.getId(block)\n                    val rawOreId = \"raw_${blockId.path.replace(\"deepslate_\", \"\").replace(\"_ore\", \"\")}\"\n                    val rawOre = Registry.ITEM.get(Identifier(blockId.namespace, rawOreId))\n                    val lootTable = LootTable.builder()\n                        .pool(\n                            LootPool.builder()\n                                .bonusRolls(ConstantLootNumberProvider.create(0f))\n                                .rolls(ConstantLootNumberProvider.create(1f))\n                                .with(\n                                    AlternativeEntry.builder(\n                                        ItemEntry.builder(block)\n                                            .conditionally(\n                                                MatchToolLootCondition.builder(\n                                                    ItemPredicate.Builder.create().enchantment(\n                                                        EnchantmentPredicate(\n                                                            Enchantments.SILK_TOUCH,\n                                                            NumberRange.IntRange.atLeast(1)\n                                                        )\n                                                    )\n                                                )\n                                            ),\n                                        ItemEntry.builder(rawOre)\n                                            .apply(ApplyBonusLootFunction.oreDrops(Enchantments.FORTUNE))\n                                            .apply(ExplosionDecayLootFunction.builder())\n                                    )\n                                )\n                        )\n\n\n                        .type(LootContextTypes.BLOCK)\n                        .build()\n                    return LootManager.toJson(lootTable).asJsonObject\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/generators/MaterialRecipeGenerator.kt",
    "content": "package me.steven.indrev.datagen.generators\n\nimport com.google.gson.JsonObject\nimport me.steven.indrev.datagen.DataGenerator\nimport me.steven.indrev.datagen.JsonFactory\nimport net.minecraft.util.Identifier\nimport java.io.File\n\nclass MaterialRecipeGenerator(\n    val root: File,\n    namespace: String,\n    fallback: (String) -> JsonFactory<String>\n) : DataGenerator<String, JsonObject?>(File(root, \"recipes\"), namespace, fallback) {\n\n    fun register(id: Identifier, factory: JsonFactory<String>) {\n        generators[id.toString()] = factory\n    }\n\n    override fun generate(): Int {\n        var count = 0\n        generators.forEach { (tag, _) ->\n            if (generate(Identifier(tag), tag))\n                count++\n        }\n        return count\n    }\n\n    companion object\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/generators/MaterialTagGenerator.kt",
    "content": "package me.steven.indrev.datagen.generators\n\nimport com.google.gson.JsonArray\nimport com.google.gson.JsonObject\nimport me.steven.indrev.datagen.DataGenerator\nimport me.steven.indrev.datagen.JsonFactory\nimport net.minecraft.util.Identifier\nimport java.io.File\n\nclass MaterialTagGenerator(val root: File, namespace: String, fallback: (String) -> JsonFactory<String>)\n    : DataGenerator<String, JsonObject?>(File(root, \"tags/items\"), namespace, fallback) {\n\n    override fun generate(): Int {\n        var count = 0\n        generators.forEach { (id, _) ->\n            if (generate(Identifier(id), id))\n                count++\n        }\n        return count\n    }\n\n    companion object {\n        val DEFAULT_ITEM: (String) -> JsonFactory<String> = { tag ->\n            object : JsonFactory<String> {\n                override fun generate(): JsonObject {\n                    val obj = JsonObject()\n                    obj.addProperty(\"replace\", false)\n                    val values = JsonArray()\n                    values.add(tag)\n                    obj.add(\"values\", values)\n                    return obj\n                }\n\n                override fun getFileName(t: String, id: Identifier): String = \"${super.getFileName(t, id)}s\"\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/generators/MetalSpriteGenerator.kt",
    "content": "package me.steven.indrev.datagen.generators\n\nimport me.steven.indrev.datagen.DataGenerator\nimport me.steven.indrev.datagen.ImageFactory\nimport net.minecraft.util.Identifier\nimport java.awt.image.BufferedImage\nimport java.io.File\n\nclass MetalSpriteGenerator(\n    val root: File,\n    namespace: String,\n    fallback: (Identifier) -> ImageFactory<Identifier>\n) : DataGenerator<Identifier, BufferedImage>(File(root, \"textures\"), namespace, fallback) {\n    override fun generate(): Int {\n        var count = 0\n        generators.forEach { (tag, _) ->\n            if (generate(tag, tag))\n                count++\n        }\n        return count\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/utils/MaterialColors.kt",
    "content": "package me.steven.indrev.datagen.utils\n\nconst val LEAD_BASE = 0xFF575d61\nconst val LEAD_HIGHLIGHT = 0xFF77859a\n//const val LEAD_SHADOW = 0xFF343539\n\nconst val SILVER_BASE = 0xFFc6e9e3\nconst val SILVER_HIGHLIGHT = 0xFFd9f2e3\n//const val SILVER_SHADOW = 0xFF989890\n\nconst val COPPER_BASE = 0xFFe77c56\nconst val COPPER_HIGHLIGHT = 0xFFfc9982\n//const val COPPER_SHADOW = 0xFFd1613f\n\nconst val TUNGSTEN_BASE = 0xFF686b5b\nconst val TUNGSTEN_HIGHLIGHT = 0xFF838961\n//const val TUNGSTEN_SHADOW = 0xFF58564a\n\nconst val BRONZE_BASE = 0xFF914e3b\nconst val BRONZE_HIGHLIGHT = 0xFFa86234\n//const val BRONZE_SHADOW = 0xFF815735\n\nconst val ELECTRUM_BASE = 0xFFecd36f\nconst val ELECTRUM_HIGHLIGHT = 0xFFf5e875\n//const val ELECTRUM_SHADOW = 0xFFc17a3d\n\nconst val STEEL_BASE = 0xFF3b3b3b\nconst val STEEL_HIGHLIGHT = 0xFF444341\n\nconst val NIKOLITE_BASE = 0xFF008f8c\nconst val NIKOLITE_HIGHLIGHT = 0xFF00ad88\n\nconst val ENRICHED_NIKOLITE_BASE = 0xFF5300de\nconst val ENRICHED_NIKOLITE_HIGHLIGHT = 0xFF3740d4\n\nconst val IRON_BASE = 0xFFfdfdfd\nconst val IRON_HIGHLIGHT = 0xFFffffff\n\nconst val TIN_BASE = 0xFFc9c9c9\nconst val TIN_HIGHLIGHT = 0xFFc1c7c7\nconst val TIN_OUTLINE = 0xFF9ca1aa\n\nconst val DIAMOND_BASE = 0xFF4aedd9\nconst val DIAMOND_HIGHLIGHT = 0xFFa1fbe8\nconst val DIAMOND_OUTLINE = 0xFF11727a\n\nconst val SULFUR_BASE = 0xFFf5da68\nconst val SULFUR_HIGHLIGHT = 0xFFffffff\nconst val SULFUR_OUTLINE = 0xFFbda547\n\nconst val COAL_BASE = 0xFF1f1721\nconst val COAL_HIGHLIGHT = 0xFF261e24\n\nconst val NETHERITE_SCRAP_BASE = 0xFF603a32\nconst val NETHERITE_SCRAP_HIGHLIGHT = 0xFF7e6059\nconst val NETHERITE_SCRAP_OUTLINE = 0xFF24110b\n\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/utils/MetalModel.kt",
    "content": "package me.steven.indrev.datagen.utils\n\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.util.Identifier\n\nclass MetalModel private constructor(val holders: Array<SpriteColorHolder>, val type: TransformationType) {\n\n    class Builder {\n\n        private val holders: MutableList<SpriteColorHolder> = mutableListOf()\n        private var transformationType: TransformationType = TransformationType.AUTO\n\n        fun with(id: Identifier, color: Long): Builder {\n            holders.add(SpriteColorHolder(id, color.toInt()))\n            return this\n        }\n\n        fun ingotBase(color: Long): Builder {\n            return with(identifier(\"ingot_base\"), color)\n        }\n\n        fun ingotOutline(color: Long): Builder {\n            return with(identifier(\"ingot_outline\"), color)\n        }\n\n        fun ingotHighlight(color: Long): Builder {\n            return with(identifier(\"ingot_highlight\"), color)\n        }\n\n        fun plateBase(color: Long): Builder {\n            return with(identifier(\"plate_base\"), color)\n        }\n\n        fun plateOutline(color: Long): Builder {\n            return with(identifier(\"plate_outline\"), color)\n        }\n\n        fun plateHighlight(color: Long): Builder {\n            return with(identifier(\"plate_highlight\"), color)\n        }\n\n        fun pickaxeBase(color: Long): Builder {\n            return with(identifier(\"pickaxe_base\"), color)\n        }\n\n        fun pickaxeOutline(color: Long): Builder {\n            return with(identifier(\"pickaxe_outline\"), color)\n        }\n\n        fun pickaxeHighlight(color: Long): Builder {\n            return with(identifier(\"pickaxe_highlight\"), color)\n        }\n\n        fun axeBase(color: Long): Builder {\n            return with(identifier(\"axe_base\"), color)\n        }\n\n        fun axeOutline(color: Long): Builder {\n            return with(identifier(\"axe_outline\"), color)\n        }\n\n        fun axeHighlight(color: Long): Builder {\n            return with(identifier(\"axe_highlight\"), color)\n        }\n\n        fun helmetBase(color: Long): Builder {\n            return with(identifier(\"helmet_base\"), color)\n        }\n\n        fun helmetOutline(color: Long): Builder {\n            return with(identifier(\"helmet_outline\"), color)\n        }\n\n        fun helmetHighlight(color: Long): Builder {\n            return with(identifier(\"helmet_highlight\"), color)\n        }\n\n        fun dustBase(color: Long): Builder {\n            return with(identifier(\"dust_base\"), color)\n        }\n\n        fun dustOutline(color: Long): Builder {\n            return with(identifier(\"dust_outline\"), color)\n        }\n\n        fun dustHighlight(color: Long): Builder {\n            return with(identifier(\"dust_highlight\"), color)\n        }\n\n        fun chestplateBase(color: Long): Builder {\n            return with(identifier(\"chestplate_base\"), color)\n        }\n\n        fun chestplateOutline(color: Long): Builder {\n            return with(identifier(\"chestplate_outline\"), color)\n        }\n\n        fun chestplateHighlight(color: Long): Builder {\n            return with(identifier(\"chestplate_highlight\"), color)\n        }\n\n        fun bootsBase(color: Long): Builder {\n            return with(identifier(\"boots_base\"), color)\n        }\n\n        fun bootsOutline(color: Long): Builder {\n            return with(identifier(\"boots_outline\"), color)\n        }\n\n        fun bootsHighlight(color: Long): Builder {\n            return with(identifier(\"boots_highlight\"), color)\n        }\n\n        fun hoeBase(color: Long): Builder {\n            return with(identifier(\"hoe_base\"), color)\n        }\n\n        fun hoeOutline(color: Long): Builder {\n            return with(identifier(\"hoe_outline\"), color)\n        }\n\n        fun hoeHighlight(color: Long): Builder {\n            return with(identifier(\"hoe_highlight\"), color)\n        }\n\n        fun leggingsBase(color: Long): Builder {\n            return with(identifier(\"leggings_base\"), color)\n        }\n\n        fun leggingsOutline(color: Long): Builder {\n            return with(identifier(\"leggings_outline\"), color)\n        }\n\n        fun leggingsHighlight(color: Long): Builder {\n            return with(identifier(\"leggings_highlight\"), color)\n        }\n\n        fun nuggetBase(color: Long): Builder {\n            return with(identifier(\"nugget_base\"), color)\n        }\n\n        fun nuggetOutline(color: Long): Builder {\n            return with(identifier(\"nugget_outline\"), color)\n        }\n\n        fun nuggetHighlight(color: Long): Builder {\n            return with(identifier(\"nugget_highlight\"), color)\n        }\n\n        fun oreBase(color: Long): Builder {\n            return with(identifier(\"ore_base\"), color)\n        }\n\n        fun oreHighlight(color: Long): Builder {\n            return with(identifier(\"ore_highlight\"), color)\n        }\n\n        fun blockBase(color: Long): Builder {\n            return with(identifier(\"block_base\"), color)\n        }\n\n        fun blockOutline(color: Long): Builder {\n            return with(identifier(\"block_outline\"), color)\n        }\n\n        fun blockHighlight(color: Long): Builder {\n            return with(identifier(\"block_highlight\"), color)\n        }\n\n        fun purifiedOreBase(color: Long): Builder {\n            return with(identifier(\"purified_ore_base\"), color)\n        }\n\n        fun purifiedOreOutline(color: Long): Builder {\n            return with(identifier(\"purified_ore_outline\"), color)\n        }\n\n        fun purifiedOreHighlight(color: Long): Builder {\n            return with(identifier(\"purified_ore_highlight\"), color)\n        }\n\n        fun chunkBase(color: Long): Builder {\n            return with(identifier(\"chunk_base\"), color)\n        }\n\n        fun chunkOutline(color: Long): Builder {\n            return with(identifier(\"chunk_outline\"), color)\n        }\n\n        fun chunkHighlight(color: Long): Builder {\n            return with(identifier(\"chunk_highlight\"), color)\n        }\n\n        fun shovelBase(color: Long): Builder {\n            return with(identifier(\"shovel_base\"), color)\n        }\n\n        fun shovelOutline(color: Long): Builder {\n            return with(identifier(\"shovel_outline\"), color)\n        }\n\n        fun shovelHighlight(color: Long): Builder {\n            return with(identifier(\"shovel_highlight\"), color)\n        }\n\n        fun swordBase(color: Long): Builder {\n            return with(identifier(\"sword_base\"), color)\n        }\n\n        fun swordOutline(color: Long): Builder {\n            return with(identifier(\"sword_outline\"), color)\n        }\n\n        fun swordHighlight(color: Long): Builder {\n            return with(identifier(\"sword_highlight\"), color)\n        }\n\n        fun moltenBucketBase(color: Long): Builder {\n            return with(identifier(\"molten_bucket_base\"), color)\n        }\n\n        fun moltenBucketHighlight(color: Long): Builder {\n            return with(identifier(\"molten_bucket_highlight\"), color)\n        }\n\n        fun rawOre(color: Long): Builder {\n            return with(identifier(\"raw_ore\"), color)\n        }\n\n        fun rawOreBlock(color: Long): Builder {\n            transformationType = TransformationType.BLOCK\n            return with(identifier(\"raw_ore_block\"), color)\n        }\n\n        fun moltenBucketOutline(color: Long): Builder {\n            return with(identifier(\"molten_bucket_outline\"), color)\n        }\n\n        fun toolStick(): Builder {\n            transformationType = TransformationType.HANDHELD\n            return with(identifier(\"tool_stick\"), -1)\n        }\n\n        fun bucket(): Builder {\n            transformationType = TransformationType.HANDHELD\n            return with(Identifier(\"bucket\"), -1)\n        }\n\n        fun ore(): Builder {\n            transformationType = TransformationType.BLOCK\n            return with(Identifier(\"stone\"), -1)\n        }\n\n        fun deepslateOre(): Builder {\n            transformationType = TransformationType.BLOCK\n            return with(Identifier(\"deepslate\"), -1)\n        }\n\n        fun block(): Builder {\n            transformationType = TransformationType.BLOCK\n            return this\n        }\n\n        fun build() = MetalModel(holders.toTypedArray(), transformationType)\n    }\n\n    enum class TransformationType {\n        AUTO,\n        HANDHELD,\n        BLOCK;\n\n        val variant = if (this.toString() == \"BLOCK\") \"\" else \"inventory\"\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/utils/MetalSpriteRegistry.kt",
    "content": "package me.steven.indrev.datagen.utils\n\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.util.ModelIdentifier\n\nobject MetalSpriteRegistry {\n        val MATERIAL_PROVIDERS: HashMap<ModelIdentifier, MetalModel> = hashMapOf()\n\n        init {\n            put(\"lead_ingot\", MetalModel.Builder().ingotBase(LEAD_BASE).ingotHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_plate\", MetalModel.Builder().plateBase(LEAD_BASE).plateHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_pickaxe\", MetalModel.Builder().toolStick().pickaxeBase(LEAD_BASE).pickaxeHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_axe\", MetalModel.Builder().toolStick().axeBase(LEAD_BASE).axeHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_sword\", MetalModel.Builder().toolStick().swordBase(LEAD_BASE).swordHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_hoe\", MetalModel.Builder().toolStick().hoeBase(LEAD_BASE).hoeHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_shovel\", MetalModel.Builder().toolStick().shovelBase(LEAD_BASE).shovelHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_helmet\", MetalModel.Builder().helmetBase(LEAD_BASE).helmetHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_dust\", MetalModel.Builder().dustBase(LEAD_BASE).dustHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_chestplate\", MetalModel.Builder().chestplateBase(LEAD_BASE).chestplateHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_boots\", MetalModel.Builder().bootsBase(LEAD_BASE).bootsHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_leggings\", MetalModel.Builder().leggingsBase(LEAD_BASE).leggingsHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_nugget\", MetalModel.Builder().nuggetBase(LEAD_BASE).nuggetHighlight(LEAD_HIGHLIGHT).build())\n            putBlock(\"lead_ore\", MetalModel.Builder().ore().oreBase(LEAD_BASE).oreHighlight(LEAD_HIGHLIGHT).build())\n            putBlock(\"deepslate_lead_ore\", MetalModel.Builder().deepslateOre().oreBase(LEAD_BASE).oreHighlight(LEAD_HIGHLIGHT).build())\n            put(\"raw_lead\", MetalModel.Builder().rawOre(LEAD_BASE).build())\n            putBlock(\"raw_lead_block\", MetalModel.Builder().rawOreBlock(LEAD_BASE).build())\n            putBlock(\"lead_block\", MetalModel.Builder().block().blockBase(LEAD_BASE).build())\n            put(\"lead_purified_ore\", MetalModel.Builder().purifiedOreBase(LEAD_BASE).purifiedOreHighlight(LEAD_HIGHLIGHT).build())\n            put(\"lead_chunk\", MetalModel.Builder().chunkBase(LEAD_BASE).chunkHighlight(LEAD_HIGHLIGHT).build())\n            put(\"molten_lead_bucket\", MetalModel.Builder().bucket().moltenBucketBase(LEAD_BASE).moltenBucketHighlight(LEAD_HIGHLIGHT).build())\n\n\n            put(\"silver_ingot\", MetalModel.Builder().ingotBase(SILVER_BASE).ingotHighlight(SILVER_HIGHLIGHT).build())\n            put(\"silver_plate\", MetalModel.Builder().plateBase(SILVER_BASE).plateHighlight(SILVER_HIGHLIGHT).build())\n            put(\"silver_pickaxe\", MetalModel.Builder().toolStick().pickaxeBase(SILVER_BASE).pickaxeHighlight(\n                SILVER_HIGHLIGHT\n            ).build())\n            put(\"silver_axe\", MetalModel.Builder().toolStick().axeBase(SILVER_BASE).axeHighlight(\n                SILVER_HIGHLIGHT\n            ).build())\n            put(\"silver_sword\", MetalModel.Builder().toolStick().swordBase(SILVER_BASE).swordHighlight(\n                SILVER_HIGHLIGHT\n            ).build())\n            put(\"silver_hoe\", MetalModel.Builder().toolStick().hoeBase(SILVER_BASE).hoeHighlight(\n                SILVER_HIGHLIGHT\n            ).build())\n            put(\"silver_shovel\", MetalModel.Builder().toolStick().shovelBase(SILVER_BASE).shovelHighlight(\n                SILVER_HIGHLIGHT\n            ).build())\n            put(\"silver_helmet\", MetalModel.Builder().helmetBase(SILVER_BASE).helmetHighlight(SILVER_HIGHLIGHT).build())\n            put(\"silver_dust\", MetalModel.Builder().dustBase(SILVER_BASE).dustHighlight(SILVER_HIGHLIGHT).build())\n            put(\"silver_chestplate\", MetalModel.Builder().chestplateBase(SILVER_BASE).chestplateHighlight(\n                SILVER_HIGHLIGHT\n            ).build())\n            put(\"silver_boots\", MetalModel.Builder().bootsBase(SILVER_BASE).bootsHighlight(SILVER_HIGHLIGHT).build())\n            put(\"silver_leggings\", MetalModel.Builder().leggingsBase(SILVER_BASE).leggingsHighlight(\n                SILVER_HIGHLIGHT\n            ).build())\n            put(\"silver_nugget\", MetalModel.Builder().nuggetBase(SILVER_BASE).nuggetHighlight(SILVER_HIGHLIGHT).build())\n            putBlock(\"silver_ore\", MetalModel.Builder().ore().oreBase(SILVER_BASE).oreHighlight(SILVER_HIGHLIGHT).build())\n            putBlock(\"deepslate_silver_ore\", MetalModel.Builder().deepslateOre().oreBase(SILVER_BASE).oreHighlight(SILVER_HIGHLIGHT).build())\n            put(\"raw_silver\", MetalModel.Builder().rawOre(SILVER_BASE).build())\n            putBlock(\"raw_silver_block\", MetalModel.Builder().rawOreBlock(SILVER_BASE).build())\n            putBlock(\"silver_block\", MetalModel.Builder().block().blockBase(SILVER_BASE).build())\n            put(\"silver_purified_ore\", MetalModel.Builder().purifiedOreBase(SILVER_BASE).purifiedOreHighlight(\n                SILVER_HIGHLIGHT\n            ).build())\n            put(\"silver_chunk\", MetalModel.Builder().chunkBase(SILVER_BASE).chunkHighlight(SILVER_HIGHLIGHT).build())\n            put(\"molten_silver_bucket\", MetalModel.Builder().bucket().moltenBucketBase(SILVER_BASE).moltenBucketHighlight(SILVER_HIGHLIGHT).build())\n\n            put(\"copper_ingot\", MetalModel.Builder().ingotBase(COPPER_BASE).ingotHighlight(COPPER_HIGHLIGHT).build())\n            put(\"copper_plate\", MetalModel.Builder().plateBase(COPPER_BASE).plateHighlight(COPPER_HIGHLIGHT).build())\n            put(\"copper_pickaxe\", MetalModel.Builder().toolStick().pickaxeBase(COPPER_BASE).pickaxeHighlight(\n                COPPER_HIGHLIGHT\n            ).build())\n            put(\"copper_axe\", MetalModel.Builder().toolStick().axeBase(COPPER_BASE).axeHighlight(\n                COPPER_HIGHLIGHT\n            ).build())\n            put(\"copper_sword\", MetalModel.Builder().toolStick().swordBase(COPPER_BASE).swordHighlight(\n                COPPER_HIGHLIGHT\n            ).build())\n            put(\"copper_hoe\", MetalModel.Builder().toolStick().hoeBase(COPPER_BASE).hoeHighlight(\n                COPPER_HIGHLIGHT\n            ).build())\n            put(\"copper_shovel\", MetalModel.Builder().toolStick().shovelBase(COPPER_BASE).shovelHighlight(\n                COPPER_HIGHLIGHT\n            ).build())\n            put(\"copper_helmet\", MetalModel.Builder().helmetBase(COPPER_BASE).helmetHighlight(COPPER_HIGHLIGHT).build())\n            put(\"copper_dust\", MetalModel.Builder().dustBase(COPPER_BASE).dustHighlight(COPPER_HIGHLIGHT).build())\n            put(\"copper_chestplate\", MetalModel.Builder().chestplateBase(COPPER_BASE).chestplateHighlight(\n                COPPER_HIGHLIGHT\n            ).build())\n            put(\"copper_boots\", MetalModel.Builder().bootsBase(COPPER_BASE).bootsHighlight(COPPER_HIGHLIGHT).build())\n            put(\"copper_leggings\", MetalModel.Builder().leggingsBase(COPPER_BASE).leggingsHighlight(\n                COPPER_HIGHLIGHT\n            ).build())\n            put(\"copper_nugget\", MetalModel.Builder().nuggetBase(COPPER_BASE).nuggetHighlight(COPPER_HIGHLIGHT).build())\n            putBlock(\"copper_ore\", MetalModel.Builder().ore().oreBase(COPPER_BASE).oreHighlight(COPPER_HIGHLIGHT).build())\n            putBlock(\"copper_block\", MetalModel.Builder().block().blockBase(COPPER_BASE).build())\n            put(\"copper_purified_ore\", MetalModel.Builder().purifiedOreBase(COPPER_BASE).purifiedOreHighlight(\n                COPPER_HIGHLIGHT\n            ).build())\n            put(\"copper_chunk\", MetalModel.Builder().chunkBase(COPPER_BASE).chunkHighlight(COPPER_HIGHLIGHT).build())\n            put(\"molten_copper_bucket\", MetalModel.Builder().bucket().moltenBucketBase(COPPER_BASE).moltenBucketHighlight(COPPER_HIGHLIGHT).build())\n\n            put(\"tungsten_ingot\", MetalModel.Builder().ingotBase(TUNGSTEN_BASE).ingotHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_plate\", MetalModel.Builder().plateBase(TUNGSTEN_BASE).plateHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_pickaxe\", MetalModel.Builder().toolStick().pickaxeBase(TUNGSTEN_BASE).pickaxeHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_axe\", MetalModel.Builder().toolStick().axeBase(TUNGSTEN_BASE).axeHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_sword\", MetalModel.Builder().toolStick().swordBase(TUNGSTEN_BASE).swordHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_hoe\", MetalModel.Builder().toolStick().hoeBase(TUNGSTEN_BASE).hoeHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_shovel\", MetalModel.Builder().toolStick().shovelBase(TUNGSTEN_BASE).shovelHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_helmet\", MetalModel.Builder().helmetBase(TUNGSTEN_BASE).helmetHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_dust\", MetalModel.Builder().dustBase(TUNGSTEN_BASE).dustHighlight(TUNGSTEN_HIGHLIGHT).build())\n            put(\"tungsten_chestplate\", MetalModel.Builder().chestplateBase(TUNGSTEN_BASE).chestplateHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_boots\", MetalModel.Builder().bootsBase(TUNGSTEN_BASE).bootsHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_leggings\", MetalModel.Builder().leggingsBase(TUNGSTEN_BASE).leggingsHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_nugget\", MetalModel.Builder().nuggetBase(TUNGSTEN_BASE).nuggetHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            putBlock(\"deepslate_tungsten_ore\", MetalModel.Builder().deepslateOre().oreBase(TUNGSTEN_BASE).oreHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            putBlock(\"tungsten_ore\", MetalModel.Builder().ore().oreBase(TUNGSTEN_BASE).oreHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"raw_tungsten\", MetalModel.Builder().rawOre(TUNGSTEN_BASE).build())\n            putBlock(\"raw_tungsten_block\", MetalModel.Builder().rawOreBlock(TUNGSTEN_BASE).build())\n            putBlock(\"tungsten_block\", MetalModel.Builder().block().blockBase(TUNGSTEN_BASE).build())\n            put(\"tungsten_purified_ore\", MetalModel.Builder().purifiedOreBase(TUNGSTEN_BASE).purifiedOreHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n            put(\"tungsten_chunk\", MetalModel.Builder().chunkBase(TUNGSTEN_BASE).chunkHighlight(\n                TUNGSTEN_HIGHLIGHT\n            ).build())\n\n            put(\"bronze_ingot\", MetalModel.Builder().ingotBase(BRONZE_BASE).ingotHighlight(BRONZE_HIGHLIGHT).build())\n            put(\"bronze_plate\", MetalModel.Builder().plateBase(BRONZE_BASE).plateHighlight(BRONZE_HIGHLIGHT).build())\n            put(\"bronze_pickaxe\", MetalModel.Builder().toolStick().pickaxeBase(BRONZE_BASE).pickaxeHighlight(\n                BRONZE_HIGHLIGHT\n            ).build())\n            put(\"bronze_axe\", MetalModel.Builder().toolStick().axeBase(BRONZE_BASE).axeHighlight(\n                BRONZE_HIGHLIGHT\n            ).build())\n            put(\"bronze_sword\", MetalModel.Builder().toolStick().swordBase(BRONZE_BASE).swordHighlight(\n                BRONZE_HIGHLIGHT\n            ).build())\n            put(\"bronze_hoe\", MetalModel.Builder().toolStick().hoeBase(BRONZE_BASE).hoeHighlight(\n                BRONZE_HIGHLIGHT\n            ).build())\n            put(\"bronze_shovel\", MetalModel.Builder().toolStick().shovelBase(BRONZE_BASE).shovelHighlight(\n                BRONZE_HIGHLIGHT\n            ).build())\n            put(\"bronze_helmet\", MetalModel.Builder().helmetBase(BRONZE_BASE).helmetHighlight(BRONZE_HIGHLIGHT).build())\n            put(\"bronze_dust\", MetalModel.Builder().dustBase(BRONZE_BASE).dustHighlight(BRONZE_HIGHLIGHT).build())\n            put(\"bronze_chestplate\", MetalModel.Builder().chestplateBase(BRONZE_BASE).chestplateHighlight(\n                BRONZE_HIGHLIGHT\n            ).build())\n            put(\"bronze_boots\", MetalModel.Builder().bootsBase(BRONZE_BASE).bootsHighlight(BRONZE_HIGHLIGHT).build())\n            put(\"bronze_leggings\", MetalModel.Builder().leggingsBase(BRONZE_BASE).leggingsHighlight(\n                BRONZE_HIGHLIGHT\n            ).build())\n            put(\"bronze_nugget\", MetalModel.Builder().nuggetBase(BRONZE_BASE).nuggetHighlight(BRONZE_HIGHLIGHT).build())\n            putBlock(\"bronze_ore\", MetalModel.Builder().ore().oreBase(BRONZE_BASE).oreHighlight(BRONZE_HIGHLIGHT).build())\n            putBlock(\"bronze_block\", MetalModel.Builder().block().blockBase(BRONZE_BASE).build())\n            put(\"bronze_purified_ore\", MetalModel.Builder().purifiedOreBase(BRONZE_BASE).purifiedOreHighlight(\n                BRONZE_HIGHLIGHT\n            ).build())\n            put(\"bronze_chunk\", MetalModel.Builder().chunkBase(BRONZE_BASE).chunkHighlight(BRONZE_HIGHLIGHT).build())\n\n            put(\"electrum_ingot\", MetalModel.Builder().ingotBase(ELECTRUM_BASE).ingotHighlight(\n                ELECTRUM_HIGHLIGHT\n            ).build())\n            put(\"electrum_plate\", MetalModel.Builder().plateBase(ELECTRUM_BASE).plateHighlight(\n                ELECTRUM_HIGHLIGHT\n            ).build())\n            put(\"electrum_pickaxe\", MetalModel.Builder().toolStick().pickaxeBase(ELECTRUM_BASE).pickaxeHighlight(\n                ELECTRUM_HIGHLIGHT\n            ).build())\n            put(\"electrum_axe\", MetalModel.Builder().toolStick().axeBase(ELECTRUM_BASE).axeHighlight(\n                ELECTRUM_HIGHLIGHT\n            ).build())\n            put(\"electrum_sword\", MetalModel.Builder().toolStick().swordBase(ELECTRUM_BASE).swordHighlight(\n                ELECTRUM_HIGHLIGHT\n            ).build())\n            put(\"electrum_hoe\", MetalModel.Builder().toolStick().hoeBase(ELECTRUM_BASE).hoeHighlight(\n                ELECTRUM_HIGHLIGHT\n            ).build())\n            put(\"electrum_shovel\", MetalModel.Builder().toolStick().shovelBase(ELECTRUM_BASE).shovelHighlight(\n                ELECTRUM_HIGHLIGHT\n            ).build())\n            put(\"electrum_helmet\", MetalModel.Builder().helmetBase(ELECTRUM_BASE).helmetHighlight(ELECTRUM_HIGHLIGHT).build())\n            put(\"electrum_dust\", MetalModel.Builder().dustBase(ELECTRUM_BASE).dustHighlight(ELECTRUM_HIGHLIGHT).build())\n            put(\"electrum_chestplate\", MetalModel.Builder().chestplateBase(ELECTRUM_BASE).chestplateHighlight(\n                ELECTRUM_HIGHLIGHT\n            ).build())\n            put(\"electrum_boots\", MetalModel.Builder().bootsBase(ELECTRUM_BASE).bootsHighlight(ELECTRUM_HIGHLIGHT).build())\n            put(\"electrum_leggings\", MetalModel.Builder().leggingsBase(ELECTRUM_BASE).leggingsHighlight(\n                ELECTRUM_HIGHLIGHT\n            ).build())\n            put(\"electrum_nugget\", MetalModel.Builder().nuggetBase(ELECTRUM_BASE).nuggetHighlight(ELECTRUM_HIGHLIGHT).build())\n            putBlock(\"electrum_ore\", MetalModel.Builder().ore().oreBase(ELECTRUM_BASE).oreHighlight(ELECTRUM_HIGHLIGHT).build())\n            putBlock(\"electrum_block\", MetalModel.Builder().block().blockBase(ELECTRUM_BASE).build())\n            put(\"electrum_purified_ore\", MetalModel.Builder().purifiedOreBase(ELECTRUM_BASE).purifiedOreHighlight(\n                ELECTRUM_HIGHLIGHT\n            ).build())\n            put(\"electrum_chunk\", MetalModel.Builder().chunkBase(ELECTRUM_BASE).chunkHighlight(ELECTRUM_HIGHLIGHT).build())\n\n            put(\"steel_ingot\", MetalModel.Builder().ingotBase(STEEL_BASE).ingotHighlight(STEEL_HIGHLIGHT).build())\n            put(\"steel_plate\", MetalModel.Builder().plateBase(STEEL_BASE).plateHighlight(STEEL_HIGHLIGHT).build())\n            put(\"steel_pickaxe\", MetalModel.Builder().toolStick().pickaxeBase(STEEL_BASE).pickaxeHighlight(\n                STEEL_HIGHLIGHT\n            ).build())\n            put(\"steel_axe\", MetalModel.Builder().toolStick().axeBase(STEEL_BASE).axeHighlight(STEEL_HIGHLIGHT).build())\n            put(\"steel_sword\", MetalModel.Builder().toolStick().swordBase(STEEL_BASE).swordHighlight(STEEL_HIGHLIGHT).build())\n            put(\"steel_hoe\", MetalModel.Builder().toolStick().hoeBase(STEEL_BASE).hoeHighlight(STEEL_HIGHLIGHT).build())\n            put(\"steel_shovel\", MetalModel.Builder().toolStick().shovelBase(STEEL_BASE).shovelHighlight(STEEL_HIGHLIGHT).build())\n            put(\"steel_helmet\", MetalModel.Builder().helmetBase(STEEL_BASE).helmetHighlight(STEEL_HIGHLIGHT).build())\n            put(\"steel_dust\", MetalModel.Builder().dustBase(STEEL_BASE).dustHighlight(STEEL_HIGHLIGHT).build())\n            put(\"steel_chestplate\", MetalModel.Builder().chestplateBase(STEEL_BASE).chestplateHighlight(STEEL_HIGHLIGHT).build())\n            put(\"steel_boots\", MetalModel.Builder().bootsBase(STEEL_BASE).bootsHighlight(STEEL_HIGHLIGHT).build())\n            put(\"steel_leggings\", MetalModel.Builder().leggingsBase(STEEL_BASE).leggingsHighlight(STEEL_HIGHLIGHT).build())\n            put(\"steel_nugget\", MetalModel.Builder().nuggetBase(STEEL_BASE).nuggetHighlight(STEEL_HIGHLIGHT).build())\n            putBlock(\"steel_ore\", MetalModel.Builder().ore().oreBase(STEEL_BASE).oreHighlight(STEEL_HIGHLIGHT).build())\n            putBlock(\"steel_block\", MetalModel.Builder().block().blockBase(STEEL_BASE).build())\n            put(\"steel_purified_ore\", MetalModel.Builder().purifiedOreBase(STEEL_BASE).purifiedOreHighlight(\n                STEEL_HIGHLIGHT\n            ).build())\n            put(\"steel_chunk\", MetalModel.Builder().chunkBase(STEEL_BASE).chunkHighlight(STEEL_HIGHLIGHT).build())\n\n            put(\"tin_ingot\", MetalModel.Builder().ingotBase(TIN_BASE).ingotHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_plate\", MetalModel.Builder().plateBase(TIN_BASE).plateHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_pickaxe\", MetalModel.Builder().toolStick().pickaxeBase(TIN_BASE).pickaxeHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_axe\", MetalModel.Builder().toolStick().axeBase(TIN_BASE).axeHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_sword\", MetalModel.Builder().toolStick().swordBase(TIN_BASE).swordHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_hoe\", MetalModel.Builder().toolStick().hoeBase(TIN_BASE).hoeHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_shovel\", MetalModel.Builder().toolStick().shovelBase(TIN_BASE).shovelHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_helmet\", MetalModel.Builder().helmetBase(TIN_BASE).helmetHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_dust\", MetalModel.Builder().dustBase(TIN_BASE).dustHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_chestplate\", MetalModel.Builder().chestplateBase(TIN_BASE).chestplateHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_boots\", MetalModel.Builder().bootsBase(TIN_BASE).bootsHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_leggings\", MetalModel.Builder().leggingsBase(TIN_BASE).leggingsHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_nugget\", MetalModel.Builder().nuggetBase(TIN_BASE).nuggetHighlight(TIN_HIGHLIGHT).build())\n            putBlock(\"deepslate_tin_ore\", MetalModel.Builder().deepslateOre().oreBase(0xFFFFFFFF).oreHighlight(0xFFdedede).build())\n            putBlock(\"tin_ore\", MetalModel.Builder().ore().oreBase(0xFFFFFFFF).oreHighlight(0xFFdedede).build())\n            putBlock(\"tin_block\", MetalModel.Builder().block().blockBase(TIN_BASE).build())\n            put(\"raw_tin\", MetalModel.Builder().rawOre(TIN_BASE).build())\n            putBlock(\"raw_tin_block\", MetalModel.Builder().rawOreBlock(TIN_BASE).build())\n            put(\"tin_purified_ore\", MetalModel.Builder().purifiedOreBase(TIN_BASE).purifiedOreHighlight(TIN_HIGHLIGHT).build())\n            put(\"tin_chunk\", MetalModel.Builder().chunkBase(TIN_BASE).chunkHighlight(TIN_HIGHLIGHT).build())\n            put(\"molten_tin_bucket\", MetalModel.Builder().bucket().moltenBucketBase(TIN_BASE).moltenBucketHighlight(TIN_HIGHLIGHT).build())\n\n            put(\"nikolite_ingot\", MetalModel.Builder().ingotBase(NIKOLITE_BASE).ingotHighlight(NIKOLITE_HIGHLIGHT).build())\n            put(\"nikolite_dust\", MetalModel.Builder().dustBase(NIKOLITE_BASE).dustHighlight(NIKOLITE_HIGHLIGHT).build())\n            putBlock(\"deepslate_nikolite_ore\", MetalModel.Builder().deepslateOre().oreBase(NIKOLITE_BASE).oreHighlight(NIKOLITE_HIGHLIGHT).build())\n            putBlock(\"nikolite_ore\", MetalModel.Builder().ore().oreBase(NIKOLITE_BASE).oreHighlight(NIKOLITE_HIGHLIGHT).build())\n\n            put(\"iron_plate\", MetalModel.Builder().plateBase(IRON_BASE).plateHighlight(IRON_HIGHLIGHT).build())\n            put(\"iron_dust\", MetalModel.Builder().dustBase(IRON_BASE).dustHighlight(IRON_HIGHLIGHT).build())\n            put(\"iron_purified_ore\", MetalModel.Builder().purifiedOreBase(IRON_BASE).purifiedOreHighlight(IRON_HIGHLIGHT).build())\n            put(\"iron_chunk\", MetalModel.Builder().chunkBase(IRON_BASE).chunkHighlight(IRON_HIGHLIGHT).build())\n\n            put(\"diamond_dust\", MetalModel.Builder().dustBase(DIAMOND_BASE).dustHighlight(DIAMOND_HIGHLIGHT).dustOutline(\n                DIAMOND_OUTLINE\n            ).build())\n            put(\"sulfur_dust\", MetalModel.Builder().dustBase(SULFUR_BASE).dustHighlight(SULFUR_HIGHLIGHT).dustOutline(\n                SULFUR_OUTLINE\n            ).build())\n            put(\"coal_dust\", MetalModel.Builder().dustBase(COAL_BASE).dustHighlight(COAL_HIGHLIGHT).build())\n\n            put(\"netherite_scrap_dust\", MetalModel.Builder().dustBase(NETHERITE_SCRAP_BASE).dustHighlight(\n                NETHERITE_SCRAP_HIGHLIGHT\n            ).dustOutline(NETHERITE_SCRAP_OUTLINE).build())\n            put(\"netherite_scrap_chunk\", MetalModel.Builder().chunkBase(NETHERITE_SCRAP_BASE).chunkHighlight(\n                NETHERITE_SCRAP_HIGHLIGHT\n            ).chunkOutline(NETHERITE_SCRAP_OUTLINE).build())\n            put(\"netherite_scrap_purified_ore\", MetalModel.Builder().purifiedOreBase(NETHERITE_SCRAP_BASE).purifiedOreHighlight(\n                NETHERITE_SCRAP_HIGHLIGHT\n            ).purifiedOreOutline(NETHERITE_SCRAP_OUTLINE).build())\n            put(\"molten_netherite_bucket\", MetalModel.Builder().bucket().moltenBucketBase(NETHERITE_SCRAP_BASE).moltenBucketHighlight(NETHERITE_SCRAP_HIGHLIGHT).build())\n\n        }\n\n        private fun put(id: String, model: MetalModel) {\n            MATERIAL_PROVIDERS[ModelIdentifier(identifier(id), \"inventory\")] = model\n        }\n\n        private fun putBlock(id: String, model: MetalModel) {\n            MATERIAL_PROVIDERS[ModelIdentifier(identifier(id), \"inventory\")] = model\n            MATERIAL_PROVIDERS[ModelIdentifier(identifier(id), \"\")] = model\n        }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/datagen/utils/SpriteColorHolder.kt",
    "content": "package me.steven.indrev.datagen.utils\n\nimport net.minecraft.util.Identifier\n\nclass SpriteColorHolder(val id: Identifier, val color: Int)"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/events/client/IRClientTickEvents.kt",
    "content": "package me.steven.indrev.events.client\n\nimport me.steven.indrev.IndustrialRevolutionClient\nimport me.steven.indrev.gui.IRModularControllerScreen\nimport me.steven.indrev.gui.screenhandlers.modular.ModularItemConfigurationScreenHandler\nimport me.steven.indrev.packets.common.ToggleGamerAxePacket\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.tools.modular.IRModularItem\nimport net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.fabricmc.fabric.api.networking.v1.PacketByteBufs\nimport net.minecraft.client.MinecraftClient\n\nobject IRClientTickEvents : ClientTickEvents.EndTick {\n    override fun onEndTick(client: MinecraftClient) {\n        while (IndustrialRevolutionClient.MODULAR_CONTROLLER_KEYBINDING.wasPressed()) {\n            val playerInventory = MinecraftClient.getInstance().player?.inventory ?: break\n            val hasModularItem = (0 until playerInventory.size())\n                .associateWith { slot -> playerInventory.getStack(slot) }\n                .filter { (_, stack) -> stack.item is IRModularItem<*> }\n                .isNotEmpty()\n            if (hasModularItem)\n                MinecraftClient.getInstance()\n                    .setScreen(IRModularControllerScreen(ModularItemConfigurationScreenHandler(client.player!!.inventory)))\n        }\n        while (IndustrialRevolutionClient.GAMER_AXE_TOGGLE_KEYBINDING.wasPressed()) {\n            val itemStack = client.player?.mainHandStack ?: break\n            if (itemStack.isOf(IRItemRegistry.GAMER_AXE_ITEM)) {\n                ClientPlayNetworking.send(ToggleGamerAxePacket.PACKET_ID, PacketByteBufs.empty())\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/events/client/IRHudRenderCallback.kt",
    "content": "package me.steven.indrev.events.client\n\nimport com.mojang.blaze3d.systems.RenderSystem\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport me.steven.indrev.api.IRPlayerEntityExtension\nimport me.steven.indrev.blocks.machine.MachineBlock\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.items.armor.IRModularArmorItem\nimport me.steven.indrev.items.misc.IRMachineUpgradeItem\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.GameRenderer\nimport net.minecraft.client.render.Tessellator\nimport net.minecraft.client.render.VertexFormat\nimport net.minecraft.client.render.VertexFormats\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.entity.EquipmentSlot.*\nimport net.minecraft.item.ArmorItem\nimport net.minecraft.item.ItemStack\nimport net.minecraft.screen.PlayerScreenHandler\nimport net.minecraft.util.hit.BlockHitResult\n\nobject IRHudRenderCallback : HudRenderCallback {\n    override fun onHudRender(matrices: MatrixStack, tickDelta: Float) {\n        renderModularArmorHud(matrices)\n        renderTierUpgrade(matrices)\n    }\n\n    private fun renderModularArmorHud(matrices: MatrixStack) {\n        val client = MinecraftClient.getInstance()\n        val player = client.player\n        if (player is IRPlayerEntityExtension && player.getMaxShieldDurability() > 0) {\n\n            val color = player.inventory.getArmorStack(HEAD.entitySlotId).let { (stack, item) ->\n                if ((item as? IRModularArmorItem)?.slotType != HEAD) -1\n                else item.getColor(stack)\n            }\n            val x = IRConfig.hud.renderPosX + 2\n            val y = IRConfig.hud.renderPosY + 2\n\n            val spriteId = when {\n                player.shieldDurability == player.getMaxShieldDurability() -> DEFAULT\n                player.isRegenerating -> REGENERATING\n                player.shieldDurability <= player.getMaxShieldDurability() * 0.25 -> WARNING\n                else -> DAMAGED\n            }\n            val sprite = client.getSpriteAtlas(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE).apply(spriteId)\n            texturedRect(x + 5, y + 50, 16, 16, sprite, color, 0.8f)\n\n            ScreenDrawing.texturedRect(matrices, x, y, 90, 62, HUD_MAIN, color, 0.8f)\n            ScreenDrawing.texturedRect(matrices, x + 7, y + 33, 83, 20, HOLDER, color, 0.3f)\n            val shieldText = \"${player.shieldDurability.toInt()}/${player.getMaxShieldDurability().toInt()}\"\n            ScreenDrawing.drawStringWithShadow(matrices, shieldText, HorizontalAlignment.CENTER, x + 20, y + 56, client.textRenderer.getWidth(shieldText), color)\n            player.armorItems.forEach { stack ->\n                val item = stack.item as? ArmorItem ?: return@forEach\n                val xOffset = 21 * when (item.slotType) {\n                    FEET -> 3\n                    LEGS -> 2\n                    CHEST -> 1\n                    HEAD -> 0\n                    else -> return@forEach\n                }\n                client.itemRenderer.renderInGui(stack, x + 9 + xOffset, y + 35)\n                client.itemRenderer.renderGuiItemOverlay(client.textRenderer, stack, x + 9 + xOffset, y + 35)\n            }\n        }\n    }\n\n    private fun renderTierUpgrade(matrices: MatrixStack) {\n        val client = MinecraftClient.getInstance()\n        val world = client.world ?: return\n        val player = client.player ?: return\n        val hit = MinecraftClient.getInstance().crosshairTarget as? BlockHitResult ?: return\n        val x = client.window.scaledWidth / 2\n        val y = (client.window.scaledHeight / 2) + 8\n\n        val stack = player.mainHandStack\n        val item = stack.item as? IRMachineUpgradeItem ?: return\n        val state = world.getBlockState(hit.blockPos)\n        val block = state?.block as? MachineBlock\n\n\n        client.itemRenderer.renderInGui(stack, x, y)\n\n        if (block != null && block.registry.upgradeable && block.tier == item.from) {\n            matrices.push()\n            matrices.scale(0.5f, 0.5f, 0.5f)\n            client.textRenderer.draw(matrices, \"Right Click to upgrade machine\", x.toFloat() + 16 + 64 + 64 + 64 + 42, y.toFloat()+ 64 + 64 + 13, -1)\n            matrices.pop()\n\n            client.itemRenderer.renderInGui(ItemStack(block), x + 8, y + 16)\n            RenderSystem.disableDepthTest()\n            ScreenDrawing.texturedRect(matrices, x + 8 + 16 + 2, y + 16, 16, 16, ARROW, -1)\n            ScreenDrawing.texturedRect(matrices, x + 8 + 16 + 6, y + 18, 16, 16, CHECKMARK, -1)\n            client.itemRenderer.renderInGui(ItemStack(block.registry.block(item.to)), x + 8 + 32 + 4, y + 16)\n        } else {\n            RenderSystem.disableDepthTest()\n            ScreenDrawing.texturedRect(matrices, x + 5, y + 5, 16, 16, X, -1)\n            RenderSystem.enableDepthTest()\n            matrices.push()\n            matrices.scale(0.5f, 0.5f, 0.5f)\n\n            val txt = when {\n                block == null -> \"This is not a machine\"\n                !block.registry.upgradeable -> \"This machine does not accept tier upgrades\"\n                block.tier != item.from -> \"This is not the right tier\"\n                else -> \"This should not happen!\"\n            }\n\n            client.textRenderer.draw(matrices, txt,  x.toFloat() + 16 + 64 + 64 + 64 + 42, y.toFloat()+ 64 + 64 + 13, -1)\n            matrices.pop()\n        }\n\n    }\n\n\n    fun texturedRect(\n        x: Int,\n        y: Int,\n        width: Int,\n        height: Int,\n        sprite: Sprite,\n        color: Int,\n        opacity: Float\n    ) {\n        val r = (color shr 16 and 255).toFloat() / 255.0f\n        val g = (color shr 8 and 255).toFloat() / 255.0f\n        val b = (color and 255).toFloat() / 255.0f\n        RenderSystem.disableDepthTest()\n        RenderSystem.depthMask(false)\n        RenderSystem.enableBlend()\n        RenderSystem.defaultBlendFunc()\n        RenderSystem.setShader { GameRenderer.getPositionTexShader() }\n        RenderSystem.setShaderColor(r, g, b, opacity)\n        RenderSystem.setShaderTexture(0, PlayerScreenHandler.BLOCK_ATLAS_TEXTURE)\n        Tessellator.getInstance().run {\n            buffer.run {\n                begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE)\n                vertex(x.toDouble(), y + height.toDouble(), -90.0).texture(sprite.minU, sprite.maxV).next()\n                vertex(x + width.toDouble(), y + height.toDouble(), -90.0).texture(sprite.maxU, sprite.maxV).next()\n                vertex(x + width.toDouble(), y.toDouble(), -90.0).texture(sprite.maxU, sprite.minV).next()\n                vertex(x.toDouble(), y.toDouble(), -90.0).texture(sprite.minU, sprite.minV).next()\n            }\n            draw()\n        }\n        RenderSystem.depthMask(true)\n        RenderSystem.enableDepthTest()\n        RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f)\n    }\n\n    private val WARNING = identifier(\"gui/hud_warning\")\n    private val REGENERATING = identifier(\"gui/hud_regenerating\")\n    private val DAMAGED = identifier(\"gui/hud_damaged\")\n    private val DEFAULT = identifier(\"gui/hud_default\")\n    private val HUD_MAIN = identifier(\"textures/gui/hud_main.png\")\n    private val HOLDER = identifier(\"textures/gui/hud_armor_holder.png\")\n\n    private val CHECKMARK = identifier(\"textures/gui/checkmark.png\")\n    private val X = identifier(\"textures/gui/x.png\")\n    private val ARROW = identifier(\"textures/gui/widget_processing_empty.png\")\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/events/client/IRLivingEntityFeatureRendererCallback.kt",
    "content": "package me.steven.indrev.events.client\n\nimport me.steven.indrev.armor.ModuleFeatureRenderer\nimport me.steven.indrev.armor.ReinforcedElytraFeatureRenderer\nimport net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback\nimport net.minecraft.client.render.entity.EntityRendererFactory\nimport net.minecraft.client.render.entity.LivingEntityRenderer\nimport net.minecraft.client.render.entity.model.BipedEntityModel\nimport net.minecraft.client.render.entity.model.EntityModelLayers\nimport net.minecraft.entity.EntityType\nimport net.minecraft.entity.LivingEntity\n\nobject IRLivingEntityFeatureRendererCallback : LivingEntityFeatureRendererRegistrationCallback {\n    override fun registerRenderers(\n        entityType: EntityType<out LivingEntity>,\n        renderer: LivingEntityRenderer<*, *>,\n        helper: LivingEntityFeatureRendererRegistrationCallback.RegistrationHelper,\n        ctx: EntityRendererFactory.Context\n    ) {\n        val slim = false\n        helper.register(\n            ModuleFeatureRenderer(\n                renderer as LivingEntityRenderer<LivingEntity, BipedEntityModel<LivingEntity>>,\n                BipedEntityModel(ctx.getPart(if (slim) EntityModelLayers.PLAYER_SLIM_INNER_ARMOR else EntityModelLayers.PLAYER_INNER_ARMOR)),\n                BipedEntityModel(ctx.getPart(if (slim) EntityModelLayers.PLAYER_SLIM_OUTER_ARMOR else EntityModelLayers.PLAYER_OUTER_ARMOR))\n            )\n        )\n\n        helper.register(ReinforcedElytraFeatureRenderer(renderer, ctx.modelLoader))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/events/client/IRTooltipComponentsCallback.kt",
    "content": "package me.steven.indrev.events.client\n\nimport me.steven.indrev.gui.tooltip.energy.EnergyTooltipComponent\nimport me.steven.indrev.gui.tooltip.energy.EnergyTooltipData\nimport me.steven.indrev.gui.tooltip.modular.ModularTooltipComponent\nimport me.steven.indrev.gui.tooltip.modular.ModularTooltipData\nimport me.steven.indrev.gui.tooltip.oredatacards.OreDataCardTooltipComponent\nimport me.steven.indrev.gui.tooltip.oredatacards.OreDataCardTooltipData\nimport net.fabricmc.fabric.api.client.rendering.v1.TooltipComponentCallback\nimport net.minecraft.client.gui.tooltip.TooltipComponent\nimport net.minecraft.client.item.TooltipData\n\nobject IRTooltipComponentsCallback : TooltipComponentCallback {\n    override fun getComponent(data: TooltipData?): TooltipComponent? {\n        return when (data) {\n            is ModularTooltipData -> ModularTooltipComponent(data)\n            is EnergyTooltipData -> EnergyTooltipComponent(data)\n            is OreDataCardTooltipData -> OreDataCardTooltipComponent(data)\n            else -> null\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/events/client/IRWorldRenderer.kt",
    "content": "package me.steven.indrev.events.client\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport me.steven.indrev.utils.component3\nimport net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext\nimport net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.WorldRenderer\nimport net.minecraft.nbt.NbtLong\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.hit.HitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Box\n\nobject IRWorldRenderer : WorldRenderEvents.BeforeBlockOutline {\n\n    override fun beforeBlockOutline(context: WorldRenderContext, hitResult: HitResult?): Boolean {\n        val player = MinecraftClient.getInstance().player ?: return true\n        val stack = player.mainHandStack ?: return true\n        if (!stack.isIn(IndustrialRevolution.SCREWDRIVER_TAG) || stack.nbt?.contains(\"SelectedHeliostats\") != true) return true\n\n        val positions = stack.nbt?.getList(\"SelectedHeliostats\", 4)?.map { BlockPos.fromLong((it as NbtLong).longValue()) } ?: return true\n        val vcp = context.consumers() as VertexConsumerProvider.Immediate\n        val vc = vcp.getBuffer(RenderLayer.getLines()) ?: return true\n        val (cX, cY, cZ) = MinecraftClient.getInstance().gameRenderer.camera.pos\n        context.matrixStack().run {\n            positions.forEach { (x, y, z) ->\n                push()\n                translate(x.toDouble() - cX, y.toDouble() - cY, z.toDouble() - cZ)\n                WorldRenderer.drawBox(this, vc, Box(BlockPos.ORIGIN), 1f, 0.6f, 1f, 1f)\n                pop()\n            }\n        }\n        vcp.draw()\n        return !positions.contains((hitResult as? BlockHitResult)?.blockPos)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/events/client/MatterProjectorPreviewRenderer.kt",
    "content": "package me.steven.indrev.events.client\n\nimport draylar.magna.api.MagnaTool\nimport me.steven.indrev.tools.modular.DrillModule\nimport net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext\nimport net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.OverlayTexture\nimport net.minecraft.util.hit.BlockHitResult\n\n\nobject MatterProjectorPreviewRenderer : WorldRenderEvents.BeforeEntities {\n    override fun beforeEntities(context: WorldRenderContext) {\n        val player = MinecraftClient.getInstance().player ?: return\n        val target = MinecraftClient.getInstance().crosshairTarget\n        val stack = player.mainHandStack\n        val item = stack.item\n        if (player.isSneaking && DrillModule.MATTER_PROJECTOR.isInstalled(stack) && item is MagnaTool && target is BlockHitResult) {\n            item.blockFinder.findPositions(context.world(), player, item.getRadius(stack)).forEach { pos ->\n                val blockState = context.world().getBlockState(pos)\n                val offset = pos.offset(target.side)\n                if (context.world().getBlockState(offset).material.isReplaceable) {\n                    val cameraPos = MinecraftClient.getInstance().gameRenderer.camera.pos\n                    val x = offset.x - cameraPos.x\n                    val y = offset.y - cameraPos.y\n                    val z = offset.z - cameraPos.z\n                    context.matrixStack().push()\n                    context.matrixStack().translate(x, y, z)\n                    context.matrixStack().scale(0.6f, 0.6f, 0.6f)\n                    MinecraftClient.getInstance().blockRenderManager.renderBlockAsEntity(blockState, context.matrixStack(), context.consumers(), 0xFF, OverlayTexture.DEFAULT_UV)\n                    context.matrixStack().pop()\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/events/client/MiningRigInfoTooltipCallback.kt",
    "content": "package me.steven.indrev.events.client\n\nimport me.steven.indrev.api.OreDataCards\nimport me.steven.indrev.blockentities.miningrig.DataCardWriterBlockEntity\nimport me.steven.indrev.gui.screenhandlers.machines.DataCardWriterScreenHandler\nimport net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\n\nobject MiningRigInfoTooltipCallback : ItemTooltipCallback {\n    override fun getTooltip(stack: ItemStack, ctx: TooltipContext, lines: MutableList<Text>) {\n        val handler = MinecraftClient.getInstance().player?.currentScreenHandler\n        if (handler is DataCardWriterScreenHandler) {\n\n            val index = lines.size - if (ctx.isAdvanced) 1 else 0\n\n            handler.ctx.run { world, pos ->\n                val blockEntity = world.getBlockEntity(pos) as? DataCardWriterBlockEntity ?: return@run\n                DataCardWriterBlockEntity.ORES_SLOTS.forEach { slot ->\n                    val oreStack = blockEntity.inventoryComponent!!.inventory.getStack(slot)\n                    if (oreStack.equals(stack) && stack.count < 64) {\n                        lines.add(index, LiteralText(\"Missing ${64-stack.count} blocks.\").formatted(Formatting.RED))\n                        lines.add(index, LiteralText(\"Not enough blocks to collect data.\").formatted(Formatting.RED))\n                    }\n                }\n            }\n\n            val data: OreDataCards.Data = handler.ctx.get { world, pos ->\n                val blockEntity = world.getBlockEntity(pos) as? DataCardWriterBlockEntity ?: return@get OreDataCards.INVALID_DATA\n                val cardStack = blockEntity.inventoryComponent!!.inventory.getStack(0)\n                OreDataCards.readNbt(cardStack) ?: OreDataCards.INVALID_DATA\n            }.orElse(OreDataCards.INVALID_DATA)\n\n            val modifier = OreDataCards.Modifier.byItem(stack.item)\n            var remainingLevels = 0\n            var level = 0\n            when (modifier) {\n                OreDataCards.Modifier.RICHNESS -> {\n                    level = stack.count / 16\n                    remainingLevels = 40 - (data.modifiersUsed[modifier] ?: 0)\n                }\n                OreDataCards.Modifier.SPEED, OreDataCards.Modifier.SIZE -> {\n                    level = stack.count / 64\n                    remainingLevels = 1\n                }\n                OreDataCards.Modifier.RNG -> {}\n                else -> return\n            }\n\n            val modifierName = TranslatableText(modifier.translationKey)\n            if (remainingLevels <= 0)\n                lines.add(index, LiteralText(\"Cannot increase \").append(modifierName).append(\" level anymore\").formatted(\n                    Formatting.RED))\n            else if (level > 0)\n                lines.add(index, LiteralText(\"+$level \").append(modifierName).append(\" modifiers\").formatted(Formatting.GREEN))\n            else\n                lines.add(index, LiteralText(\"Not enough to increase \").append(modifierName).formatted(Formatting.RED))\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/events/common/IRLootTableCallback.kt",
    "content": "package me.steven.indrev.events.common\n\nimport me.steven.indrev.registry.IRItemRegistry\nimport net.fabricmc.fabric.api.loot.v1.FabricLootSupplierBuilder\nimport net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback\nimport net.minecraft.loot.LootManager\nimport net.minecraft.loot.LootPool\nimport net.minecraft.loot.condition.RandomChanceLootCondition\nimport net.minecraft.loot.entry.ItemEntry\nimport net.minecraft.loot.provider.number.ConstantLootNumberProvider\nimport net.minecraft.resource.ResourceManager\nimport net.minecraft.util.Identifier\n\nobject IRLootTableCallback : LootTableLoadingCallback {\n    override fun onLootTableLoading(\n        resourceManager: ResourceManager,\n        manager: LootManager,\n        id: Identifier,\n        supplier: FabricLootSupplierBuilder,\n        setter: LootTableLoadingCallback.LootTableSetter\n    ) {\n        val chance = when (id) {\n            abandonedMineshaft, simpleDungeon -> 0.3f\n            buriedTreasure -> 0.4f\n            woodlandMansion -> 0.5f\n            endCityTreasure -> 0.6f\n            else -> return\n        }\n        val builder = LootPool.builder()\n        builder.rolls(ConstantLootNumberProvider.create(1f))\n        colorModules.forEach { builder.with(ItemEntry.builder(it)) }\n        builder.conditionally(RandomChanceLootCondition.builder(chance))\n        supplier.withPool(builder.build())\n    }\n\n    private val abandonedMineshaft = Identifier(\"chests/abandoned_mineshaft\")\n    private val buriedTreasure = Identifier(\"chests/buried_treasure\")\n    private val simpleDungeon = Identifier(\"chests/simple_dungeon\")\n    private val woodlandMansion = Identifier(\"chests/woodland_mansion\")\n    private val endCityTreasure = Identifier(\"chests/end_city_treasure\")\n    private val colorModules = arrayOf(\n        IRItemRegistry.PINK_MODULE_ITEM,\n        IRItemRegistry.RED_MODULE_ITEM,\n        IRItemRegistry.PURPLE_MODULE_ITEM,\n        IRItemRegistry.BLUE_MODULE_ITEM,\n        IRItemRegistry.CYAN_MODULE_ITEM,\n        IRItemRegistry.GREEN_MODULE_ITEM,\n        IRItemRegistry.YELLOW_MODULE_ITEM,\n        IRItemRegistry.ORANGE_MODULE_ITEM,\n        IRItemRegistry.BLUE_MODULE_ITEM,\n        IRItemRegistry.BLACK_MODULE_ITEM,\n        IRItemRegistry.BROWN_MODULE_ITEM\n    )\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/fluids/BaseFluid.kt",
    "content": "package me.steven.indrev.fluids\n\nimport net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap\nimport net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler\nimport net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.FluidBlock\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.fluid.FlowableFluid\nimport net.minecraft.fluid.Fluid\nimport net.minecraft.fluid.FluidState\nimport net.minecraft.item.BucketItem\nimport net.minecraft.item.Item\nimport net.minecraft.state.StateManager\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.BlockRenderView\nimport net.minecraft.world.BlockView\nimport net.minecraft.world.WorldAccess\nimport net.minecraft.world.WorldView\n\nabstract class BaseFluid(\n    val identifier: Identifier,\n    val block: () -> FluidBlock,\n    private val bucketItem: () -> BucketItem?,\n    private val color: Int\n) : FlowableFluid() {\n    override fun toBlockState(state: FluidState?): BlockState? = block().defaultState.with(\n        FluidBlock.LEVEL, getBlockStateLevel(state)\n    )\n\n    override fun getBucketItem(): Item? = bucketItem()\n\n    override fun getLevelDecreasePerBlock(world: WorldView?): Int = 1\n\n    override fun getTickRate(world: WorldView?): Int = 5\n\n    override fun isInfinite(): Boolean = false\n\n    override fun getFlowSpeed(world: WorldView?): Int = 2\n\n    override fun canBeReplacedWith(\n        state: FluidState?,\n        world: BlockView?,\n        pos: BlockPos?,\n        fluid: Fluid,\n        direction: Direction\n    ): Boolean = false\n\n    override fun getBlastResistance(): Float = 100F\n\n    override fun beforeBreakingBlock(world: WorldAccess, pos: BlockPos?, state: BlockState) {\n        val blockEntity = if (state.hasBlockEntity()) world.getBlockEntity(pos) else null\n        Block.dropStacks(state, world, pos, blockEntity)\n    }\n\n    override fun matchesType(fluid: Fluid?): Boolean = fluid == flowing || fluid == still\n\n    fun registerRender(fluidType: FluidType) {\n        val fluidRenderHandler = object : FluidRenderHandler {\n            override fun getFluidSprites(view: BlockRenderView?, pos: BlockPos?, state: FluidState?): Array<Sprite?> =\n                fluidType.sprites\n\n            override fun getFluidColor(view: BlockRenderView?, pos: BlockPos?, state: FluidState?): Int = color\n        }\n        FluidRenderHandlerRegistry.INSTANCE.register(still, fluidRenderHandler)\n        FluidRenderHandlerRegistry.INSTANCE.register(flowing, fluidRenderHandler)\n        BlockRenderLayerMap.INSTANCE.putFluids(\n            RenderLayer.getTranslucent(),\n            still,\n            flowing\n        )\n    }\n\n    class Flowing(\n        identifier: Identifier,\n        block: () -> FluidBlock,\n        bucketItem: () -> BucketItem?,\n        color: Int,\n        val still: () -> Still\n    ) : BaseFluid(identifier, block, bucketItem, color) {\n        override fun appendProperties(builder: StateManager.Builder<Fluid, FluidState>?) {\n            super.appendProperties(builder)\n            builder?.add(LEVEL)\n        }\n\n        override fun getFlowing(): Fluid = this\n\n        override fun getStill(): Fluid = still()\n\n        override fun getLevel(state: FluidState): Int = state[LEVEL]\n\n        override fun isStill(state: FluidState?): Boolean = false\n    }\n\n    class Still(\n        identifier: Identifier,\n        block: () -> FluidBlock,\n        bucketItem: () -> BucketItem?,\n        color: Int,\n        val flowing: () -> Flowing\n    ) : BaseFluid(identifier, block, bucketItem, color) {\n        override fun getLevel(state: FluidState?): Int = 8\n\n        override fun getFlowing(): Fluid = flowing()\n\n        override fun getStill(): Fluid = this\n\n        override fun isStill(state: FluidState?): Boolean = true\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/fluids/FluidType.kt",
    "content": "package me.steven.indrev.fluids\n\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback\nimport net.fabricmc.fabric.api.resource.ResourceManagerHelper\nimport net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.resource.ResourceManager\nimport net.minecraft.resource.ResourceType\nimport net.minecraft.screen.PlayerScreenHandler\nimport net.minecraft.util.Identifier\nimport java.util.*\n\nenum class FluidType(val stillId: Identifier, val flowId: Identifier) {\n    LAVA(identifier(\"block/gray_lava_still\"), identifier(\"block/gray_lava_flow\")),\n    WATER(Identifier(\"block/water_still\"), Identifier(\"block/water_flow\")),\n    GAS(identifier(\"block/gas\"), identifier(\"block/gas\"));\n\n    var sprites = arrayOfNulls<Sprite>(2)\n\n    fun registerReloadListener() {\n        ClientSpriteRegistryCallback.event(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE)\n            .register(ClientSpriteRegistryCallback { _, registry ->\n                registry.register(stillId)\n                registry.register(flowId)\n            })\n        ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES)\n            .registerReloadListener(object : SimpleSynchronousResourceReloadListener {\n                override fun reload(manager: ResourceManager?) {\n                    val atlas = MinecraftClient.getInstance().getSpriteAtlas(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE)\n                    sprites[0] = atlas.apply(stillId)\n                    sprites[1] = atlas.apply(flowId)\n                }\n\n                override fun getFabricId(): Identifier =\n                    identifier(\"${this@FluidType.name.lowercase(Locale.getDefault())}_reload_listener\")\n            })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/IRInventoryScreen.kt",
    "content": "package me.steven.indrev.gui\n\nimport io.github.cottonmc.cotton.gui.SyncedGuiDescription\nimport io.github.cottonmc.cotton.gui.client.CottonInventoryScreen\nimport net.minecraft.entity.player.PlayerEntity\n\nclass IRInventoryScreen<T : SyncedGuiDescription>(controller: T, playerEntity: PlayerEntity) : CottonInventoryScreen<T>(controller, playerEntity) {\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/IRModularControllerScreen.kt",
    "content": "package me.steven.indrev.gui\n\nimport io.github.cottonmc.cotton.gui.GuiDescription\nimport io.github.cottonmc.cotton.gui.client.CottonClientScreen\n\nclass IRModularControllerScreen(desc: GuiDescription) : CottonClientScreen(desc)"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/IRScreenHandlerFactory.kt",
    "content": "package me.steven.indrev.gui\n\nimport net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.screen.ScreenHandler\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.server.network.ServerPlayerEntity\nimport net.minecraft.text.Text\nimport net.minecraft.util.math.BlockPos\n\nopen class IRScreenHandlerFactory(\n    private val handlerFactory: (Int, PlayerInventory, ScreenHandlerContext) -> ScreenHandler,\n    private val pos: BlockPos\n) : ExtendedScreenHandlerFactory {\n    override fun createMenu(syncId: Int, inv: PlayerInventory?, player: PlayerEntity?): ScreenHandler {\n        return handlerFactory(syncId, inv!!, ScreenHandlerContext.create(inv.player.world, pos))\n    }\n\n    override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) {\n        buf.writeBlockPos(pos)\n    }\n\n    override fun getDisplayName(): Text? = null\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/ScrewdriverScreenHandlerFactory.kt",
    "content": "package me.steven.indrev.gui\n\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.screen.ScreenHandler\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.server.network.ServerPlayerEntity\nimport net.minecraft.util.math.BlockPos\n\nclass ScrewdriverScreenHandlerFactory(\n    handlerFactory: (Int, PlayerInventory, ScreenHandlerContext) -> ScreenHandler,\n    pos: BlockPos,\n    val blockEntity: MachineBlockEntity<*>\n) : IRScreenHandlerFactory(handlerFactory, pos) {\n    override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) {\n        super.writeScreenOpeningData(player, buf)\n\n        buf.writeBoolean(blockEntity.isConfigurable(ConfigurationType.ITEM))\n        if (blockEntity.isConfigurable(ConfigurationType.ITEM)) {\n            blockEntity.getCurrentConfiguration(ConfigurationType.ITEM).writeBuf(buf)\n        }\n        buf.writeBoolean(blockEntity.isConfigurable(ConfigurationType.FLUID))\n        if (blockEntity.isConfigurable(ConfigurationType.FLUID)) {\n            blockEntity.getCurrentConfiguration(ConfigurationType.FLUID).writeBuf(buf)\n        }\n        buf.writeBoolean(blockEntity.isConfigurable(ConfigurationType.ENERGY))\n        if (blockEntity.isConfigurable(ConfigurationType.ENERGY)) {\n            blockEntity.getCurrentConfiguration(ConfigurationType.ENERGY).writeBuf(buf)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/guiutils.kt",
    "content": "package me.steven.indrev.utils\n\nimport io.github.cottonmc.cotton.gui.SyncedGuiDescription\nimport io.github.cottonmc.cotton.gui.ValidatedSlot\nimport io.github.cottonmc.cotton.gui.client.BackgroundPainter\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.*\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport io.github.cottonmc.cotton.gui.widget.icon.Icon\nimport io.netty.buffer.Unpooled\nimport me.steven.indrev.blockentities.BaseBlockEntity\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blockentities.crafters.CraftingMachineBlockEntity\nimport me.steven.indrev.blockentities.farms.AOEMachineBlockEntity\nimport me.steven.indrev.gui.widgets.machines.energyBar\nimport me.steven.indrev.gui.widgets.machines.temperatureBar\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot\nimport me.steven.indrev.items.upgrade.IREnhancerItem\nimport me.steven.indrev.packets.common.ToggleFactoryStackSplittingPacket\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.inventory.Inventory\nimport net.minecraft.item.ItemStack\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\n\nfun WGridPanel.add(w: WWidget, x: Double, y: Double, width: Double, height: Double) {\n    this.add(w, x.toInt(), y.toInt(), width.toInt(), height.toInt())\n    w.setLocation((x * 18).toInt(), (y * 18).toInt())\n}\n\nfun WGridPanel.add(w: WWidget, x: Double, y: Double) {\n    this.add(w, x.toInt(), y.toInt())\n    w.setLocation((x * 18).toInt(), (y * 18).toInt())\n}\n\nval SPLIT_ON_ICON = identifier(\"textures/gui/split_on.png\")\nval SPLIT_OFF_ICON = identifier(\"textures/gui/split_off.png\")\n\nfun SyncedGuiDescription.configure(\n    titleId: String,\n    ctx: ScreenHandlerContext,\n    playerInventory: PlayerInventory,\n    blockInventory: Inventory,\n    panel: WGridPanel = rootPanel as WGridPanel,\n    invPos: Double = 4.0,\n    widgetPos: Double = 0.15\n) {\n    panel.setSize(150, 120)\n    panel.add(createPlayerInventoryPanel(), 0.0, invPos)\n    val title = WText(TranslatableText(titleId), HorizontalAlignment.CENTER, 0x404040)\n    var titlePos = 4.7\n\n    ctx.run { world, blockPos ->\n        val blockEntity = world.getBlockEntity(blockPos) as BaseBlockEntity\n\n        val energyWidget = energyBar(blockEntity)\n        panel.add(energyWidget, 0.1, widgetPos)\n\n        if (blockEntity is MachineBlockEntity<*> && blockEntity.enhancerComponent != null) {\n            addUpgradeSlots(blockEntity, blockInventory, world, panel)\n        }\n\n        if (blockEntity is MachineBlockEntity<*> && blockEntity.temperatureComponent != null) {\n            titlePos += 0.5\n            addTemperatureWidget(blockEntity, panel, blockInventory, world, widgetPos)\n        }\n\n        if (blockEntity is AOEMachineBlockEntity<*>) {\n            addAOEWidgets(world, blockEntity, panel)\n        }\n\n        if (blockEntity is CraftingMachineBlockEntity<*> && blockEntity.craftingComponents.size > 1) {\n            addSplitStackButton(blockEntity, blockPos, world, panel)\n        }\n    }\n    panel.add(title, titlePos, 0.0)\n}\n\nfun addSplitStackButton(blockEntity: CraftingMachineBlockEntity<*>, blockPos: BlockPos, world: World, panel: WGridPanel) {\n    val buttonPanel = WGridPanel()\n    val button = object : WButton() {\n        init {\n            if (world.isClient) {\n                icon = Icon { matrices, x, y, size ->\n                    val id = if (blockEntity.isSplitOn) SPLIT_ON_ICON else SPLIT_OFF_ICON\n                    ScreenDrawing.texturedRect(matrices, x + 1, y + 1, size, size, id, -1)\n                }\n            }\n        }\n\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            tooltip?.add(TranslatableText(\"gui.indrev.button.auto_split\"))\n        }\n    }\n    button.setOnClick {\n        blockEntity.isSplitOn = !blockEntity.isSplitOn\n        val buf = PacketByteBuf(Unpooled.buffer())\n        buf.writeBlockPos(blockPos)\n        ClientPlayNetworking.send(ToggleFactoryStackSplittingPacket.SPLIT_STACKS_PACKET, buf)\n    }\n    if (world.isClient)\n        buttonPanel.backgroundPainter = UPGRADE_SLOT_PANEL_PAINTER\n    buttonPanel.add(button, 0, 0)\n    panel.add(buttonPanel, 9.7, 4.2)\n    button.setSize(20, 20)\n}\n\nfun addUpgradeSlots(blockEntity: MachineBlockEntity<*>, blockInventory: Inventory, world: World, panel: WGridPanel) {\n    val enhancerComponent = blockEntity.enhancerComponent!!\n    val slotPanel = WGridPanel()\n    for ((i, slot) in enhancerComponent.slots.withIndex()) {\n        val s =\n            object : WTooltipedItemSlot(inventory = blockInventory, startIndex = slot, emptyTooltip = mutableListOf(TranslatableText(\"gui.indrev.upgrade_slot_type\"))) {\n                override fun createSlotPeer(inventory: Inventory?, index: Int, x: Int, y: Int): ValidatedSlot {\n                    return object : ValidatedSlot(inventory, index, x, y) {\n                        override fun getMaxItemCount(stack: ItemStack): Int {\n                            val upgrade = (stack.item as? IREnhancerItem)?.enhancer ?: return 0\n                            return enhancerComponent.maxSlotCount(upgrade)\n                        }\n                    }\n                }\n            }\n        if (world.isClient)\n            s.backgroundPainter = if (enhancerComponent.isLocked(slot, blockEntity.tier)) getLockedSlotPainter(\n                blockInventory,\n                slot\n            ) else getUpgradeSlotPainter(blockInventory, slot)\n        slotPanel.add(s, 0, i)\n    }\n    if (world.isClient)\n        slotPanel.backgroundPainter = UPGRADE_SLOT_PANEL_PAINTER\n    panel.add(slotPanel, 9.7, -0.25)\n}\n\nfun addTemperatureWidget(blockEntity: MachineBlockEntity<*>, panel: WGridPanel, blockInventory: Inventory, world: World, widgetPos: Double) {\n    panel.add(temperatureBar(blockEntity), 0.95, widgetPos)\n    val coolerSlot =\n        WTooltipedItemSlot.of(blockInventory, blockEntity.inventoryComponent!!.inventory.coolerSlot!!, TranslatableText(\"gui.indrev.cooler_slot_type\"))\n    if (world.isClient)\n        coolerSlot.backgroundPainter = getCoolerSlotPainter(blockInventory, 1)\n    panel.add(coolerSlot, 0.75, widgetPos + 2.6)\n}\n\nfun addAOEWidgets(world: World, blockEntity: AOEMachineBlockEntity<*>, panel: WGridPanel) {\n    val buttonPanel = WGridPanel()\n    val button = object : WButton() {\n        override fun addTooltip(information: TooltipBuilder?) {\n            information?.add(TranslatableText(\"block.indrev.aoe.toggle.${blockEntity.renderWorkingArea}\"))\n        }\n    }\n    button.setOnClick {\n        blockEntity.renderWorkingArea = !blockEntity.renderWorkingArea\n    }\n    if (world.isClient) {\n        button.icon = Icon { matrices, x, y, _ ->\n            ScreenDrawing.texturedRect(matrices,x + 1, y + 1, 16, 16, identifier(\"textures/gui/range_icon.png\"), -1)\n        }\n        buttonPanel.backgroundPainter = UPGRADE_SLOT_PANEL_PAINTER\n    }\n    buttonPanel.add(button, 0, 0)\n    panel.add(buttonPanel, 9.7, 4.2)\n    button.setSize(20, 20)\n}\n\nfun WItemSlot.setPainterSafe(ctx: ScreenHandlerContext, painter: () -> BackgroundPainter) {\n    ctx.run { world, _ ->\n        if (world.isClient) this.backgroundPainter = painter()\n    }\n}\n\nfun WItemSlot.setIcon(ctx: ScreenHandlerContext, inventory: Inventory, slot: Int, identifier: Identifier) {\n    setPainterSafe(ctx) {\n        BackgroundPainter { matrices, left, top, widget ->\n            BackgroundPainter.SLOT.paintBackground(matrices, left, top, widget)\n            if (inventory.getStack(slot).isEmpty)\n                ScreenDrawing.texturedRect(matrices, left + 1, top + 1, 16, 16, identifier, -1)\n        }\n    }\n}\n\nval POWER_ICON_ID = identifier(\"textures/gui/power_icon.png\")\n\nfun getEnergySlotPainter(inventory: Inventory, slot: Int) = BackgroundPainter { matrices, left, top, widget ->\n    BackgroundPainter.SLOT.paintBackground(matrices, left, top, widget)\n    if (inventory.getStack(slot).isEmpty)\n        ScreenDrawing.texturedRect(matrices, left, top, 18, 18, POWER_ICON_ID, -1)\n}\n\nval VENT_ICON_ID = identifier(\"textures/gui/vent_icon.png\")\n\nfun getCoolerSlotPainter(inventory: Inventory, slot: Int) = BackgroundPainter { matrices, left, top, widget ->\n    BackgroundPainter.SLOT.paintBackground(matrices, left, top, widget)\n    if (inventory.getStack(slot).isEmpty)\n        ScreenDrawing.texturedRect(matrices, left, top, 18, 18, VENT_ICON_ID, -1)\n}\n\nval UPGRADE_ICON_ID = identifier(\"textures/gui/upgrade_icon.png\")\n\nfun getUpgradeSlotPainter(inventory: Inventory, slot: Int) = BackgroundPainter { matrices, left, top, widget ->\n    BackgroundPainter.SLOT.paintBackground(matrices, left, top, widget)\n    if (inventory.getStack(slot).isEmpty)\n        ScreenDrawing.texturedRect(matrices, left, top, 18, 18, UPGRADE_ICON_ID, -1)\n}\n\nval LOCKED_ICON_ID = identifier(\"textures/gui/locked_icon.png\")\n\nfun getLockedSlotPainter(inventory: Inventory, slot: Int) = BackgroundPainter { matrices, left, top, widget ->\n    BackgroundPainter.SLOT.paintBackground(matrices, left, top, widget)\n    if (inventory.getStack(slot).isEmpty)\n        ScreenDrawing.texturedRect(matrices, left, top, 18, 18, LOCKED_ICON_ID, -1)\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/properties/SyncableProperty.kt",
    "content": "package me.steven.indrev.gui.properties\n\nimport me.steven.indrev.components.DefaultSyncableObject\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.minecraft.network.PacketByteBuf\nimport kotlin.reflect.KProperty\n\nabstract class SyncableProperty<T>(val id: Int, val defaultValue: T, val setter: (T) -> T = { it }) : DefaultSyncableObject() {\n\n    open var value = defaultValue\n\n    fun set(value: Any?) {\n        this.value = value as T\n    }\n\n    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {\n        return value\n    }\n\n    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {\n        val before = this.value\n        this.value = setter(value)\n        markDirty { before != value }\n    }\n}\n\nobject NullSyncableProperty : SyncableProperty<Int>(-1, -1, { it }) {\n    override fun toPacket(buf: PacketByteBuf) {\n    }\n\n    override fun fromPacket(buf: PacketByteBuf) {\n    }\n}\n\nopen class IntSyncableProperty(id: Int, defaultValue: Int, setter: (Int) -> Int = { it }) : SyncableProperty<Int>(id, defaultValue, setter) {\n\n    @Environment(EnvType.CLIENT)\n    override fun fromPacket(buf: PacketByteBuf) {\n        value = buf.readInt()\n    }\n\n    override fun toPacket(buf: PacketByteBuf) {\n        buf.writeInt(value)\n    }\n}\n\nopen class LongSyncableProperty(id: Int, defaultValue: Long, setter: (Long) -> Long = { it }) : SyncableProperty<Long>(id, defaultValue, setter) {\n\n    @Environment(EnvType.CLIENT)\n    override fun fromPacket(buf: PacketByteBuf) {\n        value = buf.readLong()\n    }\n\n    override fun toPacket(buf: PacketByteBuf) {\n        buf.writeLong(value)\n    }\n}\n\nopen class DoubleSyncableProperty(id: Int, defaultValue: Double, setter: (Double) -> Double = { it }) : SyncableProperty<Double>(id, defaultValue, setter) {\n\n    @Environment(EnvType.CLIENT)\n    override fun fromPacket(buf: PacketByteBuf) {\n        value = buf.readDouble()\n    }\n\n    override fun toPacket(buf: PacketByteBuf) {\n        buf.writeDouble(value)\n    }\n}\n\nopen class BooleanSyncableProperty(id: Int, defaultValue: Boolean, setter: (Boolean) -> Boolean = { it }) : SyncableProperty<Boolean>(id, defaultValue, setter) {\n\n    @Environment(EnvType.CLIENT)\n    override fun fromPacket(buf: PacketByteBuf) {\n        value = buf.readBoolean()\n    }\n\n    override fun toPacket(buf: PacketByteBuf) {\n        buf.writeBoolean(value)\n    }\n}\n\nopen class EnumSyncableProperty<T : Enum<T>>(id: Int, defaultValue: T, val values: Array<T>, setter: (T) -> T = { it }) : SyncableProperty<T>(id, defaultValue, setter) {\n\n    @Environment(EnvType.CLIENT)\n    override fun fromPacket(buf: PacketByteBuf) {\n        value = values[buf.readInt()]\n    }\n\n    override fun toPacket(buf: PacketByteBuf) {\n        buf.writeInt(value.ordinal)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/IRGuiScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers\n\nimport io.github.cottonmc.cotton.gui.SyncedGuiDescription\nimport io.github.cottonmc.cotton.gui.client.BackgroundPainter\nimport io.netty.buffer.Unpooled\nimport me.steven.indrev.blockentities.BaseBlockEntity\nimport me.steven.indrev.components.GuiSyncableComponent\nimport me.steven.indrev.gui.properties.SyncableProperty\nimport me.steven.indrev.packets.client.GuiPropertySyncPacket\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.screen.ScreenHandlerType\nimport net.minecraft.server.network.ServerPlayerEntity\nimport net.minecraft.util.Identifier\nimport java.util.function.BiFunction\n\nopen class IRGuiScreenHandler(\n    type: ScreenHandlerType<*>?,\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    val ctx: ScreenHandlerContext\n) : SyncedGuiDescription(type, syncId, playerInventory, getBlockInventory(ctx), getBlockPropertyDelegate(ctx)) {\n\n    var component: GuiSyncableComponent? = null\n\n    init {\n        properties.clear()\n        trackedPropertyValues.clear()\n\n        ctx.run { world, pos ->\n            val blockEntity = world.getBlockEntity(pos) as? BaseBlockEntity ?: return@run\n            component = blockEntity.guiSyncableComponent\n        }\n\n    }\n\n    @Environment(EnvType.CLIENT)\n    override fun addPainters() {\n        super.addPainters()\n        val offset = 170 - rootPanel.width\n        rootPanel.backgroundPainter =\n            BackgroundPainter.createLightDarkVariants(\n                BackgroundPainter.createNinePatch(Identifier(\"libgui\", \"textures/widget/panel_light.png\")).setPadding(8)\n                    .setRightPadding(offset),\n                BackgroundPainter.createNinePatch(Identifier(\"libgui\", \"textures/widget/panel_dark.png\")).setPadding(8)\n                    .setRightPadding(offset)\n            )\n    }\n\n\n    @Suppress(\"UNCHECKED_CAST\")\n    inline fun <T : BlockEntity> withBlockEntity(block: (T) -> Unit) {\n        val be = ctx.get(BiFunction { world, pos ->\n            world.getBlockEntity(pos) as? T ?: return@BiFunction null\n        }).orElse(null)\n        block(be ?: return)\n    }\n\n    @Suppress(\"UNCHECKED_CAST\")\n    inline fun <T : BlockEntity, U> query(block: (T) -> U): U {\n        val be = ctx.get(BiFunction { world, pos ->\n            world.getBlockEntity(pos) as? T ?: return@BiFunction null\n        }).orElse(null)\n        return block(be ?: error(\"burh\"))\n    }\n\n    fun syncProperties() {\n        val props = component?.properties ?: return\n        val player = playerInventory.player\n        if (player is ServerPlayerEntity) {\n            for (i in props.indices) {\n                if (props[i].isDirty) {\n                    val buf = PacketByteBuf(Unpooled.buffer())\n                    buf.writeInt(syncId)\n                    buf.writeInt(i)\n                    props[i].toPacket(buf)\n                    ServerPlayNetworking.send(player, GuiPropertySyncPacket.SYNC_PROPERTY, buf)\n                    props[i].isDirty = false\n\n                    onSyncedProperty(i, props[i])\n                }\n            }\n        }\n    }\n\n    open fun onSyncedProperty(index: Int, property: SyncableProperty<*>) {\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/blockblacklister/BlockBlacklisterScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.blockblacklister\n\nimport io.github.cottonmc.cotton.gui.client.BackgroundPainter\nimport io.github.cottonmc.cotton.gui.client.LightweightGuiDescription\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.impl.LibGuiCommon\nimport io.github.cottonmc.cotton.gui.widget.WButton\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WLabel\nimport io.github.cottonmc.cotton.gui.widget.WWidget\nimport io.github.cottonmc.cotton.gui.widget.data.InputResult\nimport io.github.cottonmc.cotton.gui.widget.data.Insets\nimport me.steven.indrev.packets.common.UpdateMiningDrillBlockBlacklistPacket\nimport me.steven.indrev.tools.modular.DrillModule\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.fabricmc.fabric.api.networking.v1.PacketByteBufs\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport kotlin.random.Random\n\nclass BlockBlacklisterScreenHandler : LightweightGuiDescription() {\n    init {\n        val panel = WGridPanel()\n        this.rootPanel = panel\n        panel.insets = Insets.ROOT_PANEL\n\n        val stack = { MinecraftClient.getInstance().player!!.mainHandStack }\n        val range = DrillModule.RANGE.getLevel(stack())\n        if (range <= 0) {\n            panel.add(WLabel(\"Install at least one Range Module to unlock functionality.\"), 0, 0)\n        } else {\n            for (x in -range..range) {\n                for (y in -range..range) {\n                    val pos = BlockPos(x, -y, 0)\n                    panel.add(WToggleBlock(pos, stack), x + range, y + range)\n                }\n            }\n        }\n\n        val buttonFlipY = WButton(TranslatableText(\"Mirror vertically\"))\n        buttonFlipY.onClick = Runnable {\n            val buf = PacketByteBufs.create()\n            buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.FLIP_Y.ordinal)\n            ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf)\n        }\n        panel.add(buttonFlipY, range * 2 + 2, 0)\n        buttonFlipY.setSize(20, 20)\n\n        val buttonFlipX = WButton(TranslatableText(\"Mirror horizontally\"))\n        buttonFlipX.onClick = Runnable {\n            val buf = PacketByteBufs.create()\n            buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.FLIP_X.ordinal)\n            ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf)\n        }\n        panel.add(buttonFlipX, range * 2 + 2, 2)\n        buttonFlipX.setSize(20, 20)\n\n        val buttonRotX90 = WButton(TranslatableText(\"Rotate 90º\"))\n        buttonRotX90.onClick = Runnable {\n            val buf = PacketByteBufs.create()\n            buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.ROT_X_90_CLOCKWISE.ordinal)\n            ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf)\n        }\n        panel.add(buttonRotX90, range * 2 + 2, 4)\n        buttonRotX90.setSize(20, 20)\n\n        val buttonRotX90CCW = WButton(TranslatableText(\"Rotate 90º Counterclockwise\"))\n        buttonRotX90CCW.onClick = Runnable {\n            val buf = PacketByteBufs.create()\n            buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.ROT_X_90_COUNTERCLOCKWISE.ordinal)\n            ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf)\n        }\n        panel.add(buttonRotX90CCW, range * 2 + 2, 6)\n        buttonRotX90CCW.setSize(20, 20)\n\n        val buttonClear = WButton(TranslatableText(\"Reset\"))\n        buttonClear.onClick = Runnable {\n            val buf = PacketByteBufs.create()\n            buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.CLEAR.ordinal)\n            ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf)\n        }\n        panel.add(buttonClear, range * 2 + 2, 8)\n        buttonClear.setSize(20, 20)\n\n        panel.validate(this)\n    }\n\n    override fun addPainters() {\n        rootPanel.backgroundPainter = BackgroundPainter.createLightDarkVariants(\n            BackgroundPainter.createNinePatch(Identifier(LibGuiCommon.MOD_ID, \"textures/widget/panel_light.png\")).setRightPadding(-35),\n            BackgroundPainter.createNinePatch(Identifier(LibGuiCommon.MOD_ID, \"textures/widget/panel_dark.png\")).setRightPadding(-35)\n        )\n    }\n\n    class WToggleBlock(private val pos: BlockPos, private val stack: () -> ItemStack) : WWidget() {\n\n        private val isSelected: Boolean get() = !DrillModule.getBlacklistedPositions(stack()).contains(pos)\n        \n        private val texture = run {\n            val r = Random.nextFloat()\n            when {\n                r < 0.0003 -> Identifier(\"textures/block/emerald_ore.png\")\n                r < 0.0008 -> Identifier(\"textures/block/diamond_ore.png\")\n                r < 0.025 -> Identifier(\"textures/block/gold_ore.png\")\n                r < 0.05 -> Identifier(\"textures/block/iron_ore.png\")\n                r < 0.1 -> Identifier(\"textures/block/coal_ore.png\")\n                r < 0.2 -> Identifier(\"textures/block/cobblestone.png\")\n                else -> Identifier(\"textures/block/stone.png\")\n            }\n        }\n\n        override fun paint(matrices: MatrixStack, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n            ScreenDrawing.coloredRect(matrices, x - 1, y - 1, width + 2, height + 2, 0xFF000000.toInt())\n            ScreenDrawing.texturedRect(matrices, x, y, width, height, texture, -1)\n            if (!isSelected) {\n                ScreenDrawing.coloredRect(matrices, x, y, width, height, 0xAA000000.toInt())\n            }\n        }\n\n\n        override fun onClick(x: Int, y: Int, button: Int): InputResult {\n            val buf = PacketByteBufs.create()\n            buf.writeInt(UpdateMiningDrillBlockBlacklistPacket.Mode.SINGLE.ordinal)\n            buf.writeBlockPos(pos)\n            ClientPlayNetworking.send(UpdateMiningDrillBlockBlacklistPacket.UPDATE_BLACKLIST_PACKET, buf)\n            return InputResult.PROCESSED\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/BiomassGeneratorScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.generators.BiomassGeneratorBlockEntity\nimport me.steven.indrev.gui.screenhandlers.BIOMASS_GENERATOR_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.WCustomBar\nimport me.steven.indrev.gui.widgets.machines.fuelBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass BiomassGeneratorScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        BIOMASS_GENERATOR_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.biomass_generator\", ctx, playerInventory, blockInventory)\n\n        // Fuel input\n        root.add(WItemSlot.of(blockInventory, 2), 4, 2)\n        // Burning widget\n\n        val wFuel = query<BiomassGeneratorBlockEntity, WCustomBar> { be -> fuelBar(be) }\n        root.add(wFuel, 4.1, 1.1)\n        wFuel.setSize(14, 14)\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"biomass_generator\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/ChopperScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WSlider\nimport io.github.cottonmc.cotton.gui.widget.WSprite\nimport io.github.cottonmc.cotton.gui.widget.data.Axis\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport me.steven.indrev.blockentities.farms.AOEMachineBlockEntity\nimport me.steven.indrev.gui.screenhandlers.CHOPPER_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport me.steven.indrev.utils.setIcon\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.TranslatableText\n\nclass ChopperScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        CHOPPER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n\n    var value = -1\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.chopper\", ctx, playerInventory, blockInventory, invPos = 4.45)\n\n        val inputFrame = WSprite(identifier(\"textures/gui/input_frame.png\"))\n        root.add(inputFrame, 1.9, 0.7)\n        inputFrame.setSize(40, 44)\n        val outputFrame = WSprite(identifier(\"textures/gui/output_frame.png\"))\n        root.add(outputFrame, 5.1, 0.7)\n        outputFrame.setSize(58, 62)\n\n        val outputSlot = WTooltipedItemSlot.of(blockInventory, 6, 3, 3, TranslatableText(\"gui.indrev.output_slot_type\"))\n        outputSlot.isInsertingAllowed = false\n        root.add(outputSlot, 5.2, 1.0)\n        \n        val axeSlot = WTooltipedItemSlot.of(blockInventory, 2, TranslatableText(\"gui.indrev.chopper_input_axe\"))\n        axeSlot.setIcon(ctx, blockInventory, 2, AXE_ICON)\n        root.add(axeSlot, 2.0, 1.0)\n\n        val boneMealSlot = WTooltipedItemSlot.of(blockInventory, 3, TranslatableText(\"gui.indrev.chopper_input_bone_meal\"))\n        boneMealSlot.setIcon(ctx, blockInventory, 3, BONE_MEAL_ICON)\n        root.add(boneMealSlot, 3.0, 1.0)\n\n        val saplingSlot = WTooltipedItemSlot.of(blockInventory, 4, TranslatableText(\"gui.indrev.chopper_input_sapling\"))\n        saplingSlot.setIcon(ctx, blockInventory, 4, SAPLING_ICON)\n        root.add(saplingSlot, 2.0, 2.0)\n\n        val otherSaplingSlot = WTooltipedItemSlot.of(blockInventory, 5, TranslatableText(\"gui.indrev.chopper_input_sapling\"))\n        otherSaplingSlot.setIcon(ctx, blockInventory, 5, SAPLING_ICON)\n        root.add(otherSaplingSlot, 3.0, 2.0)\n\n        val slider = WSlider(1, 9, Axis.HORIZONTAL)\n        root.add(slider, 1.6, 3.6)\n        slider.setSize(50, 20)\n        ctx.run { world, pos ->\n            val blockEntity = world.getBlockEntity(pos) as? AOEMachineBlockEntity<*> ?: return@run\n            slider.value = blockEntity.range\n        }\n        slider.setValueChangeListener { newValue -> this.value = newValue }\n\n        val text = WText({\n            TranslatableText(\"block.indrev.aoe.range\", slider.value)\n        }, HorizontalAlignment.LEFT)\n        root.add(text, 1.8, 3.3)\n\n        root.validate(this)\n    }\n\n\n    override fun close(player: PlayerEntity?) {\n        super.close(player)\n        AOEMachineBlockEntity.sendValueUpdatePacket(value, ctx)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"chopper_controller\")\n        val AXE_ICON = identifier(\"textures/gui/axe_icon.png\")\n        val BONE_MEAL_ICON = identifier(\"textures/gui/bone_meal_icon.png\")\n        val SAPLING_ICON = identifier(\"textures/gui/sapling_icon.png\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/CoalGeneratorScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.generators.CoalGeneratorBlockEntity\nimport me.steven.indrev.gui.screenhandlers.COAL_GENERATOR_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.WCustomBar\nimport me.steven.indrev.gui.widgets.machines.fuelBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.util.Identifier\n\nclass CoalGeneratorScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        COAL_GENERATOR_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.coal_generator\", ctx, playerInventory, blockInventory)\n\n        val itemSlot = WItemSlot.of(blockInventory, 2)\n        root.add(itemSlot, 4, 2)\n\n        val wFuel = query<CoalGeneratorBlockEntity, WCustomBar> { be -> fuelBar(be) }\n        root.add(wFuel, 4.1, 1.1)\n        wFuel.setSize(14, 14)\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID: Identifier = identifier(\"coal_generator_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/CompressorFactoryScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.CompressorFactoryBlockEntity\nimport me.steven.indrev.gui.screenhandlers.COMPRESSOR_FACTORY_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.upProcessBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass CompressorFactoryScreenHandler (\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        COMPRESSOR_FACTORY_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.compressor_factory\", ctx, playerInventory, blockInventory, widgetPos = 0.15)\n        withBlockEntity<CompressorFactoryBlockEntity> { blockEntity ->\n            val slotsAmount = 5\n            val offset = 2.2\n\n            for ((index, slot) in blockEntity.inventoryComponent!!.inventory.inputSlots.withIndex()) {\n                val inputSlot = WItemSlot.of(blockInventory, slot)\n                root.add(inputSlot, offset + (index * 1.4), 0.6)\n            }\n\n            for (i in 0 until slotsAmount) {\n                val processWidget = upProcessBar(blockEntity, CompressorFactoryBlockEntity.CRAFTING_COMPONENT_START_ID + i)\n                root.add(processWidget, offset + (i * 1.4), 1.7)\n            }\n\n            for ((index, slot) in blockEntity.inventoryComponent!!.inventory.outputSlots.withIndex()) {\n                val outputSlot = WItemSlot.of(blockInventory, slot)\n                root.add(outputSlot, offset + (index * 1.4), 2.9)\n                outputSlot.addChangeListener { _, _, _, _ ->\n                    val player = playerInventory.player\n                    if (!player.world.isClient) {\n                        blockEntity.dropExperience(player)\n                    }\n                }\n                outputSlot.isInsertingAllowed = false\n            }\n\n        }\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"compressor_factory_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/CompressorScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.CompressorBlockEntity\nimport me.steven.indrev.gui.screenhandlers.COMPRESSOR_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.WCustomBar\nimport me.steven.indrev.gui.widgets.machines.processBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass CompressorScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        COMPRESSOR_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.compressor\", ctx, playerInventory, blockInventory)\n\n        val inputSlot = WItemSlot.of(blockInventory, 2)\n        root.add(inputSlot, 3.3, 1.8)\n\n        val processWidget = query<CompressorBlockEntity, WCustomBar> { be -> processBar(be, CompressorBlockEntity.CRAFTING_COMPONENT_ID) }\n        root.add(processWidget, 4.45, 1.8)\n\n        val outputSlot = WItemSlot.outputOf(blockInventory, 3)\n        outputSlot.isInsertingAllowed = false\n        root.add(outputSlot, 5.94, 1.8)\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"compressor_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/CondenserScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.CondenserBlockEntity\nimport me.steven.indrev.gui.screenhandlers.CONDENSER_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.fluidTank\nimport me.steven.indrev.gui.widgets.machines.processBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass CondenserScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        CONDENSER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.condenser\", ctx, playerInventory, blockInventory)\n\n        withBlockEntity<CondenserBlockEntity> { be ->\n            val fluid = fluidTank(be, CondenserBlockEntity.INPUT_TANK_ID)\n            root.add(fluid, 2.8, 1.0)\n\n            val processWidget = processBar(be, CondenserBlockEntity.CRAFTING_COMPONENT_ID)\n            root.add(processWidget, 4.0, 1.8)\n        }\n\n        val outputSlot = WItemSlot.outputOf(blockInventory, 2)\n        root.add(outputSlot, 5.7, 1.8)\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"condenser\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/DataCardWriterScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WButton\nimport io.github.cottonmc.cotton.gui.widget.WDynamicLabel\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport io.github.cottonmc.cotton.gui.widget.WLabel\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport me.steven.indrev.blockentities.miningrig.DataCardWriterBlockEntity\nimport me.steven.indrev.gui.screenhandlers.DATA_CARD_WRITER_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.packets.common.DataCardWriteStartPacket\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.fabricmc.fabric.api.networking.v1.PacketByteBufs\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.LiteralText\n\nclass DataCardWriterScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        DATA_CARD_WRITER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val startButton = WButton(LiteralText(\"Write\"))\n        val root = object : WGridPanel() {\n            override fun tick() {\n                super.tick()\n                startButton.isEnabled = component!!.get<Int>(DataCardWriterBlockEntity.TOTAL_PROCESS_ID) <= 0\n            }\n        }\n        setRootPanel(root)\n        configure(\"block.indrev.data_card_writer\", ctx, playerInventory, blockInventory, invPos = 5.7, widgetPos = 0.85)\n\n        val cardSlot = WItemSlot.of(blockInventory, 0)\n        root.add(cardSlot, 4.0, 1.0)\n\n        val inputOres = WItemSlot.of(blockInventory, 1, 6, 2)\n        root.add(inputOres, 1.5, 3.5)\n\n        val modifierSlots = WItemSlot.of(blockInventory, 13, 1, 3)\n        root.add(modifierSlots, 8.0, 0.5)\n\n        startButton.onClick = Runnable {\n\n            val buf = PacketByteBufs.create()\n            ctx.run { _, pos -> buf.writeBlockPos(pos) }\n            ClientPlayNetworking.send(DataCardWriteStartPacket.START_PACKET, buf)\n        }\n        root.add(startButton, 7.65, 4.5)\n        startButton.setSize(28, 20)\n\n        val dataLabel = WLabel(\"Data\")\n        root.add(dataLabel, 1.5, 3.0)\n\n        val timeLabel = WDynamicLabel {\n            val processTime = component!!.get<Int>(DataCardWriterBlockEntity.PROCESS_ID)\n            val totalProcessTime = component!!.get<Int>(DataCardWriterBlockEntity.TOTAL_PROCESS_ID)\n            val remaining = (totalProcessTime - processTime) / 20\n            if (remaining > 0) \"${remaining}s\" else \"\"\n        }\n        timeLabel.setAlignment(HorizontalAlignment.CENTER)\n        root.add(timeLabel, 4.1, 2.1)\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"data_card_writer_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/ElectricFurnaceFactoryScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.ElectricFurnaceFactoryBlockEntity\nimport me.steven.indrev.gui.screenhandlers.ELECTRIC_FURNACE_FACTORY_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.upProcessBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass ElectricFurnaceFactoryScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        ELECTRIC_FURNACE_FACTORY_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.electric_furnace_factory\", ctx, playerInventory, blockInventory, widgetPos = 0.15)\n        withBlockEntity<ElectricFurnaceFactoryBlockEntity> { blockEntity ->\n            val slotsAmount = 5\n            val offset = 2.2\n\n            for ((index, slot) in blockEntity.inventoryComponent!!.inventory.inputSlots.withIndex()) {\n                val inputSlot = WItemSlot.of(blockInventory, slot)\n                root.add(inputSlot, offset + (index * 1.4), 0.6)\n            }\n\n            for (i in 0 until slotsAmount) {\n                val processWidget = upProcessBar(blockEntity, ElectricFurnaceFactoryBlockEntity.CRAFTING_COMPONENT_START_ID + i)\n                root.add(processWidget, offset + (i * 1.4), 1.7)\n            }\n\n            for ((index, slot) in blockEntity.inventoryComponent!!.inventory.outputSlots.withIndex()) {\n                val outputSlot = WItemSlot.of(blockInventory, slot)\n                root.add(outputSlot, offset + (index * 1.4), 2.9)\n                outputSlot.addChangeListener { _, _, _, _ ->\n                    val player = playerInventory.player\n                    if (!player.world.isClient) {\n                        blockEntity.dropExperience(player)\n                    }\n                }\n                outputSlot.isInsertingAllowed = false\n            }\n        }\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"electric_furnace_factory_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/ElectricFurnaceScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.CraftingMachineBlockEntity\nimport me.steven.indrev.blockentities.crafters.ElectricFurnaceBlockEntity\nimport me.steven.indrev.gui.screenhandlers.ELECTRIC_FURNACE_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.WCustomBar\nimport me.steven.indrev.gui.widgets.machines.processBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass ElectricFurnaceScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        ELECTRIC_FURNACE_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.electric_furnace\", ctx, playerInventory, blockInventory)\n\n        val inputSlot = WItemSlot.of(blockInventory, 2)\n        root.add(inputSlot, 3.3, 1.8)\n\n        val processWidget = query<ElectricFurnaceBlockEntity, WCustomBar> { be -> processBar(be, ElectricFurnaceBlockEntity.CRAFTING_COMPONENT_ID) }\n        root.add(processWidget, 4.45, 1.8)\n\n        val outputSlot = WItemSlot.outputOf(blockInventory, 3)\n        outputSlot.addChangeListener { _, _, _, _ ->\n            val player = playerInventory.player\n            if (!player.world.isClient) {\n                ctx.run { world, pos ->\n                    val blockEntity = world.getBlockEntity(pos) as? CraftingMachineBlockEntity<*> ?: return@run\n                    blockEntity.dropExperience(player)\n                }\n            }\n        }\n        outputSlot.isInsertingAllowed = false\n        root.add(outputSlot, 5.94, 1.8)\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"electric_furnace_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/ElectrolyticSeparatorScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport me.steven.indrev.blockentities.crafters.ElectrolyticSeparatorBlockEntity\nimport me.steven.indrev.gui.screenhandlers.ELECTROLYTIC_SEPARATOR_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.fluidTank\nimport me.steven.indrev.gui.widgets.machines.leftProcessBar\nimport me.steven.indrev.gui.widgets.machines.processBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass ElectrolyticSeparatorScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        ELECTROLYTIC_SEPARATOR_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.electrolytic_separator\", ctx, playerInventory, blockInventory)\n\n        withBlockEntity<ElectrolyticSeparatorBlockEntity> { be ->\n            val fluid = fluidTank(be, ElectrolyticSeparatorBlockEntity.INPUT_TANK_ID)\n            root.add(fluid, 5.0, 1.0)\n\n            val processWidget = processBar(be, ElectrolyticSeparatorBlockEntity.CRAFTING_COMPONENT_ID)\n            root.add(processWidget, 6.2, 1.8)\n\n            val leftProcessWidget = leftProcessBar(be, ElectrolyticSeparatorBlockEntity.CRAFTING_COMPONENT_ID)\n            root.add(leftProcessWidget, 3.7, 1.8)\n\n            val firstOutputFluid = fluidTank(be, ElectrolyticSeparatorBlockEntity.FIRST_OUTPUT_TANK_ID)\n            root.add(firstOutputFluid, 2.5, 1.0)\n\n            val secondOutputFluid = fluidTank(be, ElectrolyticSeparatorBlockEntity.SECOND_OUTPUT_TANK_ID)\n            root.add(secondOutputFluid, 7.5, 1.0)\n        }\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"electrolytic_separator\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/FarmerScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WSlider\nimport io.github.cottonmc.cotton.gui.widget.WSprite\nimport io.github.cottonmc.cotton.gui.widget.data.Axis\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport me.steven.indrev.blockentities.farms.AOEMachineBlockEntity\nimport me.steven.indrev.gui.screenhandlers.FARMER_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot\nimport me.steven.indrev.inventories.IRInventory\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.TranslatableText\n\nclass FarmerScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        FARMER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    private var value = -1\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.farmer\", ctx, playerInventory, blockInventory, invPos = 4.45)\n\n        val inputFrame = WSprite(identifier(\"textures/gui/input_frame.png\"))\n        root.add(inputFrame, 1.9, 0.7)\n        inputFrame.setSize(40, 44)\n        val outputFrame = WSprite(identifier(\"textures/gui/output_frame.png\"))\n        root.add(outputFrame, 5.1, 0.7)\n        outputFrame.setSize(58, 62)\n\n        val outputSlot = WTooltipedItemSlot.of(blockInventory, (blockInventory as IRInventory).outputSlots.first(), 3, 3, TranslatableText(\"gui.indrev.output_slot_type\"))\n        outputSlot.isInsertingAllowed = false\n        root.add(outputSlot, 5.2, 1.0)\n        val inputSlot = WTooltipedItemSlot.of(blockInventory, (blockInventory as IRInventory).inputSlots.first(), 2, 2, TranslatableText(\"gui.indrev.farmer_input_slot_type\"))\n        root.add(inputSlot, 2.0, 1.0)\n\n        val slider = WSlider(1, 10, Axis.HORIZONTAL)\n        root.add(slider, 1.6, 3.6)\n        slider.setSize(50, 20)\n        ctx.run { world, pos ->\n            val blockEntity = world.getBlockEntity(pos) as? AOEMachineBlockEntity<*> ?: return@run\n            slider.value = blockEntity.range\n        }\n        slider.setValueChangeListener { newValue -> this.value = newValue }\n\n        val text = WText({\n            TranslatableText(\"block.indrev.aoe.range\", slider.value)\n        }, HorizontalAlignment.LEFT)\n        root.add(text, 1.8, 3.3)\n\n        root.validate(this)\n    }\n\n    override fun close(player: PlayerEntity?) {\n        super.close(player)\n        AOEMachineBlockEntity.sendValueUpdatePacket(value, ctx)\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"farmer_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/FisherScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.gui.screenhandlers.FISHER_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport me.steven.indrev.utils.setIcon\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.TranslatableText\n\nclass FisherScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        FISHER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.fisher\", ctx, playerInventory, blockInventory)\n\n        root.add(WItemSlot.of(blockInventory, 2, 2, 2), 3.7, 0.7)\n\n        val fishingRodSlot = WTooltipedItemSlot.of(blockInventory, 1, TranslatableText(\"gui.indrev.fishingrod\"))\n        fishingRodSlot.setIcon(ctx, blockInventory, 1, FISHING_ROD_ICON)\n        root.add(fishingRodSlot, 4.2, 3.0)\n\n        root.validate(this)\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"fishing_farm_screen\")\n        val FISHING_ROD_ICON = identifier(\"textures/gui/fishing_rod_icon.png\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/FluidInfuserScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.FluidInfuserBlockEntity\nimport me.steven.indrev.gui.screenhandlers.FLUID_INFUSER_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.fluidTank\nimport me.steven.indrev.gui.widgets.machines.processBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass FluidInfuserScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        FLUID_INFUSER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.fluid_infuser\", ctx, playerInventory, blockInventory)\n\n        val firstInput = WItemSlot.of(blockInventory, 2)\n        root.add(firstInput, 3.7, 1.8)\n\n        withBlockEntity<FluidInfuserBlockEntity> { be ->\n            val fluid = fluidTank(be, FluidInfuserBlockEntity.INPUT_TANK_ID)\n            root.add(fluid, 2.5, 1.0)\n\n            val processWidget = processBar(be, FluidInfuserBlockEntity.CRAFTING_COMPONENT_ID)\n            root.add(processWidget, 5.0, 1.8)\n\n            val outputStack = WItemSlot.of(blockInventory, 3)\n            root.add(outputStack, 6.4, 1.8)\n\n            val outputFluid = fluidTank(be, FluidInfuserBlockEntity.OUTPUT_TANK_ID)\n            root.add(outputFluid, 7.7, 1.0)\n        }\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"fluid_infuser\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/GasBurningGeneratorScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport io.github.cottonmc.cotton.gui.widget.WSprite\nimport me.steven.indrev.blockentities.generators.GasBurningGeneratorBlockEntity\nimport me.steven.indrev.gui.screenhandlers.GAS_BURNING_GENERATOR_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.fluidTank\nimport me.steven.indrev.gui.widgets.machines.fuelBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass GasBurningGeneratorScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        GAS_BURNING_GENERATOR_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.gas_generator\", ctx, playerInventory, blockInventory)\n\n        withBlockEntity<GasBurningGeneratorBlockEntity> { be ->\n            val wFuel = fuelBar(be)\n            root.add(wFuel, 3.5, 3.2)\n            wFuel.setSize(14, 14)\n\n            val fluid = fluidTank(be, GasBurningGeneratorBlockEntity.TANK_ID)\n            root.add(fluid, 3.5, 0.8)\n        }\n        val processSprite = WSprite(identifier(\"textures/gui/widget_processing_empty.png\"))\n        root.add(processSprite, 4.9, 1.5)\n\n        val ashSlot = WItemSlot.of(blockInventory, 1)\n        root.add(ashSlot, 6.5, 1.5)\n\n        root.validate(this)\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"gas_burning_generator_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/HeatGeneratorScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport me.steven.indrev.blockentities.generators.HeatGeneratorBlockEntity\nimport me.steven.indrev.gui.screenhandlers.HEAT_GENERATOR_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.fluidTank\nimport me.steven.indrev.gui.widgets.misc.WStaticTooltip\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\n\nclass HeatGeneratorScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        HEAT_GENERATOR_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.heat_generator\", ctx, playerInventory, blockInventory, invPos = 4.25)\n\n        val info = WStaticTooltip()\n        root.add(info, 2.3, 0.9)\n        info.setSize(90, 55)\n\n        ctx.run { world, pos ->\n            val blockEntity = world.getBlockEntity(pos) as? HeatGeneratorBlockEntity ?: return@run\n            val generatingText = WText({\n                val consumptionRate = (blockEntity.getConsumptionRate(component!![HeatGeneratorBlockEntity.CONSUMPTION_RATIO_ID])).toString()\n                TranslatableText(\"gui.indrev.heatgen.title\", LiteralText(consumptionRate).formatted(Formatting.DARK_RED)).formatted(Formatting.RED)\n            }, HorizontalAlignment.LEFT)\n            root.add(generatingText, 2.5, 1.0)\n        }\n        root.add(WText(TranslatableText(\"gui.indrev.heatgen.pertick\").formatted(Formatting.RED), HorizontalAlignment.LEFT), 2.5, 1.6)\n\n        val amount = WText({\n            val ratio = component!!.get<Long>(HeatGeneratorBlockEntity.GENERATION_RATIO_ID)\n            TranslatableText(\"gui.indrev.heatgen.generating\", LiteralText(ratio.toString()).formatted(Formatting.WHITE)).formatted(Formatting.BLUE)\n        }, HorizontalAlignment.LEFT)\n        root.add(amount, 2.5, 2.6)\n\n        root.add(WText(TranslatableText(\"gui.indrev.heatgen.pertick\").formatted(Formatting.BLUE), HorizontalAlignment.LEFT), 2.5, 3.2)\n\n        withBlockEntity<HeatGeneratorBlockEntity> { be ->\n            val fluid = fluidTank(be, HeatGeneratorBlockEntity.TANK_ID)\n            root.add(fluid, 8.0, 0.6)\n        }\n\n        root.validate(this)\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"heat_generator_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/LaserEmitterScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.client.BackgroundPainter\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WLabel\nimport io.github.cottonmc.cotton.gui.widget.data.Insets\nimport me.steven.indrev.blockentities.laser.LaserBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.LASER_HANDLER\nimport me.steven.indrev.gui.widgets.machines.energyBar\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.TranslatableText\n\nclass LaserEmitterScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        LASER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        root.insets = Insets.ROOT_PANEL\n\n        val label = WLabel(TranslatableText(\"block.indrev.laser_emitter_mk4\"))\n        root.add(label, 0, 0)\n        label.setSize(75, 0)\n\n        withBlockEntity<LaserBlockEntity> { be ->\n            val energy = energyBar(be)\n            root.add(energy, 1, 1)\n            energy.setLocation(9 + 16 + root.insets.left, 16 + root.insets.top)\n        }\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    override fun addPainters() {\n        if (rootPanel != null && !fullscreen) {\n            rootPanel.backgroundPainter = BackgroundPainter.VANILLA\n        }\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"laser\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/LazuliFluxContainerScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.client.BackgroundPainter\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity\nimport me.steven.indrev.gui.screenhandlers.BATTERY_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.machines.energyBar\nimport me.steven.indrev.gui.widgets.misc.WPlayerRender\nimport me.steven.indrev.gui.widgets.misc.WStaticTooltip\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.getEnergySlotPainter\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.item.ArmorItem\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Identifier\nimport java.util.function.Predicate\n\nclass LazuliFluxContainerScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        BATTERY_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n\n    val shieldPainter by lazy { getSlotPainter(40, Identifier(\"minecraft\", \"textures/item/empty_armor_slot_shield.png\")) }\n    val helmetPainter by lazy { getSlotPainter(39, Identifier(\"minecraft\", \"textures/item/empty_armor_slot_helmet.png\")) }\n    val chestplatePainter by lazy { getSlotPainter(38, Identifier(\"minecraft\", \"textures/item/empty_armor_slot_chestplate.png\")) }\n    val leggingsPainter by lazy { getSlotPainter(37, Identifier(\"minecraft\", \"textures/item/empty_armor_slot_leggings.png\")) }\n    val bootsPainter by lazy { getSlotPainter(36, Identifier(\"minecraft\", \"textures/item/empty_armor_slot_boots.png\")) }\n\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        root.setSize(150, 120)\n\n        root.add(\n            WText(TranslatableText(\"block.indrev.lazuli_flux_container_1\"), HorizontalAlignment.CENTER, 0x404040),\n            5.95 + 0.3,\n            0.0\n        )\n        root.add(\n            WText(TranslatableText(\"block.indrev.lazuli_flux_container_2\"), HorizontalAlignment.CENTER, 0x404040),\n            5.95 + 0.3,\n            0.7\n        )\n\n        withBlockEntity<LazuliFluxContainerBlockEntity> { be ->\n            val wEnergy = energyBar(be)\n            root.add(wEnergy, 8.4, 0.4)\n        }\n\n        ctx.run { world, _ ->\n            val itemSlot = WItemSlot.of(blockInventory, 0)\n            if (world.isClient)\n                itemSlot.backgroundPainter = getEnergySlotPainter(blockInventory, 0)\n            root.add(itemSlot, 5.7, 1.3)\n\n            root.add(createPlayerInventoryPanel(), 0.0, 4.2)\n\n            val boots = WItemSlot.of(playerInventory, 36)\n            if (world.isClient)\n                boots.backgroundPainter = bootsPainter\n            boots.filter = Predicate { stack ->\n                val item = stack.item\n                item is ArmorItem && item.slotType == EquipmentSlot.FEET\n            }\n            root.add(boots, 0, 3)\n\n            val leggings = WItemSlot.of(playerInventory, 37)\n            if (world.isClient)\n                leggings.backgroundPainter = leggingsPainter\n            leggings.filter = Predicate { stack ->\n                val item = stack.item\n                item is ArmorItem && item.slotType == EquipmentSlot.LEGS\n            }\n            root.add(leggings, 0, 2)\n\n            val chestplate = WItemSlot.of(playerInventory, 38)\n            if (world.isClient)\n                chestplate.backgroundPainter = chestplatePainter\n            chestplate.filter = Predicate { stack ->\n                val item = stack.item\n                item is ArmorItem && item.slotType == EquipmentSlot.CHEST\n            }\n            root.add(chestplate, 0, 1)\n\n            val helmet = WItemSlot.of(playerInventory, 39)\n            if (world.isClient)\n                helmet.backgroundPainter = helmetPainter\n            helmet.filter = Predicate { stack ->\n                val item = stack.item\n                item is ArmorItem && item.slotType == EquipmentSlot.HEAD\n            }\n            root.add(helmet, 0, 0)\n\n            val shield = WItemSlot.of(playerInventory, 40)\n            if (world.isClient)\n                shield.backgroundPainter = shieldPainter\n            root.add(shield, 3.8, 3.0)\n        }\n\n        val playerBg = WStaticTooltip()\n        root.add(playerBg, 1.3, 0.2)\n        playerBg.setSize(40, 65)\n\n        val playerWidget = WPlayerRender()\n        root.add(playerWidget, 2.4, 3.5)\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    private fun getSlotPainter(slot: Int, identifier: Identifier) = BackgroundPainter { matrices, left, top, panel ->\n        BackgroundPainter.SLOT.paintBackground(matrices, left, top, panel)\n        if (playerInventory.getStack(slot).isEmpty)\n            ScreenDrawing.texturedRect(matrices, left + 1, top + 1, 16, 16, identifier, -1)\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"battery_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/MiningRigComputerScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.*\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport me.steven.indrev.api.OreDataCards\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blockentities.miningrig.DrillBlockEntity\nimport me.steven.indrev.blockentities.miningrig.MiningRigBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.MINING_RIG_HANDLER\nimport me.steven.indrev.gui.widgets.misc.WCircleProgressBar\nimport me.steven.indrev.gui.widgets.misc.WStaticTooltip\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\n\nclass MiningRigComputerScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        MINING_RIG_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.mining_rig\", ctx, playerInventory, blockInventory, invPos = 6.0, widgetPos = 1.5)\n\n\n        ctx.run { world, pos ->\n            val blockEntity = world.getBlockEntity(pos) as? MiningRigBlockEntity ?: return@run\n            val activeDrills = blockEntity.getActiveDrills()\n            val bg = WStaticTooltip()\n            root.add(bg, 1.0, 0.9)\n            bg.setSize(142, 85)\n\n            root.add(WText(LiteralText(\"Insert\"), HorizontalAlignment.CENTER, 0x8080), 7.5, 1.9)\n            root.add(WText(LiteralText(\"data card\"), HorizontalAlignment.CENTER, 0x8080), 7.5, 2.6)\n            val cardSlot = WTooltipedItemSlot.of(blockInventory, 0, TranslatableText(\"gui.indrev.scan_output_slo1t_type\"))\n            root.add(cardSlot, 7.0, 3.3)\n\n            root.add(WText(TranslatableText(\"block.indrev.drill.active\"), HorizontalAlignment.CENTER, 0x8080), 3.35, 1.0)\n\n            val requiredPower = component!!.get<Long>(MiningRigBlockEntity.ENERGY_REQUIRED_ID).toDouble()\n            when {\n                component!!.get<Double>(MachineBlockEntity.ENERGY_ID) < requiredPower -> {\n                    val sprite = object : WSprite(identifier(\"textures/gui/not_enough_power.png\")) {\n                        override fun addTooltip(tooltip: TooltipBuilder?) {\n                            tooltip?.add(\n                                TranslatableText(\"block.indrev.drill.not_enough_power\").formatted(Formatting.DARK_RED),\n                                TranslatableText(\"block.indrev.drill.power_required\", requiredPower)\n                                    .formatted(Formatting.DARK_RED)\n                            )\n                        }\n                    }\n                    root.add(sprite, 3.0, 1.5)\n                    sprite.setSize(16, 16)\n                }\n                activeDrills.isEmpty() -> {\n                    val noDrillsText = TranslatableText(\"block.indrev.drill.no_drills\")\n                    root.add(WText(noDrillsText, HorizontalAlignment.CENTER, 0x404040), 3.35, 1.75)\n                }\n                else -> {\n                    activeDrills.forEachIndexed { index, drill ->\n                        val panel = getDrillInfo(drill)\n                        root.add(panel, 1.4 + if (index > 3) index - 4 else index, 1.9 + if (index > 3) 1.1 else 0.0)\n                    }\n                }\n            }\n        }\n        root.add(WText({\n            val data = OreDataCards.readNbt(blockInventory.getStack(0)) ?: return@WText LiteralText.EMPTY\n            val remaining = data.maxCycles - data.used\n            LiteralText(\"$remaining\")\n        }, HorizontalAlignment.CENTER, 0x8080), 3.35, 4.4)\n        root.add(WText(TranslatableText(\"block.indrev.mining_rig.mined\"), HorizontalAlignment.CENTER, 0x8080), 3.35, 5.0)\n\n        root.validate(this)\n    }\n\n    private fun getDrillInfo(blockEntity: DrillBlockEntity): WGridPanel {\n        val itemStack = blockEntity.inventory[0]\n        val panel = WGridPanel()\n\n        panel.add(WItem(itemStack), 0, 0)\n\n        ctx.run { world, pos ->\n            val miningRig = world.getBlockEntity(pos) as? MiningRigBlockEntity ?: return@run\n            val tmpPos = miningRig.pos.mutableCopy()\n            tmpPos.y = blockEntity.pos.y\n            val offset = blockEntity.pos.subtract(tmpPos)\n\n            val index = MiningRigBlockEntity.VALID_DRILL_POSITIONS.indexOf(offset)\n            val progress = object : WCircleProgressBar({ component!!.get<Double>(MiningRigBlockEntity.START_DRILL_ID + index).toInt() }, { component!![MiningRigBlockEntity.MAX_SPEED_ID] }, { _, _ -> 0xFF007E7E.toInt()}) {\n                override fun addTooltip(tooltip: TooltipBuilder?) {\n                    tooltip?.add(itemStack.name)\n                    val seconds = blockEntity.getSpeedMultiplier()\n                    tooltip?.add(TranslatableText(\"block.indrev.drill.faster\", seconds).formatted(Formatting.DARK_GRAY))\n                    if (blockEntity.position > 0)\n                        tooltip?.add(TranslatableText(\"block.indrev.drill.activating\").formatted(Formatting.DARK_GRAY))\n                }\n            }\n            panel.add(progress, 0, 0)\n        }\n\n        return panel\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"mining_rig\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/MiningRigDrillScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport me.steven.indrev.blockentities.miningrig.DrillBlockEntity\nimport me.steven.indrev.gui.screenhandlers.DRILL_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.TranslatableText\nimport java.util.function.Predicate\n\nclass MiningRigDrillScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        DRILL_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n\n        root.add(WText(TranslatableText(\"block.indrev.drill\"), HorizontalAlignment.LEFT, 0x404040), 0.0, -0.1)\n\n        val slot = WItemSlot.of(blockInventory, 0)\n        slot.filter = Predicate { stack -> DrillBlockEntity.isValidDrill(stack.item) }\n        root.add(slot, 4, 2)\n\n        root.add(createPlayerInventoryPanel(), 0.0, 3.8)\n\n        root.validate(this)\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"drill\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/ModularWorkbenchScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport com.mojang.blaze3d.systems.RenderSystem\nimport io.github.cottonmc.cotton.gui.ValidatedSlot\nimport io.github.cottonmc.cotton.gui.client.BackgroundPainter\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.*\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport io.github.cottonmc.cotton.gui.widget.data.InputResult\nimport io.github.cottonmc.cotton.gui.widget.icon.ItemIcon\nimport me.steven.indrev.WCustomTabPanel\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.modularworkbench.ModularWorkbenchBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.MODULAR_WORKBENCH_HANDLER\nimport me.steven.indrev.gui.widgets.machines.WCustomBar\nimport me.steven.indrev.gui.widgets.machines.upProcessBar\nimport me.steven.indrev.gui.widgets.misc.WStaticTooltip\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot\nimport me.steven.indrev.items.armor.IRModularArmorItem\nimport me.steven.indrev.items.armor.IRModuleItem\nimport me.steven.indrev.packets.common.SelectModuleOnWorkbenchPacket\nimport me.steven.indrev.recipes.machines.ModuleRecipe\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.tools.modular.ArmorModule\nimport me.steven.indrev.tools.modular.IRModularItem\nimport me.steven.indrev.utils.*\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.fabricmc.fabric.api.networking.v1.PacketByteBufs\nimport net.fabricmc.fabric.api.util.TriState\nimport net.fabricmc.loader.api.FabricLoader\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.sound.PositionedSoundInstance\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.inventory.Inventory\nimport net.minecraft.item.ItemStack\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.sound.SoundEvents\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.Identifier\nimport kotlin.math.floor\nimport kotlin.math.sin\n\n// Careful, here be dragons.\n// All the `index + 3` are because the slot numbers changed and it was easier to do this, if you don't like it don't read it :)\nclass ModularWorkbenchScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        MODULAR_WORKBENCH_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n\n    private val slotLayout = hashMapOf<Int, Array<WToggleableItemSlot>>()\n    private var slotsPanel = WPlainPanel()\n\n    private val selected: ModuleRecipe?\n        get() {\n            var r: ModuleRecipe? = null\n            ctx.run { world, pos ->\n                val blockEntity = world.getBlockEntity(pos) as? ModularWorkbenchBlockEntity\n                r = blockEntity?.recipe\n            }\n            return r\n        }\n\n    init {\n\n        slotLayout[1] = arrayOf(WToggleableItemSlot(0, 2 * 18, 0, false))\n        slotLayout[2] = arrayOf(\n            WToggleableItemSlot(0, 0,2 * 18,false),\n            WToggleableItemSlot(1, 4 * 18, 2 * 18, false)\n        )\n        slotLayout[3] = arrayOf(\n            WToggleableItemSlot(0, 2 * 18, 0, false),\n            WToggleableItemSlot(1, 0, 4 * 17, false),\n            WToggleableItemSlot(2, 4 * 18, 4 * 17, false)\n        )\n        slotLayout[4] = arrayOf(\n            WToggleableItemSlot(0, 2 * 18, 0, false),\n            WToggleableItemSlot(1, 0, 2 * 18, false),\n            WToggleableItemSlot(2, 4 * 18, 2 * 18, false),\n            WToggleableItemSlot(3, 2 * 18, 4 * 18, false)\n        )\n        slotLayout[5] = arrayOf(\n            WToggleableItemSlot(0, 2 * 18, 0, false),\n            WToggleableItemSlot(1, 0, 2 * 18, false),\n            WToggleableItemSlot(2, 4 * 18, 2 * 18, false),\n            WToggleableItemSlot(3, 1 * 14, 4 * 18, false),\n            WToggleableItemSlot(4, 3 * 20, 4 * 18, false)\n        )\n        slotLayout[6] = arrayOf(\n            WToggleableItemSlot(0, 2 * 18, 0, false),\n            WToggleableItemSlot(1, 0 * 18, 1 * 18, false),\n            WToggleableItemSlot(2, 4 * 18, 1 * 18, false),\n            WToggleableItemSlot(3, 0 * 14, 3 * 18, false),\n            WToggleableItemSlot(4, 4 * 18, 3 * 18, false),\n            WToggleableItemSlot(5, 2 * 18, 4 * 18, false)\n        )\n\n        slotLayout.forEach { (_, slots) -> slots.forEach { slotsPanel.add(it, it.x, it.y) } }\n\n        if (selected != null)\n            layoutSlots(selected!!)\n\n        val root = WCustomTabPanel()\n        setRootPanel(root)\n\n        root.add(buildInstallPanel()) { it.icon(ItemIcon(MachineRegistry.MODULAR_WORKBENCH_REGISTRY.block(Tier.MK4).asItem())) }\n        root.add(buildCraftPanel()) { it.icon(ItemIcon(IRItemRegistry.PROTECTION_MODULE_ITEM)) }\n        root.validate(this)\n    }\n\n    private fun buildCraftPanel(): WGridPanel {\n        val panel = WGridPanel()\n\n        val modules = WGridPanel()\n        ctx.run { world, pos ->\n            world.recipeManager.getRecipes(ModuleRecipe.TYPE).entries.forEachIndexed { index, (id, recipe) ->\n                val button = WModule(recipe.outputs.first().stack)\n                button.clickAction = {\n                    val buf = PacketByteBufs.create()\n                    buf.writeInt(syncId)\n                    buf.writeIdentifier(id)\n                    buf.writeBlockPos(pos)\n                    ClientPlayNetworking.send(SelectModuleOnWorkbenchPacket.MODULE_SELECT_PACKET, buf)\n                    layoutSlots(recipe)\n                    slotsPanel.addPainters()\n                }\n                modules.add(button, index % 3, floor(index / 3.0).toInt())\n            }\n        }\n\n        val outputSlot = WCraftingItemSlot(blockInventory, 15, true)\n        slotsPanel.add(outputSlot, 2 * 18, 2 * 18)\n\n        panel.add(slotsPanel, 4, 0)\n\n        val scrollPanel = WScrollPanel(modules)\n        scrollPanel.isScrollingVertically = TriState.TRUE\n        scrollPanel.isScrollingHorizontally = TriState.FALSE\n        panel.add(scrollPanel, 0, 0, 4, 5)\n        scrollPanel.setSize(3 * 18 + 8, 5 * 18 - 4)\n\n        panel.add(createPlayerInventoryPanel(), 0, 5)\n\n        return panel\n    }\n\n    fun layoutSlots(recipe: ModuleRecipe) {\n        val inputs = recipe.input\n        slotLayout.forEach { (size, slots) ->\n            slots.forEachIndexed { index, slot ->\n                slot.hidden = size != inputs.size\n                if (!slot.hidden && FabricLoader.getInstance().environmentType == EnvType.CLIENT)\n                    slot.preview = inputs[index].ingredient.matchingStacks[0]\n            }\n        }\n    }\n\n    private fun buildInstallPanel(): WGridPanel {\n        val root = WGridPanel()\n        configure(\"block.indrev.modular_workbench\", ctx, playerInventory, blockInventory, root, invPos = 5.0, widgetPos = 0.9)\n\n        val armorSlot = WTooltipedItemSlot.of(blockInventory, 2, TranslatableText(\"gui.indrev.modular_armor_slot_type\"))\n        root.add(armorSlot, 1.5, 3.5)\n\n        val moduleSlot = WTooltipedItemSlot.of(blockInventory, 1, TranslatableText(\"gui.indrev.module_slot_type\"))\n        root.add(moduleSlot, 1.5, 1.0)\n\n        val process = query<ModularWorkbenchBlockEntity, WCustomBar> { upProcessBar(it, ModularWorkbenchBlockEntity.INSTALL_TIME_ID, ModularWorkbenchBlockEntity.MAX_INSTALL_TIME_ID) }\n        root.add(process, 1.5, 2.2)\n\n        val info = WStaticTooltip()\n        info.setSize(100, 60)\n        root.add(info, 3, 1)\n\n        addTextInfo(root)\n\n        return root\n    }\n\n    // Are you seriously still going?\n    private fun addTextInfo(panel: WGridPanel) {\n        val armorInfoText = WText({\n            val stack = blockInventory.getStack(2)\n            if (!stack.isEmpty)\n                TranslatableText(stack.item.translationKey).formatted(Formatting.DARK_PURPLE, Formatting.UNDERLINE)\n            else LiteralText.EMPTY\n        }, HorizontalAlignment.LEFT)\n\n        val moduleToInstall = WText({\n            val (stack, item) = blockInventory.getStack(1)\n            if (!stack.isEmpty && item is IRModuleItem) {\n                TranslatableText(item.translationKey).formatted(Formatting.GRAY, Formatting.ITALIC)\n            } else LiteralText.EMPTY\n        }, HorizontalAlignment.LEFT)\n\n        val modulesInstalled = WText({\n            val (stack, item) = blockInventory.getStack(2)\n            if (!stack.isEmpty && item is IRModularItem<*>) {\n                val modules = item.getCount(stack).toString()\n                MODULE_COUNT().append(LiteralText(modules).formatted(Formatting.WHITE))\n            } else LiteralText.EMPTY\n        }, HorizontalAlignment.LEFT)\n\n        val shield = WText({\n            val (stack, item) = blockInventory.getStack(2)\n            if (!stack.isEmpty && item is IRModularArmorItem) {\n                val shield = item.getMaxShield(ArmorModule.PROTECTION.getLevel(stack)).toString()\n                SHIELD_TEXT().append(LiteralText(shield).formatted(Formatting.WHITE))\n            } else LiteralText.EMPTY\n        }, HorizontalAlignment.LEFT)\n\n        val installing = WText({\n            val state = component!!.get<ModularWorkbenchBlockEntity.State>(ModularWorkbenchBlockEntity.STATE_ID)\n            if (state == ModularWorkbenchBlockEntity.State.INSTALLING) {\n                INSTALLING_TEXT()\n            } else LiteralText.EMPTY\n        }, HorizontalAlignment.LEFT)\n\n        val progress = WText({\n            val stack = blockInventory.getStack(1)\n            val item = stack.item\n            if (!stack.isEmpty && item is IRModuleItem) {\n                val progress = component!!.get<Int>(ModularWorkbenchBlockEntity.INSTALL_TIME_ID)\n                when (component!!.get<ModularWorkbenchBlockEntity.State>(ModularWorkbenchBlockEntity.STATE_ID)) {\n                    ModularWorkbenchBlockEntity.State.INCOMPATIBLE -> INCOMPATIBLE_TEXT()\n                    ModularWorkbenchBlockEntity.State.MAX_LEVEL -> MAX_LEVEL_TEXT()\n                    else -> {\n                        val percent = ((progress / component!!.get<Int>(ModularWorkbenchBlockEntity.MAX_INSTALL_TIME_ID).toDouble().coerceAtLeast(1.0)) * 100).toInt()\n                        PROGRESS_TEXT().append(LiteralText(\"$percent%\"))\n                    }\n                }\n            } else LiteralText.EMPTY\n        }, HorizontalAlignment.LEFT)\n\n        panel.add(installing, 3, 3)\n        panel.add(progress, 3, 4)\n        panel.add(armorInfoText, 3, 1)\n        panel.add(modulesInstalled, 3.0, 1.5)\n        panel.add(shield, 3, 2)\n        panel.add(moduleToInstall, 3.0, 3.5)\n    }\n\n    @Environment(EnvType.CLIENT)\n    override fun addPainters() {\n        val offset = 178 - rootPanel.width\n        (rootPanel as WCustomTabPanel).setForceBackgroundPainter(\n            BackgroundPainter.createLightDarkVariants(\n                BackgroundPainter.createNinePatch(Identifier(\"libgui\", \"textures/widget/panel_light.png\")).setPadding(8).setLeftPadding(0)\n                    .setRightPadding(offset).setTopPadding(-25),\n                BackgroundPainter.createNinePatch(Identifier(\"libgui\", \"textures/widget/panel_dark.png\")).setPadding(8)\n                    .setRightPadding(offset)\n            ))\n    }\n\n    inner class WModule(private val itemStack: ItemStack) : WWidget() {\n\n        var clickAction: () -> Unit = {}\n\n        override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n            val hovered = mouseX >= 0 && mouseY >= 0 && mouseX < width && mouseY < height\n            ScreenDrawing.drawBeveledPanel(matrices, x, y, width, height)\n            if (hovered) ScreenDrawing.coloredRect(matrices, x, y, width, height, 0x887d88ff.toInt())\n            MinecraftClient.getInstance().itemRenderer.renderInGui(itemStack, x + 1, y + 1)\n        }\n\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            val texts = itemStack.getTooltip(MinecraftClient.getInstance().player)\n            { MinecraftClient.getInstance().options.advancedItemTooltips }\n            tooltip?.add(*texts.toTypedArray())\n        }\n\n        override fun onClick(x: Int, y: Int, button: Int): InputResult {\n            MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0f))\n            clickAction()\n            return InputResult.PROCESSED\n        }\n    }\n\n    inner class WToggleableItemSlot(private val index: Int, x: Int, y: Int, big: Boolean)\n        : WItemSlot(blockInventory, index + 3, 1, 1, big) {\n\n        var preview: ItemStack? = null\n\n        init {\n            this.x = x\n            this.y = y\n        }\n\n        var hidden = true\n\n        override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n            if (!hidden) {\n                super.paint(matrices, x, y, mouseX, mouseY)\n                if (preview != null) {\n                    val renderer = MinecraftClient.getInstance().itemRenderer\n                    renderer.renderInGui(preview, x + 1, y + 1)\n                    RenderSystem.disableDepthTest()\n                    ScreenDrawing.coloredRect(matrices, x + 1, y + 1, 16, 16, 0xb08b8b8b.toInt())\n                }\n            }\n        }\n\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            if (!blockInventory.getStack(index + 3).isEmpty) return\n            val texts = preview?.getTooltip(MinecraftClient.getInstance().player)\n            { MinecraftClient.getInstance().options.advancedItemTooltips } ?: return\n            tooltip?.add(*texts.toTypedArray())\n        }\n\n        override fun createSlotPeer(inventory: Inventory, index: Int, x: Int, y: Int): ValidatedSlot {\n            return WToggleableSlot(inventory, index, x, y) { hidden }\n        }\n    }\n    inner class WCraftingItemSlot(inventory: Inventory, index: Int, big: Boolean)\n        : WItemSlot(inventory, index, 1, 1, big) {\n\n        init {\n            isInsertingAllowed = false\n        }\n\n        override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n            super.paint(matrices, x, y, mouseX, mouseY)\n            if (selected != null) {\n                val cur = component!!.get<Int>(ModularWorkbenchBlockEntity.PROCESS_TIME_ID)\n                val max = component!!.get<Int>(ModularWorkbenchBlockEntity.MAX_PROCESS_TIME_ID)\n                val renderer = MinecraftClient.getInstance().itemRenderer\n                renderer.renderInGui(selected!!.outputs[0].stack, x + 1, y + 1)\n                RenderSystem.disableDepthTest()\n                val a = 255 - ((cur / max.toDouble()) * 255).toInt()\n                ScreenDrawing.coloredRect(matrices, x + 1, y + 1, 16, 16, a shl 24 or 0x8b8b8b)\n                RenderSystem.enableDepthTest()\n\n                if (cur > 0 && max > 0 && cur != max) {\n                    val txt = \"${(cur / max.toDouble() * 100).toInt() }%\"\n                    val width = MinecraftClient.getInstance().textRenderer.getWidth(txt)\n                    MinecraftClient.getInstance().textRenderer.draw(matrices, txt, x.toFloat() + 10 - (width / 2), y.toFloat() + 25, 0x404040)\n\n                    val s = sin(world.time.toDouble() / 5) * 10\n                    ScreenDrawing.coloredRect(matrices, x - 3, (y + s).toInt() + 8, this.width + 6, 2, 0x99578bfa.toInt())\n                }\n            }\n        }\n\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            val texts = selected?.outputs?.get(0)?.stack?.getTooltip(MinecraftClient.getInstance().player)\n            { MinecraftClient.getInstance().options.advancedItemTooltips } ?: return\n\n            val cur = component!!.get<Int>(ModularWorkbenchBlockEntity.PROCESS_TIME_ID)\n            val max = component!!.get<Int>(ModularWorkbenchBlockEntity.MAX_PROCESS_TIME_ID)\n            if (max > 0 && cur != max) {\n                tooltip?.add(LiteralText(\"Crafting: \"))\n                tooltip?.add(LiteralText.EMPTY)\n            }\n            tooltip?.add(*texts.toTypedArray())\n        }\n    }\n\n    inner class WToggleableSlot(inventory: Inventory, index: Int, x: Int, y: Int, val hidden: () -> Boolean) : ValidatedSlot(inventory, index, x, y) {\n\n        override fun getMaxItemCount(): Int {\n            return selected?.input?.get(index - 3)?.count ?: 0\n        }\n\n        override fun canInsert(stack: ItemStack?): Boolean {\n            return !hidden() && super.canInsert(stack)\n        }\n\n        override fun isEnabled(): Boolean {\n            return !hidden() && super.isEnabled()\n        }\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"modular_workbench_screen\")\n        val SHIELD_TEXT = { TranslatableText(\"gui.indrev.shield\").formatted(Formatting.BLUE) }\n        val PROGRESS_TEXT = { TranslatableText(\"gui.indrev.progress\").formatted(Formatting.BLUE) }\n        val MODULE_COUNT = { TranslatableText(\"gui.indrev.modules_installed\").formatted(Formatting.BLUE) }\n        val INSTALLING_TEXT = { TranslatableText(\"gui.indrev.installing\").formatted(Formatting.DARK_PURPLE, Formatting.UNDERLINE) }\n        val INCOMPATIBLE_TEXT = { TranslatableText(\"gui.indrev.incompatible\").formatted(Formatting.RED) }\n        val MAX_LEVEL_TEXT = { TranslatableText(\"gui.indrev.max_level\").formatted(Formatting.RED) }\n    }\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/PulverizerFactoryScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.PulverizerFactoryBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.PULVERIZER_FACTORY_HANDLER\nimport me.steven.indrev.gui.widgets.machines.upProcessBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass PulverizerFactoryScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        PULVERIZER_FACTORY_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.pulverizer_factory\", ctx, playerInventory, blockInventory, widgetPos = 0.15)\n        withBlockEntity<PulverizerFactoryBlockEntity> { blockEntity ->\n            val slotsAmount = 5\n            val offset = 2.2\n\n            for ((index, slot) in blockEntity.inventoryComponent!!.inventory.inputSlots.withIndex()) {\n                val inputSlot = WItemSlot.of(blockInventory, slot)\n                root.add(inputSlot, offset + (index * 1.4), 0.6)\n            }\n\n            for (i in 0 until slotsAmount) {\n                val processWidget = upProcessBar(blockEntity, PulverizerFactoryBlockEntity.CRAFTING_COMPONENT_START_ID + i)\n                root.add(processWidget, offset + (i * 1.4), 1.7)\n            }\n\n            for ((index, slot) in blockEntity.inventoryComponent!!.inventory.outputSlots.withIndex()) {\n                val outputSlot = WItemSlot.of(blockInventory, slot)\n                root.add(outputSlot, offset + (index * 1.4), 2.9)\n                outputSlot.addChangeListener { _, _, _, _ ->\n                    val player = playerInventory.player\n                    if (!player.world.isClient) {\n                        blockEntity.dropExperience(player)\n                    }\n                }\n                outputSlot.isInsertingAllowed = false\n            }\n        }\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"pulverizer_factory_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/PulverizerScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.PulverizerBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.PULVERIZER_HANDLER\nimport me.steven.indrev.gui.widgets.machines.WCustomBar\nimport me.steven.indrev.gui.widgets.machines.processBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass PulverizerScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        PULVERIZER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.pulverizer\", ctx, playerInventory, blockInventory)\n\n        val inputSlot = WItemSlot.of(blockInventory, 2)\n        root.add(inputSlot, 3.3, 1.2)\n\n        val processWidget = query<PulverizerBlockEntity, WCustomBar> { be -> processBar(be, PulverizerBlockEntity.CRAFTING_COMPONENT_ID) }\n        root.add(processWidget, 4.4, 1.2)\n\n        val outputSlot = WItemSlot.outputOf(blockInventory, 3)\n        outputSlot.isInsertingAllowed = false\n        root.add(outputSlot, 5.84, 1.2)\n\n        val extraOutputSlot = WItemSlot.of(blockInventory, 4)\n        extraOutputSlot.isInsertingAllowed = false\n        root.add(extraOutputSlot, 5.84, 2.5)\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"pulverizer_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/PumpScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WDynamicLabel\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport io.github.cottonmc.cotton.gui.widget.WLabel\nimport me.steven.indrev.blockentities.crafters.PulverizerBlockEntity\nimport me.steven.indrev.blockentities.farms.PumpBlockEntity\nimport me.steven.indrev.blockentities.generators.GasBurningGeneratorBlockEntity\nimport me.steven.indrev.blocks.machine.PumpBlock\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.PULVERIZER_HANDLER\nimport me.steven.indrev.gui.screenhandlers.PUMP_HANDLER\nimport me.steven.indrev.gui.widgets.machines.WCustomBar\nimport me.steven.indrev.gui.widgets.machines.fluidTank\nimport me.steven.indrev.gui.widgets.machines.processBar\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.LiteralText\n\nclass PumpScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        PUMP_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.pump\", ctx, playerInventory, blockInventory)\n        withBlockEntity<PumpBlockEntity> { be ->\n            val fluid = fluidTank(be, PumpBlockEntity.TANK_ID)\n            root.add(fluid, 8, 1)\n        }\n\n        root.add(WLabel(\"1 bucket every\" ), 1, 1)\n        root.add(WDynamicLabel { \"${component!!.get<Int>(PumpBlockEntity.SPEED_ID)} ticks\" }, 1, 2)\n\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"pump_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/RancherScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.client.BackgroundPainter\nimport io.github.cottonmc.cotton.gui.widget.*\nimport io.github.cottonmc.cotton.gui.widget.data.Axis\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport io.github.cottonmc.cotton.gui.widget.icon.ItemIcon\nimport io.netty.buffer.Unpooled\nimport me.steven.indrev.WCustomTabPanel\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.farms.AOEMachineBlockEntity\nimport me.steven.indrev.blockentities.farms.RancherBlockEntity\nimport me.steven.indrev.gui.properties.SyncableProperty\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.RANCHER_HANDLER\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.packets.common.UpdateRancherConfigPacket\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.registry.MachineRegistry\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Identifier\n\nclass RancherScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        RANCHER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n\n    var value = -1\n\n    val feedBabyText = WToggleButton()\n    var feedBabies: Boolean = false\n\n    val mateAdultsText = WToggleButton()\n    var mateAdults: Boolean = false\n\n    val matingLimitText = WTextField()\n    var matingLimit: Int = 0\n\n    val killAfterText = WTextField()\n    var killAfter: Int = 0\n\n    init {\n        val root = WCustomTabPanel()\n\n        setRootPanel(root)\n\n        feedBabies = component!![RancherBlockEntity.FEED_BABIES_ID]\n        mateAdults = component!![RancherBlockEntity.MATE_ADULTS]\n       // matingLimit = properties[RancherBlockEntity.MATING_LIMIT]\n        //killAfter = properties[RancherBlockEntity.KILL_AFTER]\n\n\n        root.add(buildMainPanel()) { it.icon(ItemIcon(RANCHER_MK4.asItem())) }\n        root.add(buildConfigPanel()) { it.icon(ItemIcon(IRItemRegistry.WRENCH)) }\n\n        root.validate(this)\n    }\n\n    private fun buildMainPanel(): WWidget {\n        val mainPanel = WGridPanel()\n        configure(\"block.indrev.rancher\", ctx, playerInventory, blockInventory, mainPanel, invPos = 4.45)\n\n        val inputFrame = WSprite(identifier(\"textures/gui/input_frame.png\"))\n        mainPanel.add(inputFrame, 1.9, 0.7)\n        inputFrame.setSize(40, 44)\n        val outputFrame = WSprite(identifier(\"textures/gui/output_frame.png\"))\n        mainPanel.add(outputFrame, 5.1, 0.7)\n        outputFrame.setSize(58, 62)\n\n        mainPanel.add(\n            WItemSlot.of(blockInventory, 6, 3, 3),\n            5.2,\n            1.0\n        )\n        mainPanel.add(\n            WItemSlot.of(blockInventory, 2, 2, 2),\n            2.0,\n            1.0\n        )\n\n        val slider = WSlider(1, 9, Axis.HORIZONTAL)\n        mainPanel.add(slider, 1.6, 3.6)\n        slider.setSize(50, 20)\n        ctx.run { world, pos ->\n            val blockEntity = world.getBlockEntity(pos) as? AOEMachineBlockEntity<*> ?: return@run\n            slider.value = blockEntity.range\n        }\n        slider.setValueChangeListener { newValue -> this.value = newValue }\n\n        val text = WText({\n            TranslatableText(\"block.indrev.aoe.range\", slider.value)\n        }, HorizontalAlignment.LEFT)\n        mainPanel.add(text, 1.8, 3.3)\n\n        return mainPanel\n    }\n\n    private fun buildConfigPanel(): WWidget {\n        val configPanel = WGridPanel()\n        val feedBabyInput = WGridPanel()\n        feedBabyInput.add(WLabel(\"Feed babies\"), 0.0, 0.25)\n\n        feedBabyText.toggle = feedBabies\n        feedBabyText.setOnToggle { v -> feedBabies = v }\n        feedBabyInput.add(feedBabyText, 4.4, 0.0)\n        configPanel.add(feedBabyInput, 1, 1)\n\n        val mateAdultsInput = WGridPanel()\n        mateAdultsInput.add(WLabel(\"Mate adults\"), 0.0, 0.25)\n\n        mateAdultsText.toggle = mateAdults\n        mateAdultsText.setOnToggle { v -> mateAdults = v }\n        mateAdultsInput.add(mateAdultsText, 4.4, 0.0)\n        configPanel.add(mateAdultsInput, 1, 3)\n\n        val matingLimitInput = WGridPanel()\n        matingLimitInput.add(WLabel(\"Mating limit\"), 0.0, 0.25)\n\n        matingLimitInput.add(matingLimitText, 4, 0)\n        matingLimitText.setSize(34, 18)\n        configPanel.add(matingLimitInput, 1, 5)\n\n        val killAfterInput = WGridPanel()\n        killAfterInput.add(WLabel(\"Kill after\"), 0.0, 0.25)\n\n        killAfterInput.add(killAfterText, 4, 0)\n        killAfterText.setSize(34, 18)\n        configPanel.add(killAfterInput, 1, 7)\n        \n        return configPanel\n    }\n\n    override fun onSyncedProperty(index: Int, property: SyncableProperty<*>) {\n        when (index) {\n            RancherBlockEntity.KILL_AFTER -> {\n                killAfter = property.value as Int\n                killAfterText.text = killAfter.toString()\n            }\n            RancherBlockEntity.MATING_LIMIT -> {\n                matingLimit = property.value as Int\n                matingLimitText.text = matingLimit.toString()\n            }\n            RancherBlockEntity.MATE_ADULTS -> {\n                mateAdults = property.value as Boolean\n                mateAdultsText.toggle = mateAdults\n            }\n            RancherBlockEntity.FEED_BABIES_ID -> {\n                feedBabies = property.value as Boolean\n                feedBabyText.toggle = feedBabies\n            }\n        }\n    }\n\n    @Environment(EnvType.CLIENT)\n    override fun addPainters() {\n        val offset = 178 - rootPanel.width\n        (rootPanel as WCustomTabPanel).setForceBackgroundPainter(\n            BackgroundPainter.createLightDarkVariants(\n                BackgroundPainter.createNinePatch(Identifier(\"libgui\", \"textures/widget/panel_light.png\")).setPadding(8).setLeftPadding(0)\n                    .setRightPadding(offset).setTopPadding(-25),\n                BackgroundPainter.createNinePatch(Identifier(\"libgui\", \"textures/widget/panel_dark.png\")).setPadding(8)\n                    .setRightPadding(offset)\n            ))\n    }\n\n    override fun close(player: PlayerEntity?) {\n        super.close(player)\n        AOEMachineBlockEntity.sendValueUpdatePacket(value, ctx)\n\n        ctx.run { world, pos ->\n            if (world.isClient) {\n                val buf = PacketByteBuf(Unpooled.buffer())\n                buf.writeBlockPos(pos)\n                buf.writeBoolean(feedBabies)\n                buf.writeBoolean(mateAdults)\n                buf.writeInt(matingLimitText.text.toIntOrNull() ?: matingLimit)\n                buf.writeInt(killAfterText.text.toIntOrNull() ?: killAfter)\n                ClientPlayNetworking.send(UpdateRancherConfigPacket.SYNC_RANCHER_CONFIG, buf)\n            }\n        }\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"rancher_screen\")\n        val RANCHER_MK4 by lazy { MachineRegistry.RANCHER_REGISTRY.block(Tier.MK4) }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/RecyclerScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.RecyclerBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.RECYCLER_HANDLER\nimport me.steven.indrev.gui.widgets.machines.processBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass RecyclerScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        RECYCLER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.recycler\", ctx, playerInventory, blockInventory)\n\n        val inputSlot = WItemSlot.of(blockInventory, 2)\n        root.add(inputSlot, 2.8, 1.8)\n\n        withBlockEntity<RecyclerBlockEntity> { be ->\n            val processWidget = processBar(be, RecyclerBlockEntity.CRAFTING_COMPONENT_ID)\n            root.add(processWidget, 3.95, 1.8)\n        }\n\n        val outputSlot = WItemSlot.outputOf(blockInventory, 3)\n        outputSlot.isInsertingAllowed = false\n        root.add(outputSlot, 5.44, 1.8)\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"recycler\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SawmillScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.SawmillBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.SAWMILL_HANDLER\nimport me.steven.indrev.gui.widgets.machines.WCustomBar\nimport me.steven.indrev.gui.widgets.machines.processBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass SawmillScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        SAWMILL_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.sawmill\", ctx, playerInventory, blockInventory)\n\n        val inputSlot = WItemSlot.of(blockInventory, 2)\n        root.add(inputSlot, 3.0, 1.8)\n\n        val processWidget = query<SawmillBlockEntity, WCustomBar> { be -> processBar(be, SawmillBlockEntity.CRAFTING_COMPONENT_ID) }\n        root.add(processWidget, 4.15, 1.8)\n\n        val outputSlots = WItemSlot.of(blockInventory, 3, 2, 2)\n        outputSlots.isInsertingAllowed = false\n        root.add(outputSlots, 5.5, 1.3)\n\n        root.validate(this)\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"sawmill_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SlaughterScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WSlider\nimport io.github.cottonmc.cotton.gui.widget.WSprite\nimport io.github.cottonmc.cotton.gui.widget.data.Axis\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport me.steven.indrev.blockentities.farms.AOEMachineBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.SLAUGHTER_HANDLER\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.gui.widgets.misc.WTooltipedItemSlot\nimport me.steven.indrev.inventories.IRInventory\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport me.steven.indrev.utils.setIcon\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.TranslatableText\n\nclass SlaughterScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        SLAUGHTER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    private var value = -1\n\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.slaughter\", ctx, playerInventory, blockInventory)\n\n        val outputFrame = WSprite(identifier(\"textures/gui/output_frame.png\"))\n        root.add(outputFrame, 5.1, 0.7)\n        outputFrame.setSize(58, 62)\n\n        val outputSlot = WTooltipedItemSlot.of(\n            blockInventory,\n            (blockInventory as IRInventory).outputSlots.first(),\n            3,\n            3,\n            TranslatableText(\"gui.indrev.output_slot_type\")\n        )\n        outputSlot.isInsertingAllowed = false\n        root.add(outputSlot, 5.2, 1.0)\n\n        val swordSlot = WTooltipedItemSlot.of(blockInventory, 1, TranslatableText(\"gui.indrev.slaughter_input_sword\"))\n        swordSlot.setIcon(ctx, blockInventory, 1, SWORD_ICON)\n        root.add(swordSlot, 2.5, 1.5)\n\n        val slider = WSlider(1, 10, Axis.HORIZONTAL)\n        root.add(slider, 1.6, 3.1)\n        slider.setSize(50, 20)\n        ctx.run { world, pos ->\n            val blockEntity = world.getBlockEntity(pos) as? AOEMachineBlockEntity<*> ?: return@run\n            slider.value = blockEntity.range\n        }\n        slider.setValueChangeListener { newValue -> this.value = newValue }\n\n        val text = WText({\n            TranslatableText(\"block.indrev.aoe.range\", slider.value)\n        }, HorizontalAlignment.LEFT)\n        root.add(text, 1.8, 2.8)\n\n        root.validate(this)\n    }\n\n    override fun close(player: PlayerEntity?) {\n        super.close(player)\n        AOEMachineBlockEntity.sendValueUpdatePacket(value, ctx)\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"slaughter_screen\")\n        val SWORD_ICON = identifier(\"textures/gui/sword_icon.png\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SmelterScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.SmelterBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.SMELTER_HANDLER\nimport me.steven.indrev.gui.widgets.machines.fluidTank\nimport me.steven.indrev.gui.widgets.machines.processBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass SmelterScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        SMELTER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.smelter\", ctx, playerInventory, blockInventory)\n\n        val inputSlot = WItemSlot.of(blockInventory, 2)\n        root.add(inputSlot, 3.5, 1.8)\n\n        withBlockEntity<SmelterBlockEntity> { be ->\n            val processWidget = processBar(be, SmelterBlockEntity.CRAFTING_COMPONENT_ID)\n            root.add(processWidget, 4.8, 1.8)\n\n            val fluid = fluidTank(be, SmelterBlockEntity.TANK_ID)\n            root.add(fluid, 6.2, 1.0)\n        }\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"smelter\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SolarGeneratorScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport me.steven.indrev.blockentities.generators.SolarGeneratorBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.SOLAR_GENERATOR_HANDLER\nimport me.steven.indrev.gui.widgets.misc.WDynamicSprite\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.TranslatableText\n\nclass SolarGeneratorScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        SOLAR_GENERATOR_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.solar_generator\", ctx, playerInventory, blockInventory)\n\n        ctx.run { world, pos ->\n            val blockEntity = world.getBlockEntity(pos) as? SolarGeneratorBlockEntity ?: return@run\n            val sprite = WDynamicSprite { if (blockEntity.shouldGenerate()) GENERATING_ICON else NOT_GENERATING_ICON }\n            root.add(sprite, 4.5, 2.0)\n            sprite.setSize(19, 10)\n\n            val text = WText({\n                if (blockEntity.shouldGenerate()) TranslatableText(\"gui.indrev.solar.on\")\n                else TranslatableText(\"gui.indrev.heatgen.idle\")\n            }, HorizontalAlignment.CENTER, 0x404040)\n            root.add(text, 5.0, 2.7)\n        }\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"solar_generator\")\n        private val GENERATING_ICON = identifier(\"textures/gui/sun_icon.png\")\n        private val NOT_GENERATING_ICON = identifier(\"textures/gui/sun_unlit_icon.png\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SolarPowerPlantTowerScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WLabel\nimport me.steven.indrev.blockentities.solarpowerplant.SolarPowerPlantTowerBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.SOLAR_POWER_PLANT_TOWER_HANDLER\nimport me.steven.indrev.gui.widgets.machines.fluidTank\nimport me.steven.indrev.gui.widgets.machines.temperatureBar\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass SolarPowerPlantTowerScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        SOLAR_POWER_PLANT_TOWER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n\n        root.add(WLabel(\"Solar Power Plant Tower\"), 0, 0)\n\n        withBlockEntity<SolarPowerPlantTowerBlockEntity> { be ->\n            val wFluid = fluidTank(be, SolarPowerPlantTowerBlockEntity.INPUT_TANK_ID)\n            root.add(wFluid, 8, 0)\n            wFluid.setLocation(8 * 18, 8)\n            val wTemp = temperatureBar(be)\n            root.add(wTemp, 0, 0)\n            wTemp.setLocation(0, 8)\n\n        }\n\n        val inventoryPanel = createPlayerInventoryPanel()\n        root.add(inventoryPanel, 0, 4)\n        inventoryPanel.setLocation(0, 4 * 18 + 9)\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"solar_power_plant_tower\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SolidInfuserFactoryScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.SolidInfuserFactoryBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.SOLID_INFUSER_FACTORY_HANDLER\nimport me.steven.indrev.gui.widgets.machines.upProcessBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass SolidInfuserFactoryScreenHandler(\n    syncId: Int,\n    playerInventory: PlayerInventory,\n    ctx: ScreenHandlerContext\n) :\n    IRGuiScreenHandler(\n        SOLID_INFUSER_FACTORY_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.solid_infuser_factory\", ctx, playerInventory, blockInventory, invPos = 4.85, widgetPos = 0.5)\n        withBlockEntity<SolidInfuserFactoryBlockEntity> { blockEntity ->\n            val offset = 2.2\n\n            for (index in blockEntity.inventoryComponent!!.inventory.inputSlots.indices step 2) {\n                val slot = blockEntity.inventoryComponent!!.inventory.inputSlots[index]\n                val inputSlot = WItemSlot.of(blockInventory, slot, 1, 2)\n                root.add(inputSlot, offset + ((index * 1.4) / 2), 0.6)\n            }\n\n            for (i in 0 until 5) {\n                val processWidget = upProcessBar(blockEntity, SolidInfuserFactoryBlockEntity.CRAFTING_COMPONENT_START_ID + i)\n                root.add(processWidget, offset + (i * 1.4), 2.7)\n            }\n\n            for ((index, slot) in blockEntity.inventoryComponent!!.inventory.outputSlots.withIndex()) {\n                val outputSlot = WItemSlot.of(blockInventory, slot)\n                root.add(outputSlot, offset + (index * 1.4), 3.8)\n                outputSlot.addChangeListener { _, _, _, _ ->\n                    val player = playerInventory.player\n                    if (!player.world.isClient) {\n                        blockEntity.dropExperience(player)\n                    }\n                }\n                outputSlot.isInsertingAllowed = false\n            }\n\n        }\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"infuser_factory_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SolidInfuserScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport me.steven.indrev.blockentities.crafters.SolidInfuserBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.SOLID_INFUSER_HANDLER\nimport me.steven.indrev.gui.widgets.machines.WCustomBar\nimport me.steven.indrev.gui.widgets.machines.processBar\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass SolidInfuserScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        SOLID_INFUSER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.solid_infuser\", ctx, playerInventory, blockInventory)\n\n        val firstInput = WItemSlot.of(blockInventory, 2)\n        root.add(firstInput, 2.9, 1.8)\n\n        val secondInput = WItemSlot.of(blockInventory, 3)\n        root.add(secondInput, 4.0, 1.8)\n\n        val processWidget = query<SolidInfuserBlockEntity, WCustomBar> { be -> processBar(be, SolidInfuserBlockEntity.CRAFTING_COMPONENT_ID) }\n        root.add(processWidget, 5.15, 1.8)\n\n        val outputSlot = WItemSlot.outputOf(blockInventory, 4)\n        outputSlot.isInsertingAllowed = false\n        root.add(outputSlot, 6.6, 1.8)\n\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"infuser\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/machines/SteamTurbineScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.machines\n\nimport io.github.cottonmc.cotton.gui.widget.WDynamicLabel\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport me.steven.indrev.blockentities.generators.SteamTurbineBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.STEAM_TURBINE_HANDLER\nimport me.steven.indrev.gui.widgets.misc.WKnob\nimport me.steven.indrev.utils.configure\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\n\nclass SteamTurbineScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        STEAM_TURBINE_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        configure(\"block.indrev.steam_turbine_mk4\", ctx, playerInventory, blockInventory)\n\n        ctx.run { world, pos ->\n            val blockEntity = world.getBlockEntity(pos) as? SteamTurbineBlockEntity ?: return@run\n\n            root.add(WDynamicLabel { \"  Efficiency: \" + String.format(\"%.2f\", blockEntity.efficiency) }, 1, 1)\n            root.add(WDynamicLabel {\n                val a = blockEntity.consuming.asInt(1000).coerceAtLeast(1)\n                val inexact = blockEntity.consuming.asInt(1000)\n                val prefix = if (inexact > a) \">\" else if (inexact < a) \"<\" else \"\"\n                \"  Consuming: $prefix$a mB\"\n            }, 1, 2)\n            root.add(WDynamicLabel { \"  Generating: ${propertyDelegate[SteamTurbineBlockEntity.GENERATING] / 100.0}\" }, 1, 3)\n\n            //root.add(WFluid(ctx, 0), -1, 0)\n\n            val wKnob = WKnob((propertyDelegate[SteamTurbineBlockEntity.EFFICIENCY] * 3f) + 30f, pos)\n            root.add(wKnob, 7, 4)\n\n            wKnob.setSize(32, 32)\n            wKnob.setLocation(7 * 18, 2 * 18 + 9)\n        }\n        root.validate(this)\n    }\n\n    override fun canUse(player: PlayerEntity?): Boolean = true\n\n    companion object {\n        val SCREEN_ID = identifier(\"steam_turbine\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/modular/ModularItemConfigurationScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.modular\n\nimport io.github.cottonmc.cotton.gui.client.LightweightGuiDescription\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.TooltipBuilder\nimport io.github.cottonmc.cotton.gui.widget.WBox\nimport io.github.cottonmc.cotton.gui.widget.WSlider\nimport io.github.cottonmc.cotton.gui.widget.WWidget\nimport io.github.cottonmc.cotton.gui.widget.data.Axis\nimport io.github.cottonmc.cotton.gui.widget.icon.Icon\nimport io.netty.buffer.Unpooled\nimport me.steven.indrev.WCustomTabPanel\nimport me.steven.indrev.gui.widgets.misc.WPlayerRender\nimport me.steven.indrev.gui.widgets.misc.WStaticTooltip\nimport me.steven.indrev.items.energy.IRGamerAxeItem\nimport me.steven.indrev.packets.common.UpdateModularToolLevelPacket\nimport me.steven.indrev.tools.modular.IRModularItem\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.item.Item\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.registry.Registry\n\nclass ModularItemConfigurationScreenHandler(playerInventory: PlayerInventory) : LightweightGuiDescription() {\n\n    val iconProvider: (Item) -> Icon? = { item ->\n        val id = Registry.ITEM.getId(item)\n        val textureId = Identifier(id.namespace, \"textures/item/${id.path}.png\")\n        Icon { _, x, y, size ->\n            if (item is IRGamerAxeItem)\n                ScreenDrawing.texturedRect(MatrixStack(), x, y, size, size, textureId, 0f, 0f, 1f, 0.14285714f, -1)\n            else\n                ScreenDrawing.texturedRect(MatrixStack(), x, y, size, size, textureId, -1)\n        }\n    }\n\n    init {\n        val root = WCustomTabPanel()\n        this.rootPanel = root\n\n        (playerInventory.size() - 1 downTo 0)\n            .associateWith { slot -> playerInventory.getStack(slot) }\n            .filter { (_, stack) -> stack.item is IRModularItem<*> }\n            .forEach { (slot, stack) ->\n                val tabPanel = WBox(Axis.HORIZONTAL)\n                tabPanel.spacing = 10\n                val item = stack.item as IRModularItem<*>\n                item.getInstalled(stack).forEach { module ->\n                    val moduleItem = module.item.asItem()\n                    val icon = iconProvider(moduleItem!!)\n                    val moduleBox = WBox(Axis.VERTICAL)\n                    moduleBox.add(object : WWidget() {\n                        override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n                            icon?.paint(matrices, x, y, width)\n                        }\n\n                        override fun addTooltip(tooltip: TooltipBuilder?) {\n                            tooltip?.add(TranslatableText(moduleItem.translationKey))\n                        }\n                    })\n                    val maxLevel = module.getMaxInstalledLevel(stack)\n                    val slider = object : WSlider(0, maxLevel, Axis.HORIZONTAL) {\n                        override fun addTooltip(tooltip: TooltipBuilder?) {\n                            tooltip?.add(LiteralText(\"${value * 100 / maxLevel}%\"))\n                        }\n                    }\n                    slider.value = module.getLevel(stack)\n                    slider.setDraggingFinishedListener { value ->\n                        val buf = PacketByteBuf(Unpooled.buffer())\n                        buf.writeString(module.key)\n                        buf.writeInt(value)\n                        buf.writeInt(slot)\n                        ClientPlayNetworking.send(UpdateModularToolLevelPacket.UPDATE_MODULAR_TOOL_LEVEL, buf)\n                    }\n                    moduleBox.add(slider)\n                    tabPanel.add(moduleBox)\n                }\n\n                tabPanel.setSize(tabPanel.width + 65, tabPanel.height)\n\n                root.add(\n                    WCustomTabPanel.Tab(\n                        null,\n                        iconProvider(stack.item),\n                        tabPanel,\n                        { it.add(TranslatableText(stack.item.translationKey)) })\n                )\n            }\n\n        val playerBg = WStaticTooltip()\n        root.add(playerBg, -50, 4)\n        playerBg.setSize(40, 65)\n\n        val playerWidget = WPlayerRender()\n        root.add(playerWidget, -30, 64)\n\n        root.validate(this)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/pipes/PipeFilterScreen.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.pipes\n\nimport io.github.cottonmc.cotton.gui.client.CottonInventoryScreen\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.text.LiteralText\n\nclass PipeFilterScreen(val screenHandler: PipeFilterScreenHandler, val player: PlayerEntity) : CottonInventoryScreen<PipeFilterScreenHandler>(screenHandler, player, LiteralText(\"Item Pipe Filter\"))"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/pipes/PipeFilterScreenFactory.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.pipes\n\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.item.ItemNetworkState\nimport net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.screen.ScreenHandler\nimport net.minecraft.server.network.ServerPlayerEntity\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\n\nclass PipeFilterScreenFactory(\n    private val handlerFactory: (Int, PlayerInventory) -> ScreenHandler,\n    private val pos: BlockPos,\n    private val dir: Direction\n) : ExtendedScreenHandlerFactory {\n    override fun createMenu(syncId: Int, inv: PlayerInventory?, player: PlayerEntity?): ScreenHandler {\n        return handlerFactory(syncId, inv!!)\n    }\n\n    override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) {\n        (Network.Type.ITEM.getNetworkState(player.world as ServerWorld) as? ItemNetworkState)?.let { state ->\n            val data = state.getEndpointData(pos, dir, false)\n            val filterData = state.getFilterData(pos, dir, true)\n            buf.writeEnumConstant(dir)\n            buf.writeBlockPos(pos)\n            filterData.filter.forEach { buf.writeItemStack(it) }\n            buf.writeBoolean(filterData.whitelist)\n            buf.writeBoolean(filterData.matchDurability)\n            buf.writeBoolean(filterData.matchTag)\n            buf.writeBoolean(data != null)\n            if (data != null) {\n                buf.writeEnumConstant(data.type)\n                buf.writeEnumConstant(data.mode)\n            }\n        }\n    }\n\n    override fun getDisplayName(): Text = LiteralText(\"Item Filter\")\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/pipes/PipeFilterScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.pipes\n\nimport io.github.cottonmc.cotton.gui.SyncedGuiDescription\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.*\nimport io.github.cottonmc.cotton.gui.widget.data.InputResult\nimport io.github.cottonmc.cotton.gui.widget.data.Insets\nimport me.steven.indrev.gui.screenhandlers.PIPE_FILTER_HANDLER\nimport me.steven.indrev.networks.EndpointData\nimport me.steven.indrev.packets.common.ItemPipePackets\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.fabricmc.fabric.api.networking.v1.PacketByteBufs\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.gui.DrawableHelper\nimport net.minecraft.client.sound.PositionedSoundInstance\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.item.ItemStack\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.sound.SoundEvents\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.collection.DefaultedList\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport java.util.*\nimport java.util.function.Consumer\n\nclass PipeFilterScreenHandler(\n    syncId: Int, playerInventory: PlayerInventory,\n    whitelist: Boolean = false,\n    matchDurability: Boolean = false,\n    matchTag: Boolean = false,\n    var mode: EndpointData.Mode? = null,\n    val type: EndpointData.Type? = null\n) : SyncedGuiDescription(PIPE_FILTER_HANDLER, syncId, playerInventory) {\n\n    val backingList = DefaultedList.ofSize(9, ItemStack.EMPTY)\n    lateinit var direction: Direction\n    lateinit var blockPos: BlockPos\n\n    init {\n        val root = WGridPanel()\n        this.rootPanel = root\n        root.insets = Insets.ROOT_PANEL\n\n        (0 until backingList.size).forEach { index ->\n            val slot = WFilterSlot(index)\n            root.add(slot, 1 * index, 1)\n        }\n\n        val whitelistButton = object : WToggleButton(WHITELIST_ICON, BLACKLIST_ICON) {\n\n            override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n                WButton().also { it.setSize(20, 20) }.paint(matrices, x, y, mouseX, mouseY)\n                super.paint(matrices, x + 1, y + 1, mouseX, mouseY)\n            }\n\n            override fun addTooltip(tooltip: TooltipBuilder?) {\n                tooltip?.add(TranslatableText(\"gui.indrev.whitelist.$isOn\"))\n            }\n        }\n        whitelistButton.onToggle = Consumer { value ->\n            val buf = PacketByteBufs.create()\n            writeIdentifyingData(buf)\n            buf.writeInt(0)\n            buf.writeBoolean(value)\n            ClientPlayNetworking.send(ItemPipePackets.CHANGE_FILTER_MODE_PACKET, buf)\n        }\n        whitelistButton.toggle = whitelist\n        root.add(whitelistButton, 2, 2)\n        whitelistButton.setLocation(2 * 18 + root.insets.left, 3 * 18)\n\n        val matchDurabilityButton = object : WToggleButton(MATCH_DURABILITY_ICON, IGNORE_DURABILITY_ICON) {\n\n            override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n                WButton().also { it.setSize(20, 20) }.paint(matrices, x, y, mouseX, mouseY)\n                super.paint(matrices, x + 1, y + 1, mouseX, mouseY)\n            }\n\n            override fun addTooltip(tooltip: TooltipBuilder?) {\n                tooltip?.add(TranslatableText(\"gui.indrev.matchDurability.$isOn\"))\n            }\n        }\n        matchDurabilityButton.onToggle = Consumer { value ->\n            val buf = PacketByteBufs.create()\n            writeIdentifyingData(buf)\n            buf.writeInt(1)\n            buf.writeBoolean(value)\n            ClientPlayNetworking.send(ItemPipePackets.CHANGE_FILTER_MODE_PACKET, buf)\n        }\n        matchDurabilityButton.toggle = matchDurability\n        root.add(matchDurabilityButton, 4, 2)\n        matchDurabilityButton.setLocation(4 * 18 + root.insets.left, 3 * 18)\n\n        val matchTagButton = object : WToggleButton(MATCH_NBT_ICON, IGNORE_NBT_ICON) {\n\n            override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n                WButton().also { it.setSize(20, 20) }.paint(matrices, x, y, mouseX, mouseY)\n                super.paint(matrices, x + 1, y + 1, mouseX, mouseY)\n            }\n            \n            override fun addTooltip(tooltip: TooltipBuilder?) {\n                tooltip?.add(TranslatableText(\"gui.indrev.matchTag.$isOn\"))\n            }\n        }\n        matchTagButton.onToggle = Consumer { value ->\n            val buf = PacketByteBufs.create()\n            writeIdentifyingData(buf)\n            buf.writeInt(2)\n            buf.writeBoolean(value)\n            ClientPlayNetworking.send(ItemPipePackets.CHANGE_FILTER_MODE_PACKET, buf)\n        }\n        matchTagButton.toggle = matchTag\n        root.add(matchTagButton, 6, 3)\n        matchTagButton.setLocation(6 * 18 + root.insets.left, 3 * 18)\n\n        if (mode != null && type != null) {\n            val modeWidget = WServoMode()\n            root.add(modeWidget, 8, 0)\n            modeWidget.setSize(18, 9)\n        }\n\n        val panel = createPlayerInventoryPanel()\n        root.add(panel, 0, 4)\n\n        root.validate(this)\n    }\n\n    private fun writeIdentifyingData(buf: PacketByteBuf) {\n        buf.writeEnumConstant(direction)\n        buf.writeBlockPos(blockPos)\n    }\n\n    inner class WFilterSlot(val index: Int) : WWidget() {\n\n        override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n            ScreenDrawing.drawBeveledPanel(matrices, x, y, height, width, -1207959552, 1275068416, -1191182337)\n            MinecraftClient.getInstance().itemRenderer.renderInGui(backingList[index], x + 1, y + 1)\n            MinecraftClient.getInstance().itemRenderer.renderGuiItemOverlay(MinecraftClient.getInstance().textRenderer, backingList[index], x + 1, y + 1)\n            if (mouseX >= 0 && mouseY >= 0 && mouseX < width && mouseY < height)\n                DrawableHelper.fill(matrices, x + 1, y + 1, x + 17, y + 17, -2130706433)\n        }\n\n        override fun onClick(x: Int, y: Int, button: Int): InputResult {\n            val buf = PacketByteBufs.create()\n            buf.writeInt(index)\n            writeIdentifyingData(buf)\n            ClientPlayNetworking.send(ItemPipePackets.CLICK_FILTER_SLOT_PACKET, buf)\n\n            return InputResult.PROCESSED\n        }\n\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            val itemStack = backingList[index]\n            if (!itemStack.isEmpty)\n                tooltip?.add(*itemStack.getTooltip(playerInventory.player) { MinecraftClient.getInstance().options.advancedItemTooltips }.toTypedArray())\n        }\n    }\n\n    inner class WServoMode : WWidget() {\n\n        override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n            if (type == EndpointData.Type.OUTPUT)\n                MinecraftClient.getInstance().itemRenderer.renderInGui(ItemStack(IRItemRegistry.SERVO_OUTPUT), x + 1, y - 2)\n            else if (type == EndpointData.Type.RETRIEVER)\n                MinecraftClient.getInstance().itemRenderer.renderInGui(ItemStack(IRItemRegistry.SERVO_RETRIEVER), x + 1, y - 2)\n        }\n\n        override fun onClick(x: Int, y: Int, button: Int): InputResult {\n            mode = mode?.next() ?: return InputResult.IGNORED\n            MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(SoundEvents.UI_BUTTON_CLICK, 1.0f))\n            val buf = PacketByteBufs.create()\n            writeIdentifyingData(buf)\n            buf.writeEnumConstant(mode)\n            ClientPlayNetworking.send(ItemPipePackets.CHANGE_SERVO_MODE_PACKET, buf)\n            return InputResult.PROCESSED\n        }\n\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            tooltip?.add(\n                TranslatableText(\"item.indrev.servo.mode\")\n                .append(TranslatableText(\"item.indrev.servo.mode.${mode.toString().lowercase(Locale.getDefault())}\").formatted(Formatting.BLUE)))\n        }\n    }\n\n    companion object {\n\n        val WHITELIST_ICON = identifier(\"textures/gui/filter_whitelist.png\")\n        val BLACKLIST_ICON = identifier(\"textures/gui/filter_blacklist.png\")\n        val IGNORE_DURABILITY_ICON = identifier(\"textures/gui/filter_ignore_durability.png\")\n        val MATCH_DURABILITY_ICON = identifier(\"textures/gui/filter_match_durability.png\")\n        val IGNORE_NBT_ICON = identifier(\"textures/gui/filter_ignore_nbt.png\")\n        val MATCH_NBT_ICON = identifier(\"textures/gui/filter_match_nbt.png\")\n\n        val SCREEN_ID = identifier(\"pipe_filter_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/screenhandlers.kt",
    "content": "package me.steven.indrev.gui.screenhandlers\n\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.api.sideconfigs.SideConfiguration\nimport me.steven.indrev.gui.screenhandlers.machines.*\nimport me.steven.indrev.gui.screenhandlers.pipes.PipeFilterScreenHandler\nimport me.steven.indrev.gui.screenhandlers.storage.CabinetScreenHandler\nimport me.steven.indrev.gui.screenhandlers.wrench.ScrewdriverScreenHandler\nimport me.steven.indrev.networks.EndpointData\nimport me.steven.indrev.utils.registerScreenHandler\nimport net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType\nimport net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.util.math.Direction\nimport java.util.*\n\nval COAL_GENERATOR_HANDLER = CoalGeneratorScreenHandler.SCREEN_ID.registerScreenHandler(::CoalGeneratorScreenHandler)\nval SOLAR_GENERATOR_HANDLER = SolarGeneratorScreenHandler.SCREEN_ID.registerScreenHandler(::SolarGeneratorScreenHandler)\nval BIOMASS_GENERATOR_HANDLER = BiomassGeneratorScreenHandler.SCREEN_ID.registerScreenHandler(::BiomassGeneratorScreenHandler)\nval HEAT_GENERATOR_HANDLER = HeatGeneratorScreenHandler.SCREEN_ID.registerScreenHandler(::HeatGeneratorScreenHandler)\nval GAS_BURNING_GENERATOR_HANDLER = GasBurningGeneratorScreenHandler.SCREEN_ID.registerScreenHandler(::GasBurningGeneratorScreenHandler)\nval BATTERY_HANDLER = LazuliFluxContainerScreenHandler.SCREEN_ID.registerScreenHandler(::LazuliFluxContainerScreenHandler)\nval ELECTRIC_FURNACE_HANDLER = ElectricFurnaceScreenHandler.SCREEN_ID.registerScreenHandler(::ElectricFurnaceScreenHandler)\nval PULVERIZER_HANDLER = PulverizerScreenHandler.SCREEN_ID.registerScreenHandler(::PulverizerScreenHandler)\nval PUMP_HANDLER = PumpScreenHandler.SCREEN_ID.registerScreenHandler(::PumpScreenHandler)\nval COMPRESSOR_HANDLER = CompressorScreenHandler.SCREEN_ID.registerScreenHandler(::CompressorScreenHandler)\nval SOLID_INFUSER_HANDLER = SolidInfuserScreenHandler.SCREEN_ID.registerScreenHandler(::SolidInfuserScreenHandler)\nval RECYCLER_HANDLER = RecyclerScreenHandler.SCREEN_ID.registerScreenHandler(::RecyclerScreenHandler)\nval CHOPPER_HANDLER = ChopperScreenHandler.SCREEN_ID.registerScreenHandler(::ChopperScreenHandler)\nval RANCHER_HANDLER = RancherScreenHandler.SCREEN_ID.registerScreenHandler(::RancherScreenHandler)\nval MINING_RIG_HANDLER = MiningRigComputerScreenHandler.SCREEN_ID.registerScreenHandler(::MiningRigComputerScreenHandler)\nval FISHER_HANDLER = FisherScreenHandler.SCREEN_ID.registerScreenHandler(::FisherScreenHandler)\nval MODULAR_WORKBENCH_HANDLER =\n    ModularWorkbenchScreenHandler.SCREEN_ID.registerScreenHandler(::ModularWorkbenchScreenHandler)\nval SMELTER_HANDLER = SmelterScreenHandler.SCREEN_ID.registerScreenHandler(::SmelterScreenHandler)\nval CONDENSER_HANDLER = CondenserScreenHandler.SCREEN_ID.registerScreenHandler(::CondenserScreenHandler)\nval FLUID_INFUSER_HANDLER = FluidInfuserScreenHandler.SCREEN_ID.registerScreenHandler(::FluidInfuserScreenHandler)\nval FARMER_HANDLER = FarmerScreenHandler.SCREEN_ID.registerScreenHandler(::FarmerScreenHandler)\nval SLAUGHTER_HANDLER = SlaughterScreenHandler.SCREEN_ID.registerScreenHandler(::SlaughterScreenHandler)\nval SAWMILL_HANDLER = SawmillScreenHandler.SCREEN_ID.registerScreenHandler(::SawmillScreenHandler)\nval LASER_HANDLER = LaserEmitterScreenHandler.SCREEN_ID.registerScreenHandler(::LaserEmitterScreenHandler)\nval ELECTROLYTIC_SEPARATOR_HANDLER = ElectrolyticSeparatorScreenHandler.SCREEN_ID.registerScreenHandler(::ElectrolyticSeparatorScreenHandler)\nval DATA_CARD_WRITER_HANDLER = DataCardWriterScreenHandler.SCREEN_ID.registerScreenHandler(::DataCardWriterScreenHandler)\n\nval ELECTRIC_FURNACE_FACTORY_HANDLER = ElectricFurnaceFactoryScreenHandler.SCREEN_ID.registerScreenHandler(::ElectricFurnaceFactoryScreenHandler)\nval PULVERIZER_FACTORY_HANDLER = PulverizerFactoryScreenHandler.SCREEN_ID.registerScreenHandler(::PulverizerFactoryScreenHandler)\nval COMPRESSOR_FACTORY_HANDLER = CompressorFactoryScreenHandler.SCREEN_ID.registerScreenHandler(::CompressorFactoryScreenHandler)\nval SOLID_INFUSER_FACTORY_HANDLER = SolidInfuserFactoryScreenHandler.SCREEN_ID.registerScreenHandler(::SolidInfuserFactoryScreenHandler)\n\nval STEAM_TURBINE_HANDLER = SteamTurbineScreenHandler.SCREEN_ID.registerScreenHandler(::SteamTurbineScreenHandler)\nval SOLAR_POWER_PLANT_TOWER_HANDLER = SolarPowerPlantTowerScreenHandler.SCREEN_ID.registerScreenHandler(::SolarPowerPlantTowerScreenHandler)\n\nval PIPE_FILTER_HANDLER = ScreenHandlerRegistry.registerExtended(PipeFilterScreenHandler.SCREEN_ID) { syncId, inv, buf ->\n    val dir = buf.readEnumConstant(Direction::class.java)\n    val pos = buf.readBlockPos()\n    val list = (0 until 9).map { buf.readItemStack() }\n    val whitelist = buf.readBoolean()\n    val matchDurability = buf.readBoolean()\n    val matchTag = buf.readBoolean()\n    val hasServo = buf.readBoolean()\n    val type = if (hasServo) buf.readEnumConstant(EndpointData.Type::class.java) else null\n    val mode = if (hasServo) buf.readEnumConstant(EndpointData.Mode::class.java) else null\n\n    val controller = PipeFilterScreenHandler(syncId, inv, whitelist, matchDurability, matchTag, mode, type)\n    controller.direction = dir\n    controller.blockPos = pos\n    list.forEachIndexed { index, itemStack -> controller.backingList[index] = itemStack }\n    controller\n} as ExtendedScreenHandlerType<PipeFilterScreenHandler>\n\nval DRILL_HANDLER = MiningRigDrillScreenHandler.SCREEN_ID.registerScreenHandler(::MiningRigDrillScreenHandler)\n\nval SCREWDRIVER_HANDLER = ScreenHandlerRegistry.registerExtended(ScrewdriverScreenHandler.SCREEN_ID) { syncId, inv, buf ->\n    val pos = buf.readBlockPos()\n    val itemConfig = SideConfiguration(ConfigurationType.ITEM)\n    if (buf.readBoolean())\n        itemConfig.readBuf(buf)\n    val fluidConfig = SideConfiguration(ConfigurationType.FLUID)\n    if (buf.readBoolean())\n        fluidConfig.readBuf(buf)\n    val energyConfig = SideConfiguration(ConfigurationType.ENERGY)\n    if (buf.readBoolean())\n        energyConfig.readBuf(buf)\n\n    val map = EnumMap<ConfigurationType, SideConfiguration>(ConfigurationType::class.java)\n    map[ConfigurationType.ITEM] = itemConfig\n    map[ConfigurationType.FLUID] = fluidConfig\n    map[ConfigurationType.ENERGY] = energyConfig\n    ScrewdriverScreenHandler(syncId, inv, ScreenHandlerContext.create(inv.player.world, pos), map)\n} as ExtendedScreenHandlerType<ScrewdriverScreenHandler>\n\nval CABINET_HANDLER = CabinetScreenHandler.SCREEN_ID.registerScreenHandler(::CabinetScreenHandler)"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/storage/CabinetScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.storage\n\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport me.steven.indrev.gui.screenhandlers.CABINET_HANDLER\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.TranslatableText\n\nclass CabinetScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext) :\n    IRGuiScreenHandler(\n        CABINET_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n\n        root.add(WText(TranslatableText(\"block.indrev.cabinet\"), HorizontalAlignment.LEFT, 0x404040), 0.0, -0.1)\n\n        root.add(WItemSlot.of(blockInventory, 0, 9, 3), 0.0, 0.6)\n\n        root.add(createPlayerInventoryPanel(), 0.0, 3.8)\n\n        root.validate(this)\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"cabinet\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/screenhandlers/wrench/ScrewdriverScreenHandler.kt",
    "content": "package me.steven.indrev.gui.screenhandlers.wrench\n\nimport io.github.cottonmc.cotton.gui.client.BackgroundPainter\nimport io.github.cottonmc.cotton.gui.widget.WButton\nimport io.github.cottonmc.cotton.gui.widget.WGridPanel\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport io.github.cottonmc.cotton.gui.widget.data.Insets\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.api.sideconfigs.SideConfiguration\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.gui.screenhandlers.SCREWDRIVER_HANDLER\nimport me.steven.indrev.gui.widgets.misc.WText\nimport me.steven.indrev.utils.add\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.server.network.ServerPlayerEntity\nimport net.minecraft.text.TranslatableText\nimport java.util.*\n\nclass ScrewdriverScreenHandler(syncId: Int, playerInventory: PlayerInventory, ctx: ScreenHandlerContext, val configs: EnumMap<ConfigurationType, SideConfiguration>) :\n    IRGuiScreenHandler(\n        SCREWDRIVER_HANDLER,\n        syncId,\n        playerInventory,\n        ctx\n    ) {\n\n    private lateinit var currentType: ConfigurationType\n\n    init {\n        val root = WGridPanel()\n        setRootPanel(root)\n        root.setSize(100, 128)\n        root.insets = Insets.ROOT_PANEL\n\n        val titleText = WText(TranslatableText(\"item.indrev.wrench.title\"), HorizontalAlignment.LEFT, 0x404040)\n        root.add(titleText, 0.3, 0.4)\n        ctx.run { world, pos ->\n            val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return@run\n\n            val availableTypes = ConfigurationType.getTypes(blockEntity)\n            currentType = availableTypes.first()\n            var widget = configs[currentType]?.getConfigurationPanel(world, pos, blockEntity, playerInventory, currentType) ?: return@run\n            val configY = if (availableTypes.size > 1) 2 else 1\n            root.add(widget, 0, configY)\n            val configTypeButton = WButton(currentType.title)\n            configTypeButton.setOnClick {\n                currentType = currentType.next(availableTypes)\n                configTypeButton.label = currentType.title\n                root.remove(widget)\n                widget = configs[currentType]?.getConfigurationPanel(world, pos, blockEntity, playerInventory, currentType) ?: return@setOnClick\n                root.add(widget, 0, configY)\n                root.validate(this)\n            }\n            if (availableTypes.size > 1)\n                root.add(configTypeButton, 1.6, 1.0)\n            configTypeButton.setSize(45, 20)\n\n        }\n        root.validate(this)\n    }\n\n    override fun close(player: PlayerEntity?) {\n        super.close(player)\n        if (player is ServerPlayerEntity) {\n            ctx.run { world, pos ->\n                val blockEntity = world.getBlockEntity(pos) as? LazuliFluxContainerBlockEntity ?: return@run\n                blockEntity.sync()\n                world.updateNeighbors(pos, blockEntity.cachedState.block)\n            }\n        }\n    }\n\n    override fun addPainters() {\n        super.addPainters()\n        rootPanel.backgroundPainter = BackgroundPainter.VANILLA\n    }\n\n    companion object {\n        val SCREEN_ID = identifier(\"screwdriver_config_screen\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/tooltip/energy/EnergyTooltipComponent.kt",
    "content": "package me.steven.indrev.gui.tooltip.energy\n\nimport com.mojang.blaze3d.systems.RenderSystem\nimport me.steven.indrev.utils.getEnergyString\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.font.TextRenderer\nimport net.minecraft.client.gui.DrawableHelper\nimport net.minecraft.client.gui.tooltip.TooltipComponent\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.item.ItemRenderer\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.text.LiteralText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.math.Matrix4f\n\nopen class EnergyTooltipComponent(private val data: EnergyTooltipData) : TooltipComponent {\n    override fun getHeight(): Int = 18\n\n    override fun getWidth(textRenderer: TextRenderer): Int {\n        val percentage = if (data.maxEnergy > 0) data.energy * 100 / data.maxEnergy else 0\n        val text =\n            LiteralText(\"${getEnergyString(data.energy)} LF (${percentage.toInt()}%)\").formatted(Formatting.GRAY)\n        return textRenderer.getWidth(text) + 20\n    }\n\n    override fun drawItems(\n        textRenderer: TextRenderer,\n        x: Int,\n        y: Int,\n        matrices: MatrixStack,\n        itemRenderer: ItemRenderer,\n        z: Int\n    ) {\n        RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f)\n        RenderSystem.setShaderTexture(0, identifier(\"textures/gui/energy_icon.png\"))\n        DrawableHelper.drawTexture(matrices, x, y, z, 0f, 0f, 18, 18, 18, 18)\n    }\n\n    override fun drawText(\n        textRenderer: TextRenderer,\n        x: Int,\n        y: Int,\n        matrix4f: Matrix4f,\n        immediate: VertexConsumerProvider.Immediate\n    ) {\n        val percentage = if (data.maxEnergy > 0) data.energy * 100 / data.maxEnergy else 0\n        val text =\n            LiteralText(\"${getEnergyString(data.energy)} LF (${percentage.toInt()}%)\").formatted(Formatting.GRAY)\n        textRenderer.draw(text, x.toFloat() + 19, (y.toFloat() + 9) - textRenderer.fontHeight / 2, -1, true, matrix4f, immediate, false, 0, 15728880)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/tooltip/energy/EnergyTooltipData.kt",
    "content": "package me.steven.indrev.gui.tooltip.energy\n\nimport net.minecraft.client.item.TooltipData\n\nopen class EnergyTooltipData(val energy: Long, val maxEnergy: Long) : TooltipData"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/tooltip/modular/ModularTooltipComponent.kt",
    "content": "package me.steven.indrev.gui.tooltip.modular\n\nimport me.steven.indrev.gui.tooltip.energy.EnergyTooltipComponent\nimport net.minecraft.client.font.TextRenderer\nimport net.minecraft.client.gui.screen.Screen\nimport net.minecraft.client.render.item.ItemRenderer\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.item.ItemStack\nimport kotlin.math.ceil\n\nclass ModularTooltipComponent(private val data: ModularTooltipData) : EnergyTooltipComponent(data) {\n\n    override fun getHeight(): Int = if (Screen.hasShiftDown()) 18 else 18 + 18 * (ceil(data.modules.size / 5.0).toInt())\n\n    override fun getWidth(textRenderer: TextRenderer): Int {\n        val energyWidth = super.getWidth(textRenderer)\n        var cX = 0\n        data.modules.forEachIndexed { index, module ->\n            val level = data.levelProvider(module)\n            cX += (level * 5) + 18\n            if (index + 1 % 5 == 0)\n                return cX\n        }\n        return cX.coerceAtLeast(energyWidth)\n    }\n\n    override fun drawItems(\n        textRenderer: TextRenderer,\n        x: Int,\n        y: Int,\n        matrices: MatrixStack,\n        itemRenderer: ItemRenderer,\n        z: Int\n    ) {\n        super.drawItems(textRenderer, x, y, matrices, itemRenderer, z)\n        if (Screen.hasShiftDown()) return\n\n        var cX = x\n        var cY = y + 18\n        data.modules.sortedByDescending { data.levelProvider(it) }.forEachIndexed { index, module ->\n            val level = data.levelProvider(module)\n            cX += level * 5\n            repeat(level) {\n                cX -= 5\n                itemRenderer.renderInGui(ItemStack(module.item.asItem()), cX, cY)\n            }\n            cX += (level * 5) + 18\n            if (index + 1 % 5 == 0) {\n                cY += 18\n                cX = x\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/tooltip/modular/ModularTooltipData.kt",
    "content": "package me.steven.indrev.gui.tooltip.modular\n\nimport me.steven.indrev.gui.tooltip.energy.EnergyTooltipData\nimport me.steven.indrev.tools.modular.Module\n\nclass ModularTooltipData(energy: Long, maxEnergy: Long, val modules: List<Module>, val levelProvider: (Module) -> Int) : EnergyTooltipData(energy, maxEnergy) {\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/tooltip/oredatacards/OreDataCardTooltipComponent.kt",
    "content": "package me.steven.indrev.gui.tooltip.oredatacards\n\nimport me.steven.indrev.api.OreDataCards\nimport me.steven.indrev.utils.drawCircle\nimport net.minecraft.client.font.TextRenderer\nimport net.minecraft.client.gui.tooltip.TooltipComponent\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.render.item.ItemRenderer\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.LiteralText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.math.Matrix4f\n\nclass OreDataCardTooltipComponent(val data: OreDataCardTooltipData) : TooltipComponent {\n    override fun getHeight(): Int {\n        return 18 * (if (data.cardData.rng == 0) 3 else 4) + (18* data.cardData.entries.size / 4) + 18\n    }\n\n    override fun getWidth(textRenderer: TextRenderer): Int {\n        val text = LiteralText(\"Cycle: \").styled { s -> s.withColor(0x007E7E) }.append(LiteralText(\"${data.cardData.speed} ticks (${data.cardData.energyRequired} LF/tick)\").formatted(Formatting.WHITE))\n        return textRenderer.getWidth(text) + 9\n    }\n\n    override fun drawText(\n        textRenderer: TextRenderer,\n        x: Int,\n        y: Int,\n        matrix: Matrix4f?,\n        vertexConsumers: VertexConsumerProvider.Immediate?\n    ) {\n\n        val speed = LiteralText(\"Cycle: \").styled { s -> s.withColor(0x007E7E) }.append(LiteralText(\"${data.cardData.speed} ticks (${data.cardData.energyRequired} LF/tick)\").formatted(Formatting.WHITE))\n        textRenderer.draw(speed, x.toFloat() + 3, y.toFloat() + 4 + 18 * 3 + (18 * (data.cardData.entries.size / 4)),  -1, false, matrix, vertexConsumers, false, 0, 15728880)\n\n        val dataTxt = \"${(data.cardData.maxCycles - data.cardData.used) * 100 / OreDataCards.MAX_SIZE}%\"\n        val width = textRenderer.getWidth(dataTxt)\n        textRenderer.draw(dataTxt, x.toFloat() + textRenderer.getWidth(speed)  - 17- width / 2, y.toFloat() + 15, -1, false, matrix, vertexConsumers, false, 0, 15728880)\n        textRenderer.draw(\"Types\", x.toFloat() + 3, y.toFloat() + 5,  0x007E7E, false, matrix, vertexConsumers, false, 0, 15728880)\n\n        val richness = LiteralText(\"Richness: \").styled { s -> s.withColor(0x007E7E) }.append(LiteralText(\"${(data.cardData.richness * 100).toInt()}%\").formatted(Formatting.WHITE))\n        textRenderer.draw(richness, x.toFloat() + 3, y.toFloat() + 4 + 18 * 2 + (18 * (data.cardData.entries.size / 4)),  -1, false, matrix, vertexConsumers, false, 0, 15728880)\n\n        if (data.cardData.rng == -1) {\n            textRenderer.draw(\"-RNG\", x.toFloat(), y.toFloat() + 16 * 5,  -1, false, matrix, vertexConsumers, false, 0, 15728880)\n        } else if (data.cardData.rng == 1) {\n            textRenderer.draw(\"+RNG\", x.toFloat(), y.toFloat() + 16 * 5,  -1, false, matrix, vertexConsumers, false, 0, 15728880)\n        }\n    }\n\n    override fun drawItems(\n        textRenderer: TextRenderer,\n        x: Int,\n        y: Int,\n        matrices: MatrixStack,\n        itemRenderer: ItemRenderer,\n        z: Int\n    ) {\n\n        val text = LiteralText(\"Cycle: \").styled { s -> s.withColor(0x007E7E) }.append(LiteralText(\"${data.cardData.speed} ticks (${data.cardData.energyRequired} LF/tick)\").formatted(Formatting.WHITE))\n\n        data.cardData.entries.forEachIndexed { index, entry ->\n            itemRenderer.renderInGui(ItemStack(entry.item), x + 6 + (index % 3 * 18), y + 17 + (18 * (index / 3)))\n        }\n\n        matrices.push()\n        matrices.translate(0.0, 0.0, z.toDouble())\n        val width = 18 * 2\n        val offset = textRenderer.getWidth(text) - width\n        drawCircle(matrices, 1,1, x + offset, y, width) { _, _ -> 0x22FFFFFF.toInt() }\n        drawCircle(matrices, data.cardData.maxCycles, OreDataCards.MAX_SIZE, x + offset, y, width) { _, _ -> 0xFF0000FF.toInt() }\n        drawCircle(matrices, data.cardData.used, OreDataCards.MAX_SIZE, x + offset, y, width) { _, _ -> 0xFFFF0000.toInt() }\n        matrices.pop()\n    }\n\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/tooltip/oredatacards/OreDataCardTooltipData.kt",
    "content": "package me.steven.indrev.gui.tooltip.oredatacards\n\nimport me.steven.indrev.api.OreDataCards\nimport net.minecraft.client.item.TooltipData\n\nclass OreDataCardTooltipData(val cardData: OreDataCards.Data) : TooltipData"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/widgets/machines/WCustomBar.kt",
    "content": "package me.steven.indrev.gui.widgets.machines\n\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.TooltipBuilder\nimport io.github.cottonmc.cotton.gui.widget.WWidget\nimport io.github.cottonmc.cotton.gui.widget.data.InputResult\nimport io.github.cottonmc.cotton.gui.widget.data.Texture\nimport io.netty.buffer.Unpooled\nimport me.steven.indrev.blockentities.BaseBlockEntity\nimport me.steven.indrev.components.CraftingComponent\nimport me.steven.indrev.packets.common.FluidGuiHandInteractionPacket\nimport me.steven.indrev.utils.IRFluidTank\nimport me.steven.indrev.utils.getEnergyString\nimport me.steven.indrev.utils.getTooltip\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.math.MathHelper\n\nopen class WCustomBar(val bg: Texture, val bar: Texture, val value: () -> Int, val max: () -> Int, val direction: Direction) : WWidget() {\n\n    override fun canResize(): Boolean = false\n\n    override fun paint(matrices: MatrixStack, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n        ScreenDrawing.texturedRect(matrices, x, y, getWidth(), getHeight(), bg, -0x1)\n\n        val maxVal: Int = max()\n        var percent: Float = (value() / maxVal.toFloat()).coerceIn(0f, 1f)\n\n        var barMax = getWidth()\n        if (direction == Direction.DOWN || direction == Direction.UP) barMax = getHeight()\n        percent = (percent * barMax).toInt() / barMax.toFloat() //Quantize to bar size\n\n\n        val barSize = (barMax * percent).toInt()\n        if (barSize <= 0) return\n\n        when (direction) {\n            Direction.UP -> {\n                val left = x;\n                var top = y + getHeight();\n                top -= barSize;\n                drawBar(matrices, left, top, getWidth(), barSize, bar.u1(), MathHelper.lerp(percent, bar.v2(), bar.v1()), bar.u2(), bar.v2());\n            }\n            Direction.RIGHT -> {\n                drawBar(matrices, x, y, barSize, getHeight(),  bar.u1(), bar.v1(), MathHelper.lerp(percent, bar.u1(), bar.u2()), bar.v2())\n            }\n            Direction.DOWN -> {\n                drawBar(matrices, x, y, getWidth(), barSize,bar.u1(), bar.v1(), bar.u2(), MathHelper.lerp(percent, bar.v1(), bar.v2()))\n            }\n            Direction.LEFT -> {\n                var left = x + getWidth()\n                left -= barSize\n                drawBar(matrices, left, y, barSize, getHeight(), MathHelper.lerp(percent, bar.u2(), bar.u1()), bar.v1(), bar.u2(), bar.v2())\n            }\n        }\n    }\n\n    open fun drawBar(matrices: MatrixStack, left: Int, top: Int, width: Int, height: Int,  u1: Float, v1: Float, u2: Float, v2: Float) {\n        ScreenDrawing.texturedRect(matrices, left, top, width, height, bar.image(), u1, v1, u2, v2, -1);\n    }\n\n    enum class Direction {\n        UP, RIGHT, DOWN, LEFT\n    }\n}\nprivate val LIT_TEXTURE_ID = Texture(identifier(\"textures/gui/widget_fuel_burning.png\"))\nprivate val UNLIT_TEXTURE_ID = Texture(identifier(\"textures/gui/widget_fuel_not_burning.png\"))\n\nfun fuelBar(blockEntity: BaseBlockEntity, valueIndex: Int = 4, maxIndex: Int = 5): WCustomBar {\n    val properties = blockEntity.guiSyncableComponent ?: error(\"$blockEntity does not provide gui_syncable component\")\n    val fuel = WCustomBar(UNLIT_TEXTURE_ID, LIT_TEXTURE_ID, { properties[valueIndex] }, { properties[maxIndex] }, WCustomBar.Direction.UP)\n    fuel.setSize(14, 14)\n    return fuel\n}\n\nval TANK_BOTTOM = Texture(identifier(\"textures/gui/tank_bottom.png\"))\nval TANK_TOP = Texture(identifier(\"textures/gui/tank_top.png\"))\n\nfun fluidTank(blockEntity: BaseBlockEntity, index: Int): WCustomBar {\n    val properties = blockEntity.guiSyncableComponent ?: error(\"$blockEntity does not provide gui_syncable component\")\n    val tank = object : WCustomBar(TANK_BOTTOM, TANK_TOP, { 1 }, { 1 }, Direction.UP) {\n        override fun drawBar(matrices: MatrixStack, left: Int, top: Int, width: Int, height: Int, u1: Float, v1: Float, u2: Float, v2: Float) {\n\n            val maxVal: Long = properties.get<IRFluidTank>(index).capacity\n            var percent: Float = (properties.get<IRFluidTank>(index).amount / maxVal.toFloat()).coerceIn(0f, 1f)\n\n            val barMax = getHeight()\n            percent = (percent * barMax).toInt() / barMax.toFloat() //Quantize to bar size\n\n\n            val barSize = (barMax * percent).toInt()\n            if (barSize > 0) {\n                var t = top\n                t -= barSize\n\n                val tank = properties.get<IRFluidTank>(index)\n                tank\n                    .renderGuiRect(\n                        left.toDouble() + 1,\n                        t.toDouble() + 1 + height,\n                        left + width.toDouble() - 1,\n                        t + barSize.toDouble() - 1 + height\n                    )\n            }\n            ScreenDrawing.texturedRect(matrices, left, top, this.width, this.height, TANK_TOP, -1)\n        }\n\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            val tank = properties.get<IRFluidTank>(index)\n            if (!tank.isEmpty)\n                tooltip?.add(*getTooltip(tank.variant, tank.amount, tank.capacity).toTypedArray())\n        }\n\n        override fun onClick(x: Int, y: Int, button: Int): InputResult {\n            val packet = PacketByteBuf(Unpooled.buffer())\n            packet.writeInt(properties.get<IRFluidTank>(index).index)\n            ClientPlayNetworking.send(FluidGuiHandInteractionPacket.FLUID_CLICK_PACKET, packet)\n            return InputResult.PROCESSED\n        }\n    }\n    tank.setSize(16, 43)\n    return tank\n}\n\nval ENERGY_EMPTY = Texture(identifier(\"textures/gui/widget_energy_empty.png\"))\nval ENERGY_FULL = Texture(identifier(\"textures/gui/widget_energy_full.png\"))\n\nfun energyBar(blockEntity: BaseBlockEntity): WCustomBar {\n    val properties = blockEntity.guiSyncableComponent ?: error(\"$blockEntity does not provide gui_syncable component\")\n    val energy = object : WCustomBar(ENERGY_EMPTY, ENERGY_FULL, { properties.get<Double>(0).toInt() }, { properties.get<Double>(1).toInt() }, Direction.UP) {\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            val energy = getEnergyString(properties[0])\n            val maxEnergy = getEnergyString(properties[1])\n            tooltip?.add(TranslatableText(\"gui.widget.energy\").formatted(Formatting.BLUE))\n            tooltip?.add(LiteralText(\"$energy / $maxEnergy LF\"))\n        }\n    }\n    energy.setSize(10, 64)\n    return energy\n}\n\nprivate val EMPTY_HEAT = Texture(identifier(\"textures/gui/widget_temperature_empty.png\"))\nprivate val FULL_HEAT = Texture(identifier(\"textures/gui/widget_temperature_full.png\"))\n\nfun temperatureBar(blockEntity: BaseBlockEntity): WCustomBar {\n    val properties = blockEntity.guiSyncableComponent ?: error(\"$blockEntity does not provide gui_syncable component\")\n    val temp = object : WCustomBar(EMPTY_HEAT, FULL_HEAT, { properties.get<Double>(2).toInt() }, { properties.get<Double>(3).toInt() }, Direction.UP) {\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            val temperature = properties.get<Double>(2).toInt()\n            val maxTemperature = properties.get<Double>(3).toInt()\n\n            //TODO\n            /*\n            val info = when {\n                temperature > temperatureComponent.optimalRange.last ->\n                    TranslatableText(\"gui.widget.temperature_info.high\").formatted(Formatting.DARK_RED, Formatting.ITALIC)\n                temperature in temperatureComponent.optimalRange ->\n                    TranslatableText(\"gui.widget.temperature_info.medium\").formatted(Formatting.YELLOW, Formatting.ITALIC)\n                else ->\n                    TranslatableText(\"gui.widget.temperature_info.low\").formatted(Formatting.GREEN, Formatting.ITALIC)\n            }\n            tooltip?.add(TranslatableText(\"gui.widget.temperature\").formatted(Formatting.BLUE))*/\n            tooltip?.add(LiteralText(\"$temperature / $maxTemperature °C\"))\n            //tooltip?.add(info)\n        }\n    }\n    temp.setSize(10, 43)\n    return temp\n}\n\nval RIGHT_PROCESS_EMPTY = Texture(identifier(\"textures/gui/widget_processing_empty.png\"))\nval RIGHT_PROCESS_FULL = Texture(identifier(\"textures/gui/widget_processing_full.png\"))\n\nval LEFT_PROCESS_EMPTY = Texture(identifier(\"textures/gui/widget_processing_empty_left.png\"))\nval LEFT_PROCESS_FULL = Texture(identifier(\"textures/gui/widget_processing_full_left.png\"))\n\nval UP_PROCESS_EMPTY = Texture(identifier(\"textures/gui/widget_processing_empty_vertical.png\"))\nval UP_PROCESS_FULL = Texture(identifier(\"textures/gui/widget_processing_full_vertical.png\"))\n\nfun processBar(blockEntity: BaseBlockEntity, index: Int): WCustomBar {\n    val properties = blockEntity.guiSyncableComponent ?: error(\"$blockEntity does not provide gui_syncable component\")\n    val process = object : WCustomBar(RIGHT_PROCESS_EMPTY, RIGHT_PROCESS_FULL, { properties.get<CraftingComponent<*>>(index).processTime }, { properties.get<CraftingComponent<*>>(index).totalProcessTime }, Direction.RIGHT) {\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            val progress = properties.get<CraftingComponent<*>>(index).processTime\n            val max = properties.get<CraftingComponent<*>>(index).totalProcessTime\n            if (max <= 0) return\n            val percentage = progress * 100 / max\n            tooltip?.add(TranslatableText(\"gui.widget.process\", percentage).append(LiteralText(\"%\")))\n        }\n    }\n    return process\n}\n\nfun processBar(blockEntity: BaseBlockEntity, progress: Int, max: Int): WCustomBar {\n    val properties = blockEntity.guiSyncableComponent ?: error(\"$blockEntity does not provide gui_syncable component\")\n    val process = object : WCustomBar(RIGHT_PROCESS_EMPTY, RIGHT_PROCESS_FULL,  { properties[progress] }, { properties[max] }, Direction.RIGHT) {\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            if (properties.get<Int>(max) <= 0) return\n            val percentage = properties.get<Int>(progress) * 100 / properties.get<Int>(max)\n            tooltip?.add(TranslatableText(\"gui.widget.process\", percentage).append(LiteralText(\"%\")))\n        }\n    }\n    return process\n}\n\nfun upProcessBar(blockEntity: BaseBlockEntity, index: Int): WCustomBar {\n    val properties = blockEntity.guiSyncableComponent ?: error(\"$blockEntity does not provide gui_syncable component\")\n    val process = object : WCustomBar(UP_PROCESS_EMPTY, UP_PROCESS_FULL, { properties.get<CraftingComponent<*>>(index).processTime }, { properties.get<CraftingComponent<*>>(index).totalProcessTime }, Direction.DOWN) {\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            val progress = properties.get<CraftingComponent<*>>(index).processTime\n            val max = properties.get<CraftingComponent<*>>(index).totalProcessTime\n            if (max <= 0) return\n            val percentage = progress * 100 / max\n            tooltip?.add(TranslatableText(\"gui.widget.process\", percentage).append(LiteralText(\"%\")))\n        }\n    }\n    return process\n\n}\n\nfun upProcessBar(blockEntity: BaseBlockEntity, progress: Int, max: Int): WCustomBar {\n    val properties = blockEntity.guiSyncableComponent ?: error(\"$blockEntity does not provide gui_syncable component\")\n    val process = object : WCustomBar(UP_PROCESS_EMPTY, UP_PROCESS_FULL, { properties[progress] }, { properties[max] }, Direction.DOWN) {\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            if (properties.get<Int>(max) <= 0) return\n            val percentage = properties.get<Int>(progress) * 100 / properties.get<Int>(max)\n            tooltip?.add(TranslatableText(\"gui.widget.process\", percentage).append(LiteralText(\"%\")))\n        }\n    }\n    return process\n\n}\n\nfun leftProcessBar(blockEntity: BaseBlockEntity, index: Int): WCustomBar {\n    val properties = blockEntity.guiSyncableComponent ?: error(\"$blockEntity does not provide gui_syncable component\")\n    val process = object : WCustomBar(LEFT_PROCESS_EMPTY, LEFT_PROCESS_FULL, { properties.get<CraftingComponent<*>>(index).processTime }, { properties.get<CraftingComponent<*>>(index).totalProcessTime }, Direction.LEFT) {\n        override fun addTooltip(tooltip: TooltipBuilder?) {\n            val progress = properties.get<CraftingComponent<*>>(index).processTime\n            val max = properties.get<CraftingComponent<*>>(index).totalProcessTime\n            if (max <= 0) return\n            val percentage = progress * 100 / max\n            tooltip?.add(TranslatableText(\"gui.widget.process\", percentage).append(LiteralText(\"%\")))\n        }\n    }\n    return process\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/widgets/machines/WMachineSideDisplay.kt",
    "content": "package me.steven.indrev.gui.widgets.machines\n\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.TooltipBuilder\nimport io.github.cottonmc.cotton.gui.widget.WButton\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.SideConfiguration\nimport me.steven.indrev.utils.draw2Colors\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.gui.DrawableHelper\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.World\nimport java.util.*\n\nclass WMachineSideDisplay(\n    private val side: SideConfiguration.MachineSide,\n    private val direction: Direction,\n    var mode: TransferMode,\n    private val world: World,\n    private val blockPos: BlockPos\n) : WButton() {\n    init {\n        this.setSize(16, 16)\n    }\n\n    override fun setSize(x: Int, y: Int) {\n        this.width = x\n        this.height = y\n    }\n\n    override fun paint(matrices: MatrixStack, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n        ScreenDrawing.texturedRect(matrices, x, y, width, height, TEXTURE_ID, side.u1 / 16f, side.v1 / 16f, side.u2 / 16f, side.v2 / 16f, -1)\n        if (mode == TransferMode.INPUT_OUTPUT)\n            draw2Colors(matrices, x, y, x + width, y + height, TransferMode.INPUT.rgb, TransferMode.OUTPUT.rgb)\n        else if (mode != TransferMode.NONE)\n            DrawableHelper.fill(matrices, x, y, x + width, y + height, mode.rgb.toInt())\n        if (isWithinBounds(mouseX, mouseY))\n            DrawableHelper.fill(matrices, x, y, x + width, y + height, -2130706433)\n    }\n\n    override fun addTooltip(tooltip: TooltipBuilder?) {\n        val modeText = TranslatableText(\"item.indrev.wrench.mode\",\n            TranslatableText(\"item.indrev.wrench.${mode.toString().lowercase(Locale.getDefault())}\").formatted(Formatting.WHITE)\n        ).formatted(Formatting.BLUE)\n        val side = TranslatableText(\"item.indrev.wrench.side.${side.toString().lowercase(Locale.getDefault())}\")\n            .append(LiteralText(\" (\")\n                .append(TranslatableText(\"item.indrev.wrench.side.${direction.toString().lowercase(Locale.getDefault())}\"))\n                .append(LiteralText(\")\"))).formatted(Formatting.WHITE)\n        tooltip?.add(modeText, side)\n        val blockState = world.getBlockState(blockPos.offset(direction))\n        if (!blockState.isAir) {\n            val neighbor = TranslatableText(\"item.indrev.wrench.connected\", blockState.block.name)\n            tooltip?.add(neighbor)\n        }\n    }\n\n    companion object {\n        val TEXTURE_ID = identifier(\"textures/block/machine_block.png\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/widgets/misc/WBookEntryShortcut.kt",
    "content": "package me.steven.indrev.gui.widgets.misc\n\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.WButton\nimport io.github.cottonmc.cotton.gui.widget.data.InputResult\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.client.util.math.MatrixStack\n\nopen class WBookEntryShortcut : WButton() {\n\n    override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n        ScreenDrawing.texturedRect(matrices, x, y, width, height, ICON_IDENTIFIER, -1)\n    }\n\n    override fun onMouseDown(x: Int, y: Int, button: Int): InputResult {\n        onClick(x, y, button)\n        return InputResult.PROCESSED\n    }\n\n    override fun setSize(x: Int, y: Int) {\n        this.width = x\n        this.height = y\n    }\n\n    companion object {\n        private val ICON_IDENTIFIER = identifier(\"textures/gui/help.png\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/widgets/misc/WCircleProgressBar.kt",
    "content": "package me.steven.indrev.gui.widgets.misc\n\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.WWidget\nimport me.steven.indrev.utils.drawCircle\nimport net.minecraft.client.util.math.MatrixStack\nimport kotlin.math.*\n\nopen class WCircleProgressBar(val value: () -> Int, val max: () -> Int, val colorProvider: (Int, Int) -> Int) : WWidget() {\n\n    override fun paint(matrices: MatrixStack, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n        matrices.push()\n        matrices.translate(0.0, 0.0, 300.0)\n\n        drawCircle(matrices, value(), max(), x, y, width, colorProvider)\n\n        matrices.pop()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/widgets/misc/WDynamicSprite.kt",
    "content": "package me.steven.indrev.gui.widgets.misc\n\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.WWidget\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.Identifier\n\nclass WDynamicSprite(val provider: () -> Identifier) : WWidget() {\n    override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n        ScreenDrawing.texturedRect(matrices, x, y, width, height, provider(), -1)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/widgets/misc/WKnob.kt",
    "content": "package me.steven.indrev.gui.widgets.misc\n\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.WWidget\nimport io.github.cottonmc.cotton.gui.widget.data.InputResult\nimport me.steven.indrev.packets.common.UpdateKnobValue.UPDATE_EFFICIENCY_PACKET\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.fabricmc.fabric.api.networking.v1.PacketByteBufs\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Vec3f\nimport kotlin.math.atan2\n\nclass WKnob(var angle: Float = 30.0f, val pos: BlockPos) : WWidget() {\n\n    override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n        matrices?.run {\n            push()\n            translate(x.toDouble() + width / 2.0, y.toDouble() + width / 2.0, 0.0)\n            multiply(Vec3f.POSITIVE_Z.getDegreesQuaternion(angle))\n            translate(-x.toDouble() - width / 2.0, -y.toDouble() - width / 2.0, 0.0)\n            ScreenDrawing.texturedRect(matrices, x, y, width, height, KNOB_TEXTURE_ID, -1)\n            pop()\n        }\n\n        val textRenderer = MinecraftClient.getInstance().textRenderer\n        val text = String.format(\"%.1f\", ((angle - 30) / 300f) * 100) + \"%\"\n        ScreenDrawing.drawString(\n            matrices,\n            text,\n            (x - textRenderer.getWidth(text) / 2) + width / 2,\n            (y - textRenderer.fontHeight / 2) + height / 2,\n            -1\n        )\n    }\n\n    private fun calculateAngle(x: Float, y: Float): Float {\n        val px = (x / width.toFloat()) - 0.5\n        val py = (1 - y / height.toFloat()) - 0.5\n        var angle = -(Math.toDegrees(atan2(py, px))).toFloat() + 180\n        if (angle > 360) angle -= 360\n        return angle.coerceIn(30f, 330f)\n    }\n\n    override fun onMouseDrag(x: Int, y: Int, button: Int, deltaX: Double, deltaY: Double): InputResult {\n        angle = calculateAngle(x.toFloat(), y.toFloat())\n        return InputResult.PROCESSED\n    }\n\n    override fun onMouseUp(x: Int, y: Int, button: Int): InputResult {\n        val buf = PacketByteBufs.create()\n\n        buf.writeBlockPos(pos)\n        buf.writeFloat((angle - 30) / 300f)\n        ClientPlayNetworking.send(UPDATE_EFFICIENCY_PACKET, buf)\n        return InputResult.PROCESSED\n    }\n\n    companion object {\n        val KNOB_TEXTURE_ID = identifier(\"textures/gui/knob.png\")\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/widgets/misc/WPlayerRender.kt",
    "content": "package me.steven.indrev.gui.widgets.misc\n\nimport io.github.cottonmc.cotton.gui.widget.WWidget\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.gui.screen.ingame.InventoryScreen\nimport net.minecraft.client.util.math.MatrixStack\n\nclass WPlayerRender : WWidget() {\n    override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n        InventoryScreen.drawEntity(x, y, 30, -mouseX.toFloat(), -mouseY.toFloat(), MinecraftClient.getInstance().player)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/widgets/misc/WStaticTooltip.kt",
    "content": "package me.steven.indrev.gui.widgets.misc\n\nimport com.mojang.blaze3d.systems.RenderSystem\nimport io.github.cottonmc.cotton.gui.widget.WWidget\nimport net.minecraft.client.render.*\nimport net.minecraft.client.render.VertexFormat.DrawMode\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.util.math.Matrix4f\n\nclass WStaticTooltip : WWidget() {\n    override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n        drawTooltipBackground(matrices ?: return, x, y, width, height)\n    }\n\n    @Suppress(\"DEPRECATION\")\n    private fun drawTooltipBackground(matrices: MatrixStack, x: Int, y: Int, width: Int, height: Int) {\n        val tessellator = Tessellator.getInstance()\n        val bufferBuilder = tessellator.buffer\n        RenderSystem.setShader { GameRenderer.getPositionColorShader() }\n        bufferBuilder.begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR)\n        val matrix4f = matrices.peek().positionMatrix\n        val z = 0\n        fillGradient(matrix4f, bufferBuilder, x - 3, y - 4, x + width + 3, y - 3, z, -267386864, -267386864)\n        fillGradient(matrix4f, bufferBuilder, x - 3, y + height + 3, x + width + 3, y + height + 4, z, -267386864, -267386864)\n        fillGradient(matrix4f, bufferBuilder, x - 3, y - 3, x + width + 3, y + height + 3, z, -267386864, -267386864)\n        fillGradient(matrix4f, bufferBuilder, x - 4, y - 3, x - 3, y + height + 3, z, -267386864, -267386864)\n        fillGradient(matrix4f, bufferBuilder, x + width + 3, y - 3, x + width + 4, y + height + 3, z, -267386864, -267386864)\n        fillGradient(matrix4f, bufferBuilder, x - 3, y - 3 + 1, x - 3 + 1, y + height + 3 - 1, z, 1347420415, 1344798847)\n        fillGradient(matrix4f, bufferBuilder, x + width + 2, y - 3 + 1, x + width + 3, y + height + 3 - 1, z, 1347420415, 1344798847)\n        fillGradient(matrix4f, bufferBuilder, x - 3, y - 3, x + width + 3, y - 3 + 1, z, 1347420415, 1347420415)\n        fillGradient(matrix4f, bufferBuilder, x - 3, y + height + 2, x + width + 3, y + height + 3, z, 1344798847, 1344798847)\n        RenderSystem.enableDepthTest()\n        RenderSystem.disableTexture()\n        RenderSystem.enableBlend()\n        RenderSystem.defaultBlendFunc()\n        bufferBuilder.end()\n        BufferRenderer.draw(bufferBuilder)\n        RenderSystem.disableBlend()\n        RenderSystem.enableTexture()\n    }\n\n    private fun fillGradient(matrix4f: Matrix4f?, bufferBuilder: BufferBuilder, xStart: Int, yStart: Int, xEnd: Int, yEnd: Int, i: Int, j: Int, k: Int) {\n        val f = (j shr 24 and 255).toFloat() / 255.0f\n        val g = (j shr 16 and 255).toFloat() / 255.0f\n        val h = (j shr 8 and 255).toFloat() / 255.0f\n        val l = (j and 255).toFloat() / 255.0f\n        val m = (k shr 24 and 255).toFloat() / 255.0f\n        val n = (k shr 16 and 255).toFloat() / 255.0f\n        val o = (k shr 8 and 255).toFloat() / 255.0f\n        val p = (k and 255).toFloat() / 255.0f\n        bufferBuilder.vertex(matrix4f, xEnd.toFloat(), yStart.toFloat(), i.toFloat()).color(g, h, l, f).next()\n        bufferBuilder.vertex(matrix4f, xStart.toFloat(), yStart.toFloat(), i.toFloat()).color(g, h, l, f).next()\n        bufferBuilder.vertex(matrix4f, xStart.toFloat(), yEnd.toFloat(), i.toFloat()).color(n, o, p, m).next()\n        bufferBuilder.vertex(matrix4f, xEnd.toFloat(), yEnd.toFloat(), i.toFloat()).color(n, o, p, m).next()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/widgets/misc/WText.kt",
    "content": "package me.steven.indrev.gui.widgets.misc\n\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport io.github.cottonmc.cotton.gui.widget.WWidget\nimport io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.text.Text\n\nclass WText(\n    private val string: () -> Text,\n    private val alignment: HorizontalAlignment = HorizontalAlignment.CENTER,\n    private val color: Int = -1\n) : WWidget() {\n    constructor(string: Text, alignment: HorizontalAlignment = HorizontalAlignment.CENTER, color: Int = -1) : this(\n        { string },\n        alignment,\n        color\n    )\n\n    override fun paint(matrices: MatrixStack?, x: Int, y: Int, mouseX: Int, mouseY: Int) {\n        var alignedX = x.toDouble()\n        val text = string()\n        if (alignment == HorizontalAlignment.CENTER) {\n            alignedX -= MinecraftClient.getInstance().textRenderer.getWidth(text) / 2.0\n            MinecraftClient.getInstance().textRenderer.draw(matrices, text, alignedX.toFloat(), y.toFloat(), color)\n            return\n        }\n        ScreenDrawing.drawString(matrices, text.asOrderedText(), alignment, alignedX.toInt(), y, this.width, color)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/gui/widgets/misc/WTooltipedItemSlot.kt",
    "content": "package me.steven.indrev.gui.widgets.misc\n\nimport io.github.cottonmc.cotton.gui.widget.WItemSlot\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.inventory.Inventory\nimport net.minecraft.text.Text\n\nopen class WTooltipedItemSlot(\n    private val emptyTooltip: MutableList<Text>,\n    private val inventory: Inventory,\n    private val startIndex: Int = 0,\n    private val slotsWide: Int = 1,\n    private val slotsHigh: Int = 1,\n    big: Boolean = false\n) : WItemSlot(inventory, startIndex, slotsWide, slotsHigh, big) {\n\n    override fun renderTooltip(matrices: MatrixStack?, x: Int, y: Int, tX: Int, tY: Int) {\n        val slots = startIndex until startIndex + (slotsHigh * slotsWide)\n        if (emptyTooltip.size != 0 && slots.all { inventory.getStack(it).isEmpty }) {\n            val screen = MinecraftClient.getInstance().currentScreen\n            screen?.renderTooltip(matrices, emptyTooltip, tX + x, tY + y)\n        }\n    }\n\n    companion object {\n        fun of(inventory: Inventory, index: Int, vararg emptyTooltip: Text): WTooltipedItemSlot =\n            WTooltipedItemSlot(emptyTooltip.toMutableList(), inventory, index)\n\n        fun of(\n            inventory: Inventory,\n            startIndex: Int,\n            slotsWide: Int,\n            slotsHigh: Int,\n            vararg emptyTooltip: Text\n        ): WTooltipedItemSlot = WTooltipedItemSlot(emptyTooltip.toMutableList(), inventory, startIndex, slotsWide, slotsHigh)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/inventories/IRInventory.kt",
    "content": "package me.steven.indrev.inventories\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.components.InventoryComponent\nimport net.minecraft.inventory.SidedInventory\nimport net.minecraft.inventory.SimpleInventory\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.math.Direction\n\nclass IRInventory(\n    dsl: IRInventoryDSL,\n    size: Int,\n    val inputSlots: IntArray,\n    val outputSlots: IntArray,\n    val slotPredicate: (Int, ItemStack?, Direction?) -> Boolean = { _, _, _ -> true }\n) : SimpleInventory(size), SidedInventory {\n\n    var component: InventoryComponent? = null\n\n    private var availableSlots = inputSlots.plus(outputSlots)\n    val coolerSlot = dsl.coolerSlot\n    private val maxCount = dsl.maxStackCount\n\n    init {\n        if (dsl.coolerSlot != null) availableSlots = availableSlots.plus(coolerSlot!!)\n        availableSlots = availableSlots.distinct().toIntArray()\n    }\n\n    val coolerStack: ItemStack get() = if (coolerSlot == null) ItemStack.EMPTY else getStack(coolerSlot)\n\n    override fun getAvailableSlots(var1: Direction?): IntArray = availableSlots\n\n    override fun canExtract(slot: Int, stack: ItemStack?, direction: Direction?): Boolean =\n        outputSlots.contains(slot) && component?.itemConfig?.get(direction)?.output == true\n\n    override fun canInsert(slot: Int, stack: ItemStack?, direction: Direction?): Boolean =\n        (if (slot == coolerSlot) stack?.isIn(IndustrialRevolution.COOLERS_TAG) == true else stack?.isIn(IndustrialRevolution.COOLERS_TAG) == false) &&\n        (inputSlots.contains(slot) || slot == coolerSlot) && component?.itemConfig?.get(direction)?.input == true && slotPredicate(slot, stack, direction) || stack?.isEmpty == true\n\n    override fun getMaxCountPerStack(): Int = maxCount\n\n    override fun isValid(slot: Int, stack: ItemStack?): Boolean = slotPredicate(slot, stack, null) || stack?.isEmpty == true\n\n    fun fits(stack: Item, outputSlot: Int): Boolean {\n        val outStack = getStack(outputSlot)\n        if (outStack.isEmpty || (stack == outStack.item && outStack.nbt?.isEmpty != false))\n            return true\n        return false\n    }\n\n    fun output(stack: ItemStack): Boolean {\n        for (i in 0 until size()) {\n            val itemStack = getStack(i)\n            if (canCombine(itemStack, stack)) {\n                transfer(stack, itemStack)\n                if (stack.isEmpty) {\n                    return true\n                }\n            }\n        }\n        return addToOutputSlot(stack)\n    }\n\n    private fun canCombine(one: ItemStack, two: ItemStack): Boolean\n            = one.item === two.item && ItemStack.areNbtEqual(one, two)\n\n    private fun transfer(source: ItemStack, target: ItemStack) {\n        val i = this.maxCountPerStack.coerceAtMost(target.maxCount)\n        val j = source.count.coerceAtMost(i - target.count)\n        if (j > 0) {\n            target.increment(j)\n            source.decrement(j)\n            markDirty()\n        }\n    }\n\n    private fun addToOutputSlot(stack: ItemStack): Boolean {\n        for (i in outputSlots) {\n            val itemStack = getStack(i)\n            if (itemStack.isEmpty) {\n                setStack(i, stack.copy())\n                stack.count = 0\n                return true\n            }\n        }\n        return false\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/inventories/IRInventoryDSL.kt",
    "content": "package me.steven.indrev.inventories\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.components.InventoryComponent\nimport me.steven.indrev.items.upgrade.IREnhancerItem\nimport me.steven.indrev.utils.EMPTY_INT_ARRAY\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.math.Direction\n\nopen class Filterable {\n    var filters: MutableMap<Int, (ItemStack, Direction?) -> Boolean> = hashMapOf()\n\n    infix fun Int.filter(filter: (ItemStack, Direction?) -> Boolean) {\n        filters[this] = filter\n    }\n\n    infix fun IntRange.filter(filter: (ItemStack, Direction?, Int) -> Boolean) {\n        forEach { slot -> filters[slot] = { stack, direction -> filter(stack, direction, slot) } }\n    }\n\n    infix fun Int.filter(filter: (ItemStack) -> Boolean) {\n        filters[this] = { stack, _ -> filter(stack) }\n    }\n\n    infix fun IntRange.filter(filter: (ItemStack, Int) -> Boolean) {\n        forEach { slot -> filters[slot] = { stack, _ -> filter(stack,  slot) } }\n    }\n}\n\nopen class IRInventoryDSL : Filterable() {\n    var coolerSlot: Int? = null\n    private var input: FilteredSlots = FilteredSlots.EMPTY_FILTER\n    private var output: FilteredSlots = FilteredSlots.EMPTY_FILTER\n    var enhancerSlots: IntRange? = null\n    var maxStackCount = 64\n\n    fun input(block: FilteredSlots.() -> Unit) {\n        input = FilteredSlots()\n        block(input)\n        input.filters.forEach { (key, value) -> filters[key] = value }\n    }\n\n    fun output(block: FilteredSlots.() -> Unit) {\n        output = FilteredSlots()\n        block(output)\n        output.filters.forEach { (key, value) -> filters[key] = value }\n    }\n\n    fun build(blockEntity: MachineBlockEntity<*>): IRInventory {\n        var size = input.slots.plus(output.slots).plus(filters.keys).distinct().size + 1\n        if (coolerSlot == null && blockEntity.temperatureComponent != null) coolerSlot = 1\n        if (coolerSlot != null) size++\n        val enhancerComponent = blockEntity.enhancerComponent\n        if (enhancerComponent != null) size += enhancerComponent.slots.size\n        return IRInventory(this, size, input.slots, output.slots) { slot, stack, dir ->\n            if (stack == null) false\n            else filters.computeIfAbsent(slot) {\n                { (stack, item), _ ->\n                    when {\n                        coolerSlot != null && slot == coolerSlot -> stack.isIn(IndustrialRevolution.COOLERS_TAG)\n                        input.slots.contains(slot) -> true\n                        enhancerComponent != null -> item is IREnhancerItem && slot in enhancerComponent.slots && !enhancerComponent.isLocked(slot, blockEntity.tier) && enhancerComponent.compatible.contains(item.enhancer)\n                        else -> false\n                    }\n                }\n            }(stack, dir)\n        }\n    }\n\n    class FilteredSlots : Filterable() {\n        var slots: IntArray = EMPTY_INT_ARRAY\n        var slot: Int? = null\n            set(value) {\n                if (value != null)\n                    slots = intArrayOf(value)\n                field = value\n            }\n\n        fun filter(filter: (ItemStack, Direction?, Int) -> Boolean) {\n            slots.forEach { slot -> filters[slot] = { stack, dir -> filter(stack, dir, slot) } }\n        }\n\n        fun filter(filter: (ItemStack, Int) -> Boolean) {\n            slots.forEach { slot -> filters[slot] = { stack, _ -> filter(stack, slot) } }\n        }\n\n        companion object {\n            val EMPTY_FILTER = FilteredSlots()\n        }\n    }\n}\n\nfun inventory(blockEntity: MachineBlockEntity<*>, block: IRInventoryDSL.() -> Unit): InventoryComponent {\n    val dsl = IRInventoryDSL()\n    block(dsl)\n    val inv = dsl.build(blockEntity)\n    return InventoryComponent(blockEntity) { inv }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/armor/IRColorModuleItem.kt",
    "content": "package me.steven.indrev.items.armor\n\nimport me.steven.indrev.tools.modular.ArmorModule\n\nclass IRColorModuleItem(val color: Int, settings: Settings) : IRModuleItem(ArmorModule.COLOR, settings)"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/armor/IRModularArmorItem.kt",
    "content": "package me.steven.indrev.items.armor\n\nimport com.google.common.collect.ImmutableMultimap\nimport com.google.common.collect.Multimap\nimport dev.emi.stepheightentityattribute.StepHeightEntityAttributeMain\nimport me.steven.indrev.IndustrialRevolutionClient\nimport me.steven.indrev.api.AttributeModifierProvider\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.armor.IRArmorMaterial\nimport me.steven.indrev.gui.tooltip.modular.ModularTooltipData\nimport me.steven.indrev.items.energy.IREnergyItem\nimport me.steven.indrev.registry.IRFluidRegistry\nimport me.steven.indrev.tools.modular.ArmorModule\nimport me.steven.indrev.tools.modular.IRModularItem\nimport me.steven.indrev.utils.bucket\nimport me.steven.indrev.utils.energyOf\nimport net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry\nimport net.fabricmc.fabric.api.entity.event.v1.FabricElytraItem\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.minecraft.client.gui.screen.Screen\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.client.item.TooltipData\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.entity.attribute.EntityAttribute\nimport net.minecraft.entity.attribute.EntityAttributeModifier\nimport net.minecraft.entity.attribute.EntityAttributes\nimport net.minecraft.item.ArmorItem\nimport net.minecraft.item.DyeableArmorItem\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.world.World\nimport team.reborn.energy.api.EnergyStorage\nimport team.reborn.energy.impl.SimpleItemEnergyStorageImpl\nimport java.util.*\nimport kotlin.math.roundToInt\n\nclass IRModularArmorItem(slot: EquipmentSlot, maxStored: Long, settings: Settings) :\n    DyeableArmorItem(IRArmorMaterial.MODULAR, slot, settings), IRModularItem<ArmorModule>, AttributeModifierProvider, IREnergyItem, JetpackHandler, FabricElytraItem {\n\n    init {\n        EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, maxStored, Tier.MK4.io, Tier.MK4.io) }, this)\n    }\n\n    override val fluidFilter: (FluidVariant) -> Boolean = { it.isOf(IRFluidRegistry.HYDROGEN_STILL) }\n\n    override val limit: Long = bucket / 20\n\n\n    override fun isUsable(stack: ItemStack): Boolean = ArmorModule.JETPACK.getLevel(stack) > 0\n\n    fun getFluidItemBarStep(stack: ItemStack): Int {\n        val volume = getFuelStored(stack)\n        return (13.0 - (((limit - volume.amount()) * 13) / limit)).roundToInt()\n    }\n\n    fun getFluidItemBarColor(stack: ItemStack): Int = FluidRenderHandlerRegistry.INSTANCE.get(getFuelStored(stack).resource.fluid).getFluidColor(null, null, null)\n\n    fun isFluidItemBarVisible(stack: ItemStack): Boolean = getFuelStored(stack).amount > 0\n\n    override fun getItemBarColor(stack: ItemStack?): Int = getDurabilityBarColor(stack)\n\n    override fun isItemBarVisible(stack: ItemStack?): Boolean = hasDurabilityBar(stack)\n\n    override fun getItemBarStep(stack: ItemStack?): Int = getDurabilityBarProgress(stack)\n\n    override fun isEnchantable(stack: ItemStack?): Boolean = false\n\n    override fun appendTooltip(stack: ItemStack, world: World?, tooltip: MutableList<Text>?, context: TooltipContext?) {\n        if (Screen.hasShiftDown())\n            getInstalledTooltip(getInstalled(stack), stack, tooltip)\n        tooltip?.add(\n            TranslatableText(\"item.indrev.modular_item.tooltip\", LiteralText(\"\").append(\n                IndustrialRevolutionClient.MODULAR_CONTROLLER_KEYBINDING.boundKeyLocalizedText).formatted(Formatting.AQUA)).formatted(\n                Formatting.GRAY))\n    }\n\n    override fun canRepair(stack: ItemStack?, ingredient: ItemStack?): Boolean = false\n\n    override fun getColor(stack: ItemStack?): Int {\n        val nbt = stack!!.getSubNbt(\"display\")\n        return if (nbt != null && nbt.contains(\"color\", 99)) nbt.getInt(\"color\") else -1\n    }\n\n    fun getMaxShield(protectionLevel: Int) = protectionLevel * 100.0\n\n    override fun getCompatibleModules(itemStack: ItemStack): Array<ArmorModule> {\n        val armor = itemStack.item as? ArmorItem ?: return emptyArray()\n        return when (armor.slotType) {\n            EquipmentSlot.HEAD -> ArmorModule.COMPATIBLE_HELMET\n            EquipmentSlot.CHEST -> ArmorModule.COMPATIBLE_CHEST\n            EquipmentSlot.LEGS -> ArmorModule.COMPATIBLE_LEGS\n            EquipmentSlot.FEET -> ArmorModule.COMPATIBLE_BOOTS\n            else -> return emptyArray()\n        }\n    }\n\n    override fun getInstalled(stack: ItemStack): List<ArmorModule> {\n        val tag = stack.nbt ?: return emptyList()\n        return getCompatibleModules(stack).filter { module -> module != ArmorModule.COLOR }.mapNotNull { module ->\n            if (tag.contains(module.key)) module\n            else null\n        }\n    }\n\n    override fun useCustomElytra(entity: LivingEntity, chestStack: ItemStack, tickElytra: Boolean): Boolean {\n        if (ReinforcedElytraItem.canFallFly(chestStack)) {\n            if (tickElytra) {\n                doVanillaElytraTick(entity, chestStack)\n            }\n            return true\n        }\n\n        return false\n    }\n\n    override fun getAttributeModifiers(\n        itemStack: ItemStack,\n        equipmentSlot: EquipmentSlot\n    ): Multimap<EntityAttribute, EntityAttributeModifier> {\n        val item = itemStack.item as IRModularArmorItem\n        val itemIo = energyOf(itemStack)\n        if (itemIo == null || itemIo.amount <= 0) {\n            return ImmutableMultimap.of()\n        } else if (equipmentSlot == item.slotType) {\n            val level = ArmorModule.PROTECTION.getLevel(itemStack).toDouble()\n            val uUID = MODIFIERS[equipmentSlot.entitySlotId]\n            val attr = ImmutableMultimap.builder<EntityAttribute, EntityAttributeModifier>()\n            if (level > 0) {\n                val toughnessModifier = EntityAttributeModifier(uUID, \"Armor toughness\", item.toughness * level, EntityAttributeModifier.Operation.ADDITION)\n                attr.put(\n                    EntityAttributes.GENERIC_ARMOR_TOUGHNESS,\n                    toughnessModifier\n                )\n                val armorModifier = EntityAttributeModifier(uUID, \"Armor modifier\", item.protection * level, EntityAttributeModifier.Operation.ADDITION)\n                attr.put(\n                    EntityAttributes.GENERIC_ARMOR,\n                    armorModifier\n                )\n            }\n            val speedLevel = ArmorModule.SPEED.getLevel(itemStack) * 0.9\n            if (speedLevel > 0) {\n                attr.put(\n                    EntityAttributes.GENERIC_MOVEMENT_SPEED,\n                    EntityAttributeModifier(SPEED_MODIFIER, \"Speed\", speedLevel, EntityAttributeModifier.Operation.MULTIPLY_TOTAL)\n                )\n\n                attr.put(\n                    StepHeightEntityAttributeMain.STEP_HEIGHT,\n                    EntityAttributeModifier(STEP_HEIGHT_MODIFIER, \"Step-Height\", 1.0, EntityAttributeModifier.Operation.ADDITION)\n                )\n            }\n            return attr.build()\n        }\n        return getAttributeModifiers(equipmentSlot)\n    }\n\n    override fun getTooltipData(stack: ItemStack): Optional<TooltipData> {\n        val handler = energyOf(stack) ?: return Optional.empty()\n        val modules = getInstalled(stack)\n        return Optional.of(ModularTooltipData(handler.amount, handler.capacity, modules) { it.getLevel(stack) })\n    }\n\n    companion object {\n        private val MODIFIERS = arrayOf(\n            UUID.fromString(\"845DB27C-C624-495F-8C9F-6020A9A58B6B\"),\n            UUID.fromString(\"D8499B04-0E66-4726-AB29-64469D734E0D\"),\n            UUID.fromString(\"9F3D476D-C118-4544-8365-64846904B48E\"),\n            UUID.fromString(\"2AD3F246-FEE1-4E67-B886-69FD380BB150\")\n        )\n        private val SPEED_MODIFIER = UUID.randomUUID()\n        private val STEP_HEIGHT_MODIFIER = UUID.randomUUID()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/armor/IRModuleItem.kt",
    "content": "package me.steven.indrev.items.armor\n\nimport me.steven.indrev.tools.modular.Module\nimport net.minecraft.client.gui.screen.Screen\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.client.option.KeyBinding\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.world.World\n\nopen class IRModuleItem(val module: Module, settings: Settings) : Item(settings) {\n\n    override fun appendTooltip(\n        stack: ItemStack,\n        world: World?,\n        tooltip: MutableList<Text>?,\n        context: TooltipContext?\n    ) {\n        if (Screen.hasShiftDown()) {\n            module.getTooltip(stack, tooltip)\n        } else {\n            tooltip?.add(TranslatableText(\"gui.indrev.tooltip.press_shift\", LiteralText(\"\").append(KeyBinding.getLocalizedName(\"key.keyboard.left.shift\").get()).formatted(Formatting.BLUE, Formatting.ITALIC)).formatted(Formatting.BLUE, Formatting.ITALIC))\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/armor/JetpackHandler.kt",
    "content": "package me.steven.indrev.items.armor\n\nimport me.steven.indrev.registry.IRFluidRegistry\nimport me.steven.indrev.utils.bucket\nimport net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage\nimport net.fabricmc.fabric.api.transfer.v1.item.ItemVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.base.ResourceAmount\nimport net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantItemStorage\nimport net.fabricmc.fabric.api.transfer.v1.transaction.Transaction\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.inventory.Inventory\nimport net.minecraft.item.ItemConvertible\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\n\ninterface JetpackHandler : ItemConvertible {\n\n    val fluidFilter: (FluidVariant) -> Boolean\n    val limit: Long\n\n    fun isUsable(stack: ItemStack): Boolean = true\n\n    fun tickJetpack(stack: ItemStack, playerEntity: PlayerEntity) {\n        if (!playerEntity.jumping) return\n        val max = 0.5 // TODO oxyhydrogen\n        val vel = playerEntity.velocity\n        if (playerEntity.isFallFlying && useFuel(stack, playerEntity.inventory, 38)) {\n            val facing = playerEntity.rotationVector\n            playerEntity.velocity = vel.multiply(0.3).add(facing.multiply(max))\n        } else if (vel.y < max && useFuel(stack, playerEntity.inventory, 38)) {\n            playerEntity.setVelocity(vel.x, max.coerceAtMost(vel.y + 0.15), vel.z)\n        }\n    }\n\n    fun getFuelStored(stack: ItemStack): ResourceAmount<FluidVariant> {\n        val nbt = stack.getSubNbt(\"Fluid\") ?: return ResourceAmount(FluidVariant.blank(), 0)\n        val variant = FluidVariant.fromNbt(nbt)\n        val amt = nbt.getLong(\"Amount\")\n        return ResourceAmount(variant, amt)\n    }\n\n    //TODO oxyhydrogen\n    private fun getConsumptionRatio(stack: ItemStack) = bucket / 81 / 3\n\n    private fun useFuel(stack: ItemStack, inv: Inventory, slot: Int): Boolean {\n        val consumption = getConsumptionRatio(stack)\n        val storage = FluidStorage.ITEM.find(stack, ContainerItemContext.ofSingleSlot(InventoryStorage.of(inv, null).getSlot(slot)))\n        Transaction.openOuter().use { tx ->\n            val extracted = storage?.extract(FluidVariant.of(IRFluidRegistry.HYDROGEN_STILL), consumption, tx)\n            return if (extracted == consumption) {\n                tx.commit()\n                true\n            } else {\n                tx.abort()\n                false\n            }\n        }\n    }\n\n    class JetpackFluidStorage(val handler: JetpackHandler, ctx: ContainerItemContext) : SingleVariantItemStorage<FluidVariant>(ctx) {\n\n        override fun getCapacity(variant: FluidVariant?): Long = handler.limit\n\n        override fun getBlankResource(): FluidVariant = FluidVariant.blank()\n\n        override fun getResource(currentVariant: ItemVariant): FluidVariant {\n            val compound = currentVariant.nbt?.getCompound(\"Fluid\") ?: return blankResource\n            return FluidVariant.fromNbt(compound)\n        }\n\n        override fun getAmount(currentVariant: ItemVariant): Long {\n            val compound = currentVariant.nbt?.getCompound(\"Fluid\") ?: return 0\n            return compound.getLong(\"Amount\")\n        }\n\n        override fun getUpdatedVariant(\n            currentVariant: ItemVariant,\n            newResource: FluidVariant,\n            newAmount: Long\n        ): ItemVariant {\n            val nbt = NbtCompound()\n            return if (newAmount > 0 && !newResource.isBlank) {\n                val fluidNbt = newResource.toNbt()\n                fluidNbt.putLong(\"Amount\", newAmount)\n                nbt.put(\"Fluid\", fluidNbt)\n                ItemVariant.of(handler, nbt)\n            } else ItemVariant.of(handler)\n        }\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/armor/JetpackItem.kt",
    "content": "package me.steven.indrev.items.armor\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.armor.IRArmorMaterial\nimport me.steven.indrev.registry.IRFluidRegistry\nimport me.steven.indrev.utils.bucket\nimport me.steven.indrev.utils.itemSettings\nimport net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.item.ArmorItem\nimport net.minecraft.item.ItemStack\nimport kotlin.math.roundToInt\n\nclass JetpackItem(tier: Tier) : ArmorItem(IRArmorMaterial.JETPACK, EquipmentSlot.CHEST, itemSettings().maxCount(1)), JetpackHandler {\n\n    init {\n        FluidStorage.ITEM.registerForItems({ stack, ctx -> JetpackHandler.JetpackFluidStorage(this, ctx) }, this)\n    }\n\n    override val limit = bucket * (10L * (tier.ordinal + 1))\n\n    override val fluidFilter: (FluidVariant) -> Boolean = { it.isOf(IRFluidRegistry.HYDROGEN_STILL) }\n\n    private fun isActivated(stack: ItemStack) = stack.isOf(this) && stack.orCreateNbt.getBoolean(\"Activated\")\n\n    fun toggle(stack: ItemStack) {\n        if (stack.isOf(this)) {\n            val tag = stack.orCreateNbt\n            tag.putBoolean(\"Activated\", tag.getBoolean(\"Activated\"))\n        }\n    }\n\n    override fun getItemBarStep(stack: ItemStack): Int {\n        val volume = getFuelStored(stack)\n        return (13.0 - (((limit - volume.amount()) * 13) / limit).toDouble()).roundToInt()\n    }\n\n    override fun getItemBarColor(stack: ItemStack): Int = FluidRenderHandlerRegistry.INSTANCE.get(getFuelStored(stack).resource.fluid).getFluidColor(null, null, getFuelStored(stack).resource.fluid.defaultState)\n\n    override fun isItemBarVisible(stack: ItemStack): Boolean = getFuelStored(stack).amount > 0\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/armor/ReinforcedElytraItem.kt",
    "content": "package me.steven.indrev.items.armor\n\nimport me.steven.indrev.armor.IRArmorMaterial\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.tools.modular.ArmorModule\nimport me.steven.indrev.utils.energyOf\nimport me.steven.indrev.utils.itemSettings\nimport net.fabricmc.fabric.api.entity.event.v1.FabricElytraItem\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.item.ArmorItem\nimport net.minecraft.item.ElytraItem\nimport net.minecraft.item.ItemStack\n\nclass ReinforcedElytraItem : ArmorItem(IRArmorMaterial.STEEL_ELYTRA, EquipmentSlot.CHEST, itemSettings().maxDamage(800)), FabricElytraItem {\n\n\n    override fun useCustomElytra(entity: LivingEntity, chestStack: ItemStack, tickElytra: Boolean): Boolean {\n        return canFallFly(chestStack) && super.useCustomElytra(entity, chestStack, tickElytra)\n    }\n\n    companion object {\n        fun canFallFly(itemStack: ItemStack): Boolean {\n            return (itemStack.isOf(IRItemRegistry.REINFORCED_ELYTRA) && ElytraItem.isUsable(itemStack))\n                    || (itemStack.isOf(IRItemRegistry.MODULAR_ARMOR_CHEST)\n                     && ArmorModule.ELYTRA.getLevel(itemStack) > 0\n                    && energyOf(itemStack)!!.amount > 0)\n        }\n\n        fun hasValidElytra(itemStack: ItemStack): Boolean {\n            return (itemStack.isOf(IRItemRegistry.REINFORCED_ELYTRA))\n                    || (itemStack.isOf(IRItemRegistry.MODULAR_ARMOR_CHEST)\n                    && IRItemRegistry.MODULAR_ARMOR_CHEST.getInstalled(itemStack).contains(ArmorModule.ELYTRA))\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/energy/IRBatteryItem.kt",
    "content": "package me.steven.indrev.items.energy\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.gui.tooltip.energy.EnergyTooltipData\nimport me.steven.indrev.utils.energyOf\nimport net.minecraft.client.item.TooltipData\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport team.reborn.energy.api.EnergyStorage\nimport team.reborn.energy.impl.SimpleItemEnergyStorageImpl\nimport java.util.*\n\nclass IRBatteryItem(settings: Settings, maxStored: Long) :\n    Item(settings), IREnergyItem {\n\n    init {\n        EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, maxStored, Tier.MK1.io, Tier.MK1.io) }, this)\n    }\n\n    override fun getItemBarColor(stack: ItemStack?): Int = getDurabilityBarColor(stack)\n\n    override fun isItemBarVisible(stack: ItemStack?): Boolean = hasDurabilityBar(stack)\n\n    override fun getItemBarStep(stack: ItemStack?): Int = getDurabilityBarProgress(stack)\n\n    override fun canRepair(stack: ItemStack?, ingredient: ItemStack?): Boolean = false\n\n    override fun getTooltipData(stack: ItemStack): Optional<TooltipData> {\n        val handler = energyOf(stack) ?: return Optional.empty()\n        return Optional.of(EnergyTooltipData(handler.amount, handler.capacity))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/energy/IREnergyItem.kt",
    "content": "package me.steven.indrev.items.energy\n\nimport me.steven.indrev.utils.energyOf\nimport net.minecraft.item.ItemStack\nimport kotlin.math.roundToInt\n\ninterface IREnergyItem {\n    fun getDurabilityBarProgress(stack: ItemStack?): Int {\n        val energyIo = energyOf(stack) ?: return 0\n        return (13.0f - (energyIo.capacity - energyIo.amount) * 13.0f / energyIo.capacity).roundToInt()\n    }\n\n    fun hasDurabilityBar(stack: ItemStack?): Boolean = (energyOf(stack)?.amount ?: 0) > 0\n\n    fun getDurabilityBarColor(stack: ItemStack?): Int {\n        val durability = getDurabilityBarProgress(stack) / 13f\n        val r = (149 - ((149 - 55) * durability)).toInt() and 255 shl 16\n        val g = (122 - ((122) * durability)).toInt() shl 8\n        val b = 255\n        return r or g or b\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/energy/IRGamerAxeItem.kt",
    "content": "package me.steven.indrev.items.energy\n\nimport com.google.common.collect.ImmutableMultimap\nimport com.google.common.collect.Multimap\nimport me.steven.indrev.IndustrialRevolutionClient\nimport me.steven.indrev.api.AttributeModifierProvider\nimport me.steven.indrev.api.CustomEnchantmentProvider\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.gui.tooltip.modular.ModularTooltipData\nimport me.steven.indrev.tools.modular.GamerAxeModule\nimport me.steven.indrev.tools.modular.IRModularItem\nimport me.steven.indrev.tools.modular.MiningToolModule\nimport me.steven.indrev.tools.modular.Module\nimport me.steven.indrev.utils.energyOf\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.gui.screen.Screen\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.client.item.TooltipData\nimport net.minecraft.enchantment.Enchantment\nimport net.minecraft.enchantment.Enchantments\nimport net.minecraft.entity.Entity\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.entity.attribute.EntityAttribute\nimport net.minecraft.entity.attribute.EntityAttributeModifier\nimport net.minecraft.entity.attribute.EntityAttributes\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.AxeItem\nimport net.minecraft.item.ItemStack\nimport net.minecraft.item.ItemUsageContext\nimport net.minecraft.item.ToolMaterial\nimport net.minecraft.tag.BlockTags\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.Hand\nimport net.minecraft.util.TypedActionResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Box\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.World\nimport team.reborn.energy.api.EnergyStorage\nimport team.reborn.energy.api.base.SimpleBatteryItem\nimport team.reborn.energy.impl.SimpleItemEnergyStorageImpl\nimport java.util.*\n\nclass IRGamerAxeItem(\n    material: ToolMaterial,\n    maxStored: Long,\n    tier: Tier,\n    attackDamage: Float,\n    attackSpeed: Float,\n    settings: Settings\n) : AxeItem(material, attackDamage, attackSpeed, settings), IREnergyItem, IRModularItem<Module>, AttributeModifierProvider, CustomEnchantmentProvider {\n\n    init {\n        EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, maxStored, tier.io, tier.io) }, this)\n    }\n\n    override fun appendTooltip(\n        stack: ItemStack,\n        world: World?,\n        tooltip: MutableList<Text>,\n        context: TooltipContext?\n    ) {\n        if (Screen.hasShiftDown())\n            getInstalledTooltip(getInstalled(stack), stack, tooltip)\n        val active = stack.nbt?.getBoolean(\"Active\") ?: false\n        tooltip.add(TranslatableText(\"item.indrev.gamer_axe.tooltip.$active\", LiteralText(\"\").append(IndustrialRevolutionClient.GAMER_AXE_TOGGLE_KEYBINDING.boundKeyLocalizedText).formatted(Formatting.AQUA)).formatted(Formatting.GRAY))\n        tooltip.add(TranslatableText(\"item.indrev.modular_item.tooltip\", LiteralText(\"\").append(IndustrialRevolutionClient.MODULAR_CONTROLLER_KEYBINDING.boundKeyLocalizedText).formatted(Formatting.AQUA)).formatted(Formatting.GRAY))\n    }\n\n    override fun getItemBarColor(stack: ItemStack?): Int = getDurabilityBarColor(stack)\n\n    override fun isItemBarVisible(stack: ItemStack?): Boolean = hasDurabilityBar(stack)\n\n    override fun getItemBarStep(stack: ItemStack?): Int = getDurabilityBarProgress(stack)\n\n    override fun isEnchantable(stack: ItemStack?): Boolean = false\n\n    override fun getTooltipData(stack: ItemStack): Optional<TooltipData> {\n        val handler = energyOf(stack) ?: return Optional.empty()\n        val modules = getInstalled(stack)\n        return Optional.of(ModularTooltipData(handler.amount, handler.capacity, modules) { it.getLevel(stack) })\n    }\n\n    override fun useOnBlock(context: ItemUsageContext?): ActionResult {\n        return ActionResult.PASS\n    }\n\n    override fun getMiningSpeedMultiplier(stack: ItemStack, state: BlockState?): Float {\n        val speedMultiplier = MiningToolModule.EFFICIENCY.getLevel(stack) + 1\n        return if (!isActive(stack) || (energyOf(stack)?.amount ?: 0) <= 0) 0f\n        else 8f * speedMultiplier\n    }\n\n    override fun hasGlint(stack: ItemStack?): Boolean {\n        return stack?.nbt?.getBoolean(\"Active\") == true\n    }\n\n    override fun postMine(\n        stack: ItemStack,\n        world: World,\n        state: BlockState,\n        pos: BlockPos,\n        miner: LivingEntity\n    ): Boolean {\n        if (!isActive(stack) || miner !is PlayerEntity) return false\n        val canStart = unsafeUse(stack, 1)\n        if (canStart && !miner.isSneaking && state.isIn(BlockTags.LOGS)) {\n            val scanned = mutableSetOf<BlockPos>()\n            Direction.values().forEach { dir ->\n                scanTree(scanned, world, stack, pos.offset(dir))\n            }\n        }\n        return canStart\n    }\n\n    fun scanTree(scanned: MutableSet<BlockPos>, world: World, stack: ItemStack, pos: BlockPos) {\n        if (!scanned.add(pos)) return\n        val state = world.getBlockState(pos)\n        if (state.isIn(BlockTags.LOGS) || state.isIn(BlockTags.LEAVES)) {\n            if (unsafeUse(stack, 1)) {\n                world.breakBlock(pos, true)\n                if (scanned.size < 40)\n                    Direction.values().forEach { dir ->\n                        scanTree(scanned, world, stack, pos.offset(dir))\n                    }\n            }\n        }\n    }\n\n    override fun postHit(stack: ItemStack, target: LivingEntity?, attacker: LivingEntity): Boolean {\n        if (attacker !is PlayerEntity) return false\n        val level = GamerAxeModule.REACH.getLevel(stack)\n        if (isActive(stack) && level > 0) {\n            target?.world?.getEntitiesByClass(LivingEntity::class.java, Box(target.blockPos).expand(level.toDouble())) { true }?.forEach { entity ->\n                if (unsafeUse(stack, 1)) {\n                    attacker.resetLastAttackedTicks()\n                    attacker.attack(entity)\n                }\n            }\n        }\n        return unsafeUse(stack, 1)\n    }\n\n    override fun canRepair(stack: ItemStack?, ingredient: ItemStack?): Boolean = false\n\n    override fun getCompatibleModules(itemStack: ItemStack): Array<Module> = GamerAxeModule.COMPATIBLE\n\n    fun isActive(stack: ItemStack): Boolean {\n        val tag = stack.orCreateNbt ?: return false\n        return tag.contains(\"Active\") && tag.getBoolean(\"Active\")\n    }\n\n    override fun inventoryTick(stack: ItemStack?, world: World?, entity: Entity, slot: Int, selected: Boolean) {\n        val tag = stack?.orCreateNbt ?: return\n\n        tickAnimations(stack)\n\n        if (isActive(stack) && !unsafeUse(stack, 5)) {\n            tag.putBoolean(\"Active\", false)\n        }\n    }\n\n    fun unsafeUse(stack: ItemStack, amount: Long): Boolean {\n        val itemIo = energyOf(stack)!!\n        if (itemIo.amount > amount) {\n            SimpleBatteryItem.setStoredEnergyUnchecked(stack, itemIo.amount - amount)\n            return true\n        }\n        return false\n    }\n\n    private fun tickAnimations(stack: ItemStack) {\n        val tag = stack.orCreateNbt\n        if (!tag.contains(\"Active\") || !tag.contains(\"Progress\")) return\n        val active = tag.getBoolean(\"Active\")\n        var progress = tag.getFloat(\"Progress\")\n        if (active && progress < 1) {\n            progress += 0.12f\n            if (progress >= 1)\n                tag.putBoolean(\"Active\", true)\n        } else if (!active && progress > 0) {\n            progress -= 0.12f\n            if (progress <= 0)\n                tag.putBoolean(\"Active\", false)\n        }\n        tag.putFloat(\"Progress\", progress.coerceIn(0f, 1f))\n    }\n\n    override fun getAttributeModifiers(\n        itemStack: ItemStack,\n        equipmentSlot: EquipmentSlot\n    ): Multimap<EntityAttribute, EntityAttributeModifier> {\n        val itemIo = energyOf(itemStack)\n        if (!isActive(itemStack) || itemIo == null || itemIo.amount <= 0)\n            return ImmutableMultimap.of()\n        else if (equipmentSlot == EquipmentSlot.MAINHAND) {\n            val builder = ImmutableMultimap.builder<EntityAttribute, EntityAttributeModifier>()\n            builder.put(\n                EntityAttributes.GENERIC_ATTACK_DAMAGE,\n                EntityAttributeModifier(\n                    ATTACK_DAMAGE_MODIFIER_ID,\n                    \"Tool modifier\",\n                    (itemStack.item as IRGamerAxeItem).attackDamage * (GamerAxeModule.SHARPNESS.getLevel(itemStack) / 2.0 + 1),\n                    EntityAttributeModifier.Operation.ADDITION\n                )\n            )\n            builder.put(\n                EntityAttributes.GENERIC_ATTACK_SPEED,\n                EntityAttributeModifier(\n                    ATTACK_SPEED_MODIFIER_ID,\n                    \"Tool modifier\",\n                    1.0,\n                    EntityAttributeModifier.Operation.ADDITION\n                )\n            )\n            return builder.build()\n        }\n        return getAttributeModifiers(equipmentSlot)\n    }\n\n    override fun getLevel(enchantment: Enchantment, itemStack: ItemStack): Int {\n        val module =\n            when {\n                Enchantments.LOOTING == enchantment -> GamerAxeModule.LOOTING\n                Enchantments.FIRE_ASPECT == enchantment -> GamerAxeModule.FIRE_ASPECT\n                else -> return 0\n            }\n        return module.getLevel(itemStack)\n    }\n\n    companion object {\n        private val ATTACK_DAMAGE_MODIFIER_ID = UUID.fromString(\"CB3F55D3-645C-4F38-A497-9C13A33DB5CF\")\n        private val ATTACK_SPEED_MODIFIER_ID = UUID.fromString(\"FA233E1C-4180-4865-B01B-BCCE9785ACA3\")\n    }\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/energy/IRMiningDrillItem.kt",
    "content": "package me.steven.indrev.items.energy\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.gui.tooltip.energy.EnergyTooltipData\nimport me.steven.indrev.utils.energyOf\nimport me.steven.indrev.utils.use\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.Material\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.client.item.TooltipData\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.ItemStack\nimport net.minecraft.item.PickaxeItem\nimport net.minecraft.item.ToolMaterial\nimport net.minecraft.text.Text\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\nimport java.util.*\n\nopen class IRMiningDrillItem(\n    toolMaterial: ToolMaterial,\n    private val tier: Tier,\n    private val maxStored: Double,\n    val baseMiningSpeed: Float,\n    settings: Settings\n) : PickaxeItem(toolMaterial, 0, 0F, settings.maxDamage(-1)), IREnergyItem {\n\n    override fun getMiningSpeedMultiplier(stack: ItemStack, state: BlockState?): Float {\n        val material = state?.material\n        val hasEnergy = (energyOf(stack)?.amount ?: 0) > 0\n        return when {\n            SUPPORTED_MATERIALS.contains(material) && hasEnergy -> baseMiningSpeed\n            !hasEnergy -> 0F\n            else -> super.getMiningSpeedMultiplier(stack, state)\n        }\n    }\n\n    override fun postHit(stack: ItemStack, target: LivingEntity?, attacker: LivingEntity): Boolean {\n        if (attacker !is PlayerEntity) return false\n        energyOf(attacker.inventory, attacker.inventory.selectedSlot)?.use(2) ?: return false\n        return true\n    }\n\n    override fun postMine(\n        stack: ItemStack,\n        world: World,\n        state: BlockState,\n        pos: BlockPos?,\n        miner: LivingEntity\n    ): Boolean {\n        if (miner !is PlayerEntity) return false\n        energyOf(miner.inventory, miner.inventory.selectedSlot)?.use(1) ?: return false\n        return true\n    }\n\n    override fun getItemBarColor(stack: ItemStack?): Int = getDurabilityBarColor(stack)\n\n    override fun isItemBarVisible(stack: ItemStack?): Boolean = hasDurabilityBar(stack)\n\n    override fun getItemBarStep(stack: ItemStack?): Int = getDurabilityBarProgress(stack)\n\n    override fun isEnchantable(stack: ItemStack?): Boolean = false\n\n    override fun appendTooltip(\n        stack: ItemStack,\n        world: World?,\n        tooltip: MutableList<Text>?,\n        context: TooltipContext?\n    ) {\n    }\n\n    override fun canRepair(stack: ItemStack?, ingredient: ItemStack?): Boolean = false\n\n    override fun getTooltipData(stack: ItemStack): Optional<TooltipData> {\n        val handler = energyOf(stack) ?: return Optional.empty()\n        return Optional.of(EnergyTooltipData(handler.amount, handler.capacity))\n    }\n\n    companion object {\n        val SUPPORTED_MATERIALS = arrayOf(\n            Material.METAL,\n            Material.STONE,\n            Material.WOOD,\n            Material.BAMBOO,\n            Material.COBWEB,\n            Material.PISTON,\n            Material.GOURD,\n            Material.SOIL,\n            Material.SOLID_ORGANIC,\n            Material.LEAVES,\n            Material.AGGREGATE\n        )\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/energy/IRModularDrillItem.kt",
    "content": "package me.steven.indrev.items.energy\n\nimport draylar.magna.api.BlockFinder\nimport draylar.magna.api.BlockProcessor\nimport draylar.magna.api.MagnaTool\nimport draylar.magna.api.reach.ReachDistanceHelper\nimport io.github.cottonmc.cotton.gui.client.CottonClientScreen\nimport me.steven.indrev.IndustrialRevolutionClient\nimport me.steven.indrev.api.CustomEnchantmentProvider\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.gui.screenhandlers.blockblacklister.BlockBlacklisterScreenHandler\nimport me.steven.indrev.gui.tooltip.modular.ModularTooltipData\nimport me.steven.indrev.tools.modular.DrillModule\nimport me.steven.indrev.tools.modular.IRModularItem\nimport me.steven.indrev.tools.modular.MiningToolModule\nimport me.steven.indrev.tools.modular.Module\nimport me.steven.indrev.utils.energyOf\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.gui.screen.Screen\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.client.item.TooltipData\nimport net.minecraft.enchantment.Enchantment\nimport net.minecraft.enchantment.Enchantments\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.ItemStack\nimport net.minecraft.item.ItemUsageContext\nimport net.minecraft.item.ToolMaterial\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.Hand\nimport net.minecraft.util.TypedActionResult\nimport net.minecraft.util.hit.BlockHitResult\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.RaycastContext\nimport net.minecraft.world.World\nimport java.util.*\n\nclass IRModularDrillItem(\n    toolMaterial: ToolMaterial,\n    tier: Tier,\n    maxStored: Double,\n    baseMiningSpeed: Float,\n    settings: Settings\n) : IRMiningDrillItem(toolMaterial, tier, maxStored, baseMiningSpeed, settings), MagnaTool, IRModularItem<Module>, CustomEnchantmentProvider {\n\n    override fun getMiningSpeedMultiplier(stack: ItemStack, state: BlockState?): Float {\n        val material = state?.material\n        val hasEnergy = (energyOf(stack)?.amount ?: 0) > 0\n        val level = MiningToolModule.EFFICIENCY.getLevel(stack)\n        var speedMultiplier = (level + 1) * 2\n        if (level == 5) speedMultiplier *= 50\n        return when {\n            SUPPORTED_MATERIALS.contains(material) && hasEnergy -> baseMiningSpeed + speedMultiplier.toFloat()\n            !hasEnergy -> 0F\n            else -> super.getMiningSpeedMultiplier(stack, state)\n        }\n    }\n\n    override fun appendTooltip(stack: ItemStack, world: World?, tooltip: MutableList<Text>?, context: TooltipContext?) {\n        if (Screen.hasShiftDown())\n            getInstalledTooltip(getInstalled(stack), stack, tooltip)\n        tooltip?.add(\n            TranslatableText(\"item.indrev.modular_item.tooltip\", LiteralText(\"\").append(\n                IndustrialRevolutionClient.MODULAR_CONTROLLER_KEYBINDING.boundKeyLocalizedText).formatted(Formatting.AQUA)).formatted(\n                Formatting.GRAY))\n    }\n\n    override fun use(world: World?, user: PlayerEntity, hand: Hand): TypedActionResult<ItemStack> {\n        val stack = user.getStackInHand(hand)\n        if (world!!.isClient && (DrillModule.CONTROLLED_DESTRUCTION.getLevel(stack) > 0 || DrillModule.MATTER_PROJECTOR.getLevel(stack) > 0)) {\n            MinecraftClient.getInstance().setScreen(object : CottonClientScreen(BlockBlacklisterScreenHandler()) {\n                override fun shouldPause(): Boolean = false\n            })\n            return TypedActionResult.success(stack)\n        }\n        return TypedActionResult.pass(stack)\n    }\n\n    override fun useOnBlock(context: ItemUsageContext): ActionResult {\n        if (DrillModule.MATTER_PROJECTOR.getLevel(context.stack) <= 0) return ActionResult.PASS\n\n        val world = context.world\n        val player = context.player!!\n\n        blockFinder.findPositions(world, context.player, getRadius(context.stack)).forEach { pos ->\n            val blockState = world.getBlockState(pos)\n            val offset = pos.offset(context.side)\n            if (world.getBlockState(offset).material.isReplaceable) {\n                val stackToRemove = ItemStack(blockState.block)\n                val slot = player.inventory.getSlotWithStack(stackToRemove)\n                if (slot >= 0) {\n                    val removed = player.inventory.removeStack(slot, 1)\n                    if (removed.item == stackToRemove.item && removed.count == 1) {\n                        world.setBlockState(offset, blockState)\n                    }\n                }\n            }\n        }\n        return ActionResult.success(world.isClient)\n    }\n\n    override fun getBlockFinder(): BlockFinder {\n        return object : BlockFinder {\n            override fun findPositions(\n                world: World,\n                playerEntity: PlayerEntity,\n                radius: Int,\n                depth: Int\n            ): MutableList<BlockPos> {\n                val cameraPos = playerEntity.getCameraPosVec(1f)\n                val rotation = playerEntity.getRotationVec(1f)\n                val reachDistance = ReachDistanceHelper.getReachDistance(playerEntity)\n                val combined = cameraPos.add(rotation.x * reachDistance, rotation.y * reachDistance, rotation.z * reachDistance)\n                val blockHitResult = world.raycast(RaycastContext(cameraPos, combined, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, playerEntity))\n\n                val handStack = playerEntity.getStackInHand(Hand.MAIN_HAND)\n\n                val blocks = super.findPositions(world, playerEntity, radius, depth)\n\n                val center = getCenterPosition(world, playerEntity, blockHitResult, handStack)\n\n                filterBlacklistedBlocks(center, blockHitResult, playerEntity, handStack, blocks)\n\n                return blocks\n            }\n        }\n    }\n\n    override fun getCompatibleModules(itemStack: ItemStack): Array<Module> = DrillModule.COMPATIBLE\n\n    override fun getLevel(enchantment: Enchantment, itemStack: ItemStack): Int {\n        val module =\n            when {\n                Enchantments.FORTUNE == enchantment -> DrillModule.FORTUNE\n                Enchantments.SILK_TOUCH == enchantment -> DrillModule.SILK_TOUCH\n                else -> return -1\n            }\n        return module.getLevel(itemStack)\n    }\n\n    override fun getRadius(stack: ItemStack): Int = DrillModule.RANGE.getLevel(stack)\n\n    override fun playBreakEffects(): Boolean = false\n\n    override fun getCenterPosition(\n        world: World,\n        player: PlayerEntity,\n        blockHitResult: BlockHitResult,\n        toolStack: ItemStack\n    ): BlockPos {\n        val pos = blockHitResult.blockPos\n        val radius = getRadius(toolStack)\n        return if (blockHitResult.side.axis == Direction.Axis.Y || radius < 1) pos\n        else pos.up(radius - 1)\n    }\n\n    override fun attemptBreak(\n        world: World?,\n        pos: BlockPos?,\n        player: PlayerEntity,\n        breakRadius: Int,\n        processor: BlockProcessor?\n    ): Boolean {\n        val mainHandStack = player.mainHandStack\n        return if (getRadius(mainHandStack) > 0)\n            super.attemptBreak(world, pos, player, breakRadius, processor)\n        else false\n    }\n\n    override fun getTooltipData(stack: ItemStack): Optional<TooltipData> {\n        val handler = energyOf(stack) ?: return Optional.empty()\n        val modules = getInstalled(stack)\n        return Optional.of(ModularTooltipData(handler.amount, handler.capacity, modules) { it.getLevel(stack) })\n    }\n\n    companion object {\n        fun filterBlacklistedBlocks(center: BlockPos, blockHitResult: BlockHitResult, playerEntity: PlayerEntity, stack: ItemStack, blocks: MutableList<BlockPos>) {\n            blocks.removeIf { pos ->\n\n                var offset = pos.subtract(center)\n                if (blockHitResult.side.axis.isVertical) {\n                    offset = BlockPos(offset.x, offset.z, offset.y)\n                    if (playerEntity.horizontalFacing.axis == Direction.Axis.Z) {\n                        offset = BlockPos(offset.x* -playerEntity.horizontalFacing.offsetZ, offset.y* playerEntity.horizontalFacing.offsetZ, offset.z)\n                    }\n                    else if (playerEntity.horizontalFacing.axis == Direction.Axis.X) {\n                        offset = BlockPos(offset.y *playerEntity.horizontalFacing.offsetX, offset.x* playerEntity.horizontalFacing.offsetX, offset.z)\n                    }\n                } else if (blockHitResult.side.axis == Direction.Axis.X) {\n                    offset = BlockPos(offset.z * -blockHitResult.side.offsetX, offset.y, offset.x)\n                } else if (blockHitResult.side.axis == Direction.Axis.Z) {\n                    offset = BlockPos(offset.x * blockHitResult.side.offsetZ, offset.y, offset.z)\n                }\n\n                DrillModule.getBlacklistedPositions(stack).contains(offset)\n            }\n\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/energy/IRPortableChargerItem.kt",
    "content": "package me.steven.indrev.items.energy\n\nimport me.steven.indrev.gui.tooltip.energy.EnergyTooltipData\nimport me.steven.indrev.utils.energyOf\nimport net.minecraft.client.item.TooltipData\nimport net.minecraft.entity.Entity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.inventory.Inventory\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.world.World\nimport team.reborn.energy.api.EnergyStorage\nimport team.reborn.energy.api.EnergyStorageUtil\nimport team.reborn.energy.impl.SimpleItemEnergyStorageImpl\nimport java.util.*\n\nclass IRPortableChargerItem(\n    settings: Settings,\n    maxStored: Long\n) : Item(settings), IREnergyItem {\n\n    init {\n        EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, maxStored, 16384, 16384) }, this)\n    }\n\n    override fun getItemBarColor(stack: ItemStack?): Int = getDurabilityBarColor(stack)\n\n    override fun isItemBarVisible(stack: ItemStack?): Boolean = hasDurabilityBar(stack)\n\n    override fun getItemBarStep(stack: ItemStack?): Int = getDurabilityBarProgress(stack)\n\n    override fun inventoryTick(stack: ItemStack, world: World?, entity: Entity?, slot: Int, selected: Boolean) {\n        val player = entity as? PlayerEntity ?: return\n        if (player.offHandStack != stack && player.mainHandStack != stack) return\n        chargeItemsInInv(slot, player.inventory)\n    }\n\n    override fun getTooltipData(stack: ItemStack): Optional<TooltipData> {\n        val handler = energyOf(stack) ?: return Optional.empty()\n        return Optional.of(EnergyTooltipData(handler.amount, handler.capacity))\n    }\n\n    companion object {\n        fun chargeItemsInInv(slot: Int, inventory: Inventory) {\n            val items = (0 until inventory.size())\n                .filter { s -> s != slot }\n                .mapNotNull { s -> energyOf(inventory, s) }\n            var rem = 16384L\n            items.forEach { h ->\n                if (rem <= 0) return\n                rem -= EnergyStorageUtil.move(energyOf(inventory, slot) ?: return, h, rem, null)\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/energy/MachineBlockItem.kt",
    "content": "package me.steven.indrev.items.energy\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blocks.machine.MachineBlock\nimport me.steven.indrev.gui.tooltip.energy.EnergyTooltipData\nimport me.steven.indrev.utils.buildMachineTooltip\nimport me.steven.indrev.utils.energyOf\nimport net.minecraft.block.Block\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.client.item.TooltipData\nimport net.minecraft.item.BlockItem\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.Text\nimport net.minecraft.world.World\nimport team.reborn.energy.api.EnergyStorage\nimport team.reborn.energy.impl.SimpleItemEnergyStorageImpl\nimport java.util.*\n\nclass MachineBlockItem(private val machineBlock: Block, settings: Settings) : BlockItem(machineBlock, settings) {\n\n    init {\n        val capacity = ((machineBlock as? MachineBlock)?.config?.maxEnergyStored ?: 0).toLong()\n        EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, capacity, Tier.MK4.io, 0) }, this)\n    }\n\n    override fun appendTooltip(\n        stack: ItemStack?,\n        world: World?,\n        tooltip: MutableList<Text>?,\n        options: TooltipContext?\n    ) {\n        val config = (machineBlock as? MachineBlock)?.config\n        buildMachineTooltip(config ?: return, tooltip)\n    }\n\n    override fun getTooltipData(stack: ItemStack): Optional<TooltipData> {\n        val handler = energyOf(stack) ?: return Optional.empty()\n        return Optional.of(EnergyTooltipData(handler.amount, handler.capacity))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/misc/IRCraftingToolItem.kt",
    "content": "package me.steven.indrev.items.misc\n\nimport me.steven.indrev.FabricRecipeRemainder\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.inventory.CraftingInventory\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.network.ServerPlayerEntity\nimport kotlin.random.Random\nimport kotlin.random.asJavaRandom\n\nclass IRCraftingToolItem(settings: Settings) : Item(settings), FabricRecipeRemainder {\n    override fun getRemainder(stack: ItemStack?, craftingInventory: CraftingInventory?, playerEntity: PlayerEntity?): ItemStack? {\n        return if (stack != null && stack.isDamageable) {\n            val stackCopy = stack.copy().also { it.damage(1, Random.asJavaRandom(), playerEntity as? ServerPlayerEntity) }\n            if (stackCopy.damage >= stackCopy.maxDamage) ItemStack.EMPTY\n            else stackCopy\n        } else stack\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/misc/IREnergyReaderItem.kt",
    "content": "package me.steven.indrev.items.misc\n\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.utils.energyOf\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemUsageContext\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Formatting\n\nclass IREnergyReaderItem(settings: Settings) : Item(settings) {\n    override fun useOnBlock(context: ItemUsageContext?): ActionResult {\n        if (context?.world?.isClient == true) return ActionResult.SUCCESS\n        val blockPos = context?.blockPos\n        val blockEntity = context?.world?.getBlockEntity(blockPos)\n        val machineIo = energyOf(context!!.world as ServerWorld, blockPos!!, context.side)\n        if (machineIo != null) {\n            val energy = machineIo.amount\n            val text = TranslatableText(\"item.indrev.energy_reader.use\")\n                .formatted(Formatting.BLUE)\n                .append(LiteralText(\" $energy LF\").formatted(Formatting.WHITE))\n            if (blockEntity is MachineBlockEntity<*>) {\n                val energyCost =\n                    when {\n                        blockEntity !is LazuliFluxContainerBlockEntity && blockEntity.config is BasicMachineConfig ->\n                            blockEntity.getEnergyCost()\n                        else -> -1\n                    }\n                if (energyCost > 0) {\n                    val energyCostText = TranslatableText(\n                        \"item.indrev.energy_reader.use1\",\n                        LiteralText(energyCost.toString()).formatted(Formatting.WHITE)\n                    ).formatted(Formatting.BLUE)\n                    text\n                        .append(LiteralText(\" | \").formatted(Formatting.BLACK, Formatting.BOLD))\n                        .append(energyCostText)\n                }\n            }\n            context.player?.sendMessage(text, true)\n        }\n        return ActionResult.FAIL\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/misc/IRGuideBookItem.kt",
    "content": "package me.steven.indrev.items.misc\n\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.network.ServerPlayerEntity\nimport net.minecraft.util.Hand\nimport net.minecraft.util.TypedActionResult\nimport net.minecraft.world.World\nimport vazkii.patchouli.api.PatchouliAPI\n\nclass IRGuideBookItem(settings: Settings) : Item(settings) {\n    override fun use(world: World, user: PlayerEntity, hand: Hand?): TypedActionResult<ItemStack> {\n        if (!world.isClient) {\n            PatchouliAPI.get().openBookGUI(user as ServerPlayerEntity, identifier(\"indrev\"))\n            return TypedActionResult.success(user.getStackInHand(hand))\n        }\n        return TypedActionResult.consume(user.getStackInHand(hand))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/misc/IRMachineUpgradeItem.kt",
    "content": "package me.steven.indrev.items.misc\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blocks.machine.FacingMachineBlock\nimport me.steven.indrev.blocks.machine.HorizontalFacingMachineBlock\nimport me.steven.indrev.blocks.machine.MachineBlock\nimport me.steven.indrev.registry.IRItemRegistry\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.item.ItemUsageContext\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Formatting\nimport net.minecraft.world.World\nimport java.util.*\n\nclass IRMachineUpgradeItem(settings: Settings, val from: Tier, val to: Tier) : Item(settings) {\n    override fun appendTooltip(stack: ItemStack?, world: World?, tooltip: MutableList<Text>?, context: TooltipContext?) {\n        tooltip?.add(TranslatableText(\"item.indrev.tier_upgrade_${to.toString().lowercase(Locale.getDefault())}.tooltip\").formatted(Formatting.GREEN))\n        super.appendTooltip(stack, world, tooltip, context)\n    }\n\n    override fun useOnBlock(context: ItemUsageContext?): ActionResult {\n        val world = context?.world\n        if (world?.isClient == true) return ActionResult.PASS\n        val blockPos = context?.blockPos\n        val state = world?.getBlockState(blockPos)\n        val block = state?.block as? MachineBlock ?: return ActionResult.PASS\n        val blockEntity = world.getBlockEntity(blockPos) as? MachineBlockEntity<*> ?: return ActionResult.PASS\n        if (block.tier == from) {\n            if (!blockEntity.registry.upgradeable) return ActionResult.PASS\n            \n            val inventoryTag = blockEntity.inventoryComponent?.writeNbt(NbtCompound())\n            blockEntity.inventoryComponent?.inventory?.clear()\n            val fluidTag = blockEntity.fluidComponent?.toTag(NbtCompound())\n            val temperatureTag = blockEntity.temperatureComponent?.writeNbt(NbtCompound())\n            val energy = blockEntity.energy\n\n            var newState = blockEntity.registry.block(to).defaultState\n            if (state.contains(FacingMachineBlock.FACING))\n                newState = newState.with(FacingMachineBlock.FACING, state[FacingMachineBlock.FACING])\n            else if (state.contains(HorizontalFacingMachineBlock.HORIZONTAL_FACING))\n                newState = newState.with(HorizontalFacingMachineBlock.HORIZONTAL_FACING, state[HorizontalFacingMachineBlock.HORIZONTAL_FACING])\n            world.setBlockState(blockPos, newState)\n\n            val upgradedBlockEntity = world.getBlockEntity(blockPos) as? MachineBlockEntity<*>\n                ?: throw RuntimeException(\"This should never happen, what the fuck\")\n            upgradedBlockEntity.energy = energy\n            upgradedBlockEntity.inventoryComponent?.readNbt(inventoryTag)\n            upgradedBlockEntity.fluidComponent?.fromTag(fluidTag)\n            upgradedBlockEntity.temperatureComponent?.readNbt(temperatureTag)\n\n            context.player?.getStackInHand(context.hand)?.decrement(1)\n            return ActionResult.SUCCESS\n        }\n        return super.useOnBlock(context)\n    }\n\n    companion object {\n        fun fromTier(tier: Tier): IRMachineUpgradeItem {\n            return when (tier) {\n                Tier.MK1 -> IRItemRegistry.TIER_UPGRADE_MK2\n                Tier.MK2 -> IRItemRegistry.TIER_UPGRADE_MK3\n                Tier.MK3 -> IRItemRegistry.TIER_UPGRADE_MK4\n                else -> error(\"no upgrade available\")\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/misc/IRServoItem.kt",
    "content": "package me.steven.indrev.items.misc\n\nimport me.steven.indrev.blockentities.cables.BasePipeBlockEntity\nimport me.steven.indrev.blocks.machine.pipes.BasePipeBlock\nimport me.steven.indrev.networks.EndpointData\nimport me.steven.indrev.networks.ServoNetworkState\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport me.steven.indrev.utils.component3\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.item.ItemUsageContext\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.*\nimport net.minecraft.world.World\n\nclass IRServoItem(settings: Settings, val type: EndpointData.Type) : Item(settings) {\n\n    override fun appendTooltip(\n        stack: ItemStack,\n        world: World?,\n        tooltip: MutableList<Text>,\n        context: TooltipContext?\n    ) {\n        tooltip.add(TranslatableText(\"$translationKey.tooltip\"))\n        tooltip.add(LiteralText.EMPTY)\n        val modeString = getMode(stack).toString().lowercase()\n        tooltip.add(TranslatableText(\"item.indrev.servo.mode\")\n            .append(TranslatableText(\"item.indrev.servo.mode.$modeString\").formatted(Formatting.BLUE)))\n        tooltip.add(TranslatableText(\"item.indrev.servo.mode.$modeString.tooltip\").formatted(Formatting.DARK_GRAY))\n    }\n\n    override fun use(world: World?, user: PlayerEntity, hand: Hand?): TypedActionResult<ItemStack> {\n        if (world?.isClient == true) return TypedActionResult.pass(user.getStackInHand(hand))\n        val stack = user.getStackInHand(hand)\n        val newMode = getMode(stack).next()\n        stack.orCreateNbt.putString(\"mode\", newMode.toString())\n        user.sendMessage(TranslatableText(\"item.indrev.servo.mode\")\n            .append(TranslatableText(\"item.indrev.servo.mode.${newMode.toString().lowercase()}\").formatted(Formatting.BLUE)), true)\n        return TypedActionResult.consume(stack)\n    }\n\n    override fun useOnBlock(context: ItemUsageContext): ActionResult {\n        val world = context.world\n        if (world.isClient) return ActionResult.CONSUME\n        val hand = context.hand\n        val stack = context.stack\n        val hit = context.hitPos\n        val pos = context.blockPos\n\n        val state = world.getBlockState(pos)\n        val block = state.block\n        if (block !is BasePipeBlock) return ActionResult.PASS\n\n        if (world is ServerWorld && hand == Hand.MAIN_HAND) {\n            val dir = BasePipeBlock.getSideFromHit(hit, pos!!)\n            val blockEntity = world.getBlockEntity(pos) as? BasePipeBlockEntity ?: return ActionResult.PASS\n            if (dir != null && blockEntity.connections[dir]!!.isConnected()) {\n                val network = block.type.getNetworkState(world) as? ServoNetworkState?\n                network?.also { networkState ->\n                    if (networkState.networksByPos.get(pos.asLong())?.containers?.containsKey(pos.offset(dir)) == true) {\n                        val (x, y, z) = hit\n                        if (networkState.hasServo(pos, dir)) {\n                            when (networkState.getEndpointData(pos, dir)?.type) {\n                                EndpointData.Type.OUTPUT ->\n                                    ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_OUTPUT))\n                                EndpointData.Type.RETRIEVER ->\n                                    ItemScatterer.spawn(world, x, y, z, ItemStack(IRItemRegistry.SERVO_RETRIEVER))\n                                else -> {}\n                            }\n                        }\n                        val data = networkState.getEndpointData(pos, dir, true) ?: return@also context.player!!.sendMessage(LiteralText(\"Failed to put servo\"), true)\n                        data.type = type\n                        data.mode = getMode(stack)\n                        networkState.version++\n                        stack.decrement(1)\n\n                        networkState.markDirty()\n                        return ActionResult.SUCCESS\n                    }\n                }\n            }\n        }\n        return ActionResult.PASS\n    }\n\n    companion object {\n        fun getMode(itemStack: ItemStack): EndpointData.Mode {\n            val m = itemStack.orCreateNbt.getString(\"mode\")\n            if (m.isNullOrEmpty()) return EndpointData.Mode.NEAREST_FIRST\n            return EndpointData.Mode.valueOf(m.uppercase())\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/misc/OreDataCardItem.kt",
    "content": "package me.steven.indrev.items.misc\n\nimport me.steven.indrev.api.OreDataCards\nimport me.steven.indrev.gui.tooltip.oredatacards.OreDataCardTooltipData\nimport me.steven.indrev.utils.itemSettings\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.client.item.TooltipData\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.world.World\nimport java.util.*\nimport kotlin.math.roundToInt\n\nclass OreDataCardItem : Item(itemSettings().maxCount(1)) {\n    override fun appendTooltip(\n        stack: ItemStack,\n        world: World?,\n        tooltip: MutableList<Text>,\n        context: TooltipContext?\n    ) {\n        val data = OreDataCards.readNbt(stack) ?: return\n        if (!data.isValid()) {\n            tooltip.add(LiteralText(\"Invalid data card!\").formatted(Formatting.RED))\n        }\n    }\n\n    override fun getName(stack: ItemStack): Text {\n        return if (OreDataCards.readNbt(stack) == null) TranslatableText(\"item.indrev.empty_ore_data_card\")\n        else super.getName(stack)\n    }\n\n    override fun isItemBarVisible(stack: ItemStack): Boolean {\n        return OreDataCards.readNbt(stack) != null\n    }\n\n    override fun getItemBarColor(stack: ItemStack): Int {\n        val data = OreDataCards.readNbt(stack) ?: return -1\n        return if (!data.isValid()) return 0xff0000 else 0xffffff\n    }\n\n    override fun getItemBarStep(stack: ItemStack): Int {\n        val data = OreDataCards.readNbt(stack) ?: return 0\n        if (!data.isValid()) return 13\n        return 13-((OreDataCards.MAX_SIZE - (data.maxCycles - data.used)) * 13.0f / OreDataCards.MAX_SIZE.toDouble()).roundToInt()\n    }\n\n    override fun getTooltipData(stack: ItemStack): Optional<TooltipData> {\n        val data = OreDataCards.readNbt(stack)\n        if (data?.isValid() != true) return Optional.empty()\n        return Optional.of(OreDataCardTooltipData(data))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/misc/Tools.kt",
    "content": "package me.steven.indrev.items.misc\n\nimport net.minecraft.item.*\n\n//fuck Mojang with protected shit\n\nclass IRBasicSword(material: ToolMaterial, attackDamage: Int, attackSpeed: Float, settings: Settings) : SwordItem(material, attackDamage, attackSpeed, settings)\nclass IRBasicShovel(material: ToolMaterial, attackDamage: Float, attackSpeed: Float, settings: Settings) : ShovelItem(material, attackDamage, attackSpeed, settings)\nclass IRBasicPickaxe(material: ToolMaterial, attackDamage: Int, attackSpeed: Float, settings: Settings) : PickaxeItem(material, attackDamage, attackSpeed, settings)\nclass IRBasicHoe(material: ToolMaterial, attackDamage: Int, attackSpeed: Float, settings: Settings) : HoeItem(material, attackDamage, attackSpeed, settings)\nclass IRBasicAxe(material: ToolMaterial, attackDamage: Float, attackSpeed: Float, settings: Settings) : AxeItem(material, attackDamage, attackSpeed, settings)"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/models/TankItemBakedModel.kt",
    "content": "package me.steven.indrev.items.models\n\nimport alexiil.mc.lib.attributes.fluid.volume.FluidVolume\nimport com.mojang.datafixers.util.Pair\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry\nimport net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView\nimport net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter\nimport net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel\nimport net.fabricmc.fabric.api.renderer.v1.render.RenderContext\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.render.model.*\nimport net.minecraft.client.render.model.json.ModelOverrideList\nimport net.minecraft.client.render.model.json.ModelTransformation\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.client.util.ModelIdentifier\nimport net.minecraft.client.util.SpriteIdentifier\nimport net.minecraft.fluid.Fluids\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.BlockRenderView\nimport java.util.*\nimport java.util.function.Function\nimport java.util.function.Supplier\n\nclass TankItemBakedModel : UnbakedModel, BakedModel, FabricBakedModel {\n\n    private val modelIdentifier = ModelIdentifier(\n        identifier(\"tank\"),\n        \"down=false,up=false\"\n    )\n\n    private val transform: ModelTransformation by lazy {\n        MinecraftClient.getInstance().bakedModelManager.getModel(\n            ModelIdentifier(Identifier(\"stone\"), \"\")\n        ).transformation\n    }\n\n    override fun isVanillaAdapter(): Boolean = false\n\n    override fun emitItemQuads(stack: ItemStack, randSupplier: Supplier<Random>, context: RenderContext) {\n        val tankModel = MinecraftClient.getInstance().bakedModelManager.getModel(\n            modelIdentifier\n        )\n        context.fallbackConsumer().accept(tankModel)\n\n        val stackTag = stack.orCreateNbt\n\n        val volume = readNbt(stackTag) ?: return\n\n        val player = MinecraftClient.getInstance().player\n        val world = player?.world\n        val pos = player?.blockPos\n\n        val fluid = volume.rawFluid ?: Fluids.EMPTY\n        val fluidRenderHandler = FluidRenderHandlerRegistry.INSTANCE.get(fluid) ?: return\n        val fluidColor = fluidRenderHandler.getFluidColor(world, pos, fluid.defaultState)\n        val fluidSprite = fluidRenderHandler.getFluidSprites(world, pos, fluid.defaultState)[0]\n        val color = 255 shl 24 or fluidColor\n        context.pushTransform { quad ->\n            quad.spriteColor(0, color, color, color, color)\n            true\n        }\n\n        val emitter = context.emitter\n\n        val p = (volume.amount().asLong(1L) / 8f).coerceAtMost(0.9f)\n        emitter.draw(Direction.UP, fluidSprite, 0.09375f, 0.09f, 0.9f, 0.90625f, (0.9f - p) + 0.09575f)\n        emitter.draw(Direction.NORTH, fluidSprite, 0.09375f, 0.06f, 0.9f, p, 0.09575f)\n        emitter.draw(Direction.SOUTH, fluidSprite, 0.09375f, 0.06f, 0.9f, p, 0.09575f)\n        emitter.draw(Direction.EAST, fluidSprite, 0.09375f, 0.06f, 0.9f, p, 0.09575f)\n        emitter.draw(Direction.WEST, fluidSprite, 0.09375f, 0.06f, 0.9f, p, 0.09575f)\n\n        context.popTransform()\n    }\n\n    private fun QuadEmitter.draw(\n        side: Direction,\n        sprite: Sprite,\n        left: Float,\n        bottom: Float,\n        right: Float,\n        top: Float,\n        depth: Float\n    ) {\n        square(side, left, bottom, right, top, depth)\n        spriteBake(0, sprite, MutableQuadView.BAKE_LOCK_UV)\n        spriteColor(0, -1, -1, -1, -1)\n        emit()\n    }\n\n    override fun emitBlockQuads(\n        p0: BlockRenderView?,\n        p1: BlockState?,\n        p2: BlockPos?,\n        p3: Supplier<Random>?,\n        p4: RenderContext?\n    ) {\n    }\n\n    override fun getOverrides(): ModelOverrideList = ModelOverrideList.EMPTY\n\n    override fun getQuads(state: BlockState?, face: Direction?, random: Random?): MutableList<BakedQuad> = mutableListOf()\n\n    override fun getParticleSprite() = null\n\n    override fun hasDepth(): Boolean = false\n\n    override fun getTransformation(): ModelTransformation = transform\n\n    override fun useAmbientOcclusion(): Boolean = true\n\n    override fun isSideLit(): Boolean = false\n\n    override fun isBuiltin(): Boolean = false\n\n    private fun readNbt(tag: NbtCompound?): FluidVolume? {\n        val tanksTag = tag?.getCompound(\"tanks\")\n        tanksTag?.keys?.forEach { key ->\n            val tankTag = tanksTag.getCompound(key)\n            return FluidVolume.fromTag(tankTag.getCompound(\"fluids\"))\n        }\n        return null\n    }\n\n    override fun getModelDependencies(): MutableCollection<Identifier> = mutableListOf()\n\n    override fun getTextureDependencies(\n        unbakedModelGetter: Function<Identifier, UnbakedModel>?,\n        unresolvedTextureReferences: MutableSet<Pair<String, String>>?\n    ): MutableCollection<SpriteIdentifier> = mutableListOf()\n\n    override fun bake(\n        loader: ModelLoader?,\n        textureGetter: Function<SpriteIdentifier, Sprite>?,\n        rotationContainer: ModelBakeSettings?,\n        modelId: Identifier?\n    ): BakedModel = this\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/upgrade/Enhancer.kt",
    "content": "package me.steven.indrev.items.upgrade\n\nimport me.steven.indrev.components.EnhancerComponent\nimport me.steven.indrev.config.IRConfig\n\nenum class Enhancer {\n    SPEED, BUFFER, BLAST_FURNACE, SMOKER, DAMAGE;\n\n    companion object {\n        val DEFAULT = arrayOf(SPEED, BUFFER)\n        val FURNACE = arrayOf(SPEED, BUFFER, BLAST_FURNACE, SMOKER)\n\n        fun getDamageMultiplier(enhancers: Map<Enhancer, Int>): Double {\n            return (IRConfig.upgrades.damageUpgradeModifier * (enhancers[DAMAGE] ?: 0).toDouble()).coerceAtLeast(1.0)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/items/upgrade/IREnhancerItem.kt",
    "content": "package me.steven.indrev.items.upgrade\n\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.gui.IRInventoryScreen\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.world.World\nimport java.util.*\n\nclass IREnhancerItem(settings: Settings, val enhancer: Enhancer) : Item(settings) {\n    override fun appendTooltip(stack: ItemStack?, world: World?, tooltip: MutableList<Text>?, context: TooltipContext?) {\n        tooltip?.add(TranslatableText(\"item.indrev.${enhancer.toString().lowercase(Locale.getDefault())}_enhancer.tooltip\").formatted(Formatting.GREEN))\n        tooltip?.add(LiteralText.EMPTY)\n        val currentScreen = MinecraftClient.getInstance().currentScreen\n        if (currentScreen is IRInventoryScreen<*>) {\n            val handler = currentScreen.screenHandler as? IRGuiScreenHandler ?: return\n            handler.ctx.run { _, pos ->\n                val blockEntity = world?.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return@run\n                val enhancerComponent = blockEntity.enhancerComponent ?: return@run\n                if (!enhancerComponent.compatible.contains(enhancer))\n                    tooltip?.add(TranslatableText(\"item.indrev.enhancers.incompatible\").formatted(Formatting.DARK_RED))\n                else\n                    tooltip?.add(TranslatableText(\"item.indrev.enhancers.count\", enhancerComponent.maxSlotCount(enhancer)).formatted(Formatting.AQUA))\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/EndpointData.kt",
    "content": "package me.steven.indrev.networks\n\nimport me.steven.indrev.utils.ItemFilter\nimport me.steven.indrev.utils.fluidStorageOf\nimport me.steven.indrev.utils.itemStorageOf\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.transaction.Transaction\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.server.world.ServerWorld\nimport java.util.*\nimport kotlin.random.Random\n\ndata class EndpointData(var type: Type, var mode: Mode?) {\n\n    fun writeNbt(tag: NbtCompound): NbtCompound {\n        tag.putInt(\"t\", type.ordinal)\n        if (mode != null)\n            tag.putInt(\"m\", mode!!.ordinal)\n        return tag\n    }\n\n    fun readNbt(tag: NbtCompound): EndpointData {\n        val type = Type.VALUES[tag.getInt(\"t\")]\n        val mode = if (tag.contains(\"m\")) Mode.VALUES[tag.getInt(\"m\")] else null\n        return EndpointData(type, mode)\n    }\n\n    enum class Type {\n        RETRIEVER,\n        OUTPUT,\n        INPUT;\n\n        companion object {\n            val VALUES = values()\n        }\n    }\n\n    enum class Mode {\n        ROUND_ROBIN {\n            override fun getFluidSorter(world: ServerWorld, type: Type, filter: (FluidVariant) -> Boolean): (Array<Any?>) -> Unit {\n                return { array ->\n                    Transaction.openOuter().use { tx ->\n                        Arrays.sort(array,\n                            if (type == Type.RETRIEVER)\n                                Comparator.comparing<Any?, Long> { node ->\n                                    node as Node\n                                    fluidStorageOf(world, node.target, node.direction)?.iterable(tx)?.sumOf { v -> if (!filter(v.resource)) 0 else v.amount } ?: 0\n                                }.reversed()\n                            else\n                                Comparator.comparing { node ->\n                                    node as Node\n                                    fluidStorageOf(world, node.target, node.direction)?.iterable(tx)?.sumOf { v -> if (!filter(v.resource)) 0 else v.amount } ?: 0\n                                })\n                        tx.abort()\n                    }\n                }\n            }\n\n            override fun getItemSorter(world: ServerWorld, type: Type, filter: ItemFilter): (Array<Any?>) -> Unit {\n                return { array ->\n                    Transaction.openOuter().use { tx ->\n                        Arrays.sort(array,\n                            (if (type == Type.RETRIEVER)\n                                Comparator.comparing<Any, Long> { node ->\n                                    node as Node\n                                    itemStorageOf(world, node.target, node.direction)?.iterable(tx)?.sumOf { v -> if (!filter(v.resource)) 0 else v.amount } ?: 0\n                                }.reversed()\n                            else\n                                Comparator.comparing { node ->\n                                    node as Node\n                                    itemStorageOf(world, node.target, node.direction)?.iterable(tx)?.sumOf { v -> if (!filter(v.resource)) 0 else v.amount } ?: 0\n                                })\n                        )\n                    }\n\n                }\n            }\n        },\n        FURTHEST_FIRST {\n            override fun getFluidSorter(world: ServerWorld, type: Type, filter: (FluidVariant) -> Boolean): (Array<Any?>) -> Unit {\n                return { array ->\n                    Arrays.sort(array) { first, second ->\n                        first as Node\n                        second as Node\n                        when {\n                            first.dist > second.dist -> -1\n                            first.dist < second.dist -> 1\n                            else -> 0\n                        }\n                    }\n                }\n            }\n\n            override fun getItemSorter(world: ServerWorld, type: Type, filter: ItemFilter): (Array<Any?>) -> Unit {\n                return { array ->\n                    Arrays.sort(array) { first, second ->\n                        first as Node\n                        second as Node\n                        when {\n                            first.dist > second.dist -> -1\n                            first.dist < second.dist -> 1\n                            else -> 0\n                        }\n                    }\n                }\n            }\n        },\n        NEAREST_FIRST {\n            override fun getFluidSorter(world: ServerWorld, type: Type, filter: (FluidVariant) -> Boolean): (Array<Any?>) -> Unit {\n                return { array -> Arrays.sort(array) }\n            }\n\n            override fun getItemSorter(world: ServerWorld, type: Type, filter: ItemFilter): (Array<Any?>) -> Unit {\n                return { array -> Arrays.sort(array) }\n            }\n        },\n        RANDOM {\n            override fun getFluidSorter(world: ServerWorld, type: Type, filter: (FluidVariant) -> Boolean): (Array<Any?>) -> Unit {\n                return { array -> array.shuffle() }\n            }\n\n            override fun getItemSorter(world: ServerWorld, type: Type, filter: ItemFilter): (Array<Any?>) -> Unit {\n                return { array -> array.shuffle() }\n            }\n        };\n\n        abstract fun getFluidSorter(world: ServerWorld, type: Type, filter: (FluidVariant) -> Boolean): (Array<Any?>) -> Unit\n\n        abstract fun getItemSorter(world: ServerWorld, type: Type, filter: ItemFilter): (Array<Any?>) -> Unit\n\n        fun next(): Mode {\n            return when (this) {\n                ROUND_ROBIN -> FURTHEST_FIRST\n                FURTHEST_FIRST -> NEAREST_FIRST\n                NEAREST_FIRST -> RANDOM\n                RANDOM -> ROUND_ROBIN\n            }\n        }\n\n        companion object {\n            val R = Random(System.currentTimeMillis())\n            val VALUES = values()\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/Network.kt",
    "content": "package me.steven.indrev.networks\n\nimport it.unimi.dsi.fastutil.longs.LongOpenHashSet\nimport it.unimi.dsi.fastutil.objects.Object2ObjectFunction\nimport it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap\nimport it.unimi.dsi.fastutil.objects.ObjectOpenHashSet\nimport me.steven.indrev.networks.client.ClientNetworkInfo\nimport me.steven.indrev.networks.client.ClientServoNetworkInfo\nimport me.steven.indrev.networks.client.node.ClientServoNodeInfo\nimport me.steven.indrev.networks.energy.EnergyNetwork\nimport me.steven.indrev.networks.factory.ENERGY_NET_FACTORY\nimport me.steven.indrev.networks.factory.FLUID_NET_FACTORY\nimport me.steven.indrev.networks.factory.ITEM_NET_FACTORY\nimport me.steven.indrev.networks.factory.NetworkFactory\nimport me.steven.indrev.networks.fluid.FluidNetwork\nimport me.steven.indrev.networks.fluid.FluidNetworkState\nimport me.steven.indrev.networks.item.ItemNetwork\nimport me.steven.indrev.networks.item.ItemNetworkState\nimport me.steven.indrev.utils.energyNetworkState\nimport me.steven.indrev.utils.fluidNetworkState\nimport me.steven.indrev.utils.itemNetworkState\nimport net.minecraft.block.Block\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport java.util.*\n\nabstract class Network(\n    val type: Type<*>,\n    val world: ServerWorld,\n    val pipes: MutableSet<BlockPos> = ObjectOpenHashSet(),\n    val containers: MutableMap<BlockPos, EnumSet<Direction>> = Object2ObjectOpenHashMap()\n) {\n\n    val state = type.getNetworkState(world)\n\n    protected val queue = Object2ObjectOpenHashMap<BlockPos, MutableList<Node>>(containers.size)\n\n    protected fun isQueueValid(): Boolean {\n        if (queue.isEmpty() && containers.isNotEmpty()) {\n            buildQueue()\n        }\n        return queue.isNotEmpty()\n    }\n\n    protected fun buildQueue() {\n        queue.clear()\n        containers.forEach { (pos, _) ->\n            find(pos, pos, 0, LongOpenHashSet())\n        }\n    }\n\n    protected fun find(source: BlockPos, blockPos: BlockPos, count: Int, s: LongOpenHashSet) {\n        DIRECTIONS.forEach { dir ->\n            val offset = blockPos.offset(dir.opposite)\n            if (pipes.contains(offset) && s.add(offset.asLong())) {\n                find(source, offset, count + 1, s)\n            }\n            if (source != offset && containers.contains(offset) && containers[offset]!!.contains(dir)) {\n                queue.computeIfAbsent(source, Object2ObjectFunction { ArrayList(containers.size) }).add(Node(source, offset, count, dir))\n            }\n        }\n    }\n\n    abstract fun tick(world: ServerWorld)\n\n    open fun remove() {\n        state.remove(this)\n    }\n\n    @Suppress(\"UNCHECKED_CAST\")\n    open fun appendPipe(block: Block, blockPos: BlockPos) {\n        pipes.add(blockPos)\n\n        state[blockPos] = this\n    }\n\n    open fun appendContainer(blockPos: BlockPos, direction: Direction) {\n        containers.computeIfAbsent(blockPos) { EnumSet.noneOf(Direction::class.java) }.add(direction)\n        state[blockPos] = this\n    }\n\n    companion object {\n\n        val DIRECTIONS = Direction.values()\n\n        fun <T : Network> handleBreak(state: NetworkState<T>, pos: BlockPos) {\n            state.networksByPos[pos.asLong()]?.remove()\n            DIRECTIONS.forEach {\n                val offset = pos.offset(it)\n                handleUpdate(state, offset)\n            }\n        }\n\n        fun <T : Network> handleUpdate(state: NetworkState<T>, pos: BlockPos) {\n            state.networksByPos[pos.asLong()]?.remove()\n            state.queueUpdate(pos.asLong(), true)\n        }\n    }\n    abstract class Type<T : Network>(val key: String) {\n\n        abstract val factory: NetworkFactory<T>\n\n        abstract fun createEmpty(world: ServerWorld): T\n\n        abstract fun getNetworkState(world: ServerWorld): NetworkState<T>\n\n        open fun createClientNetworkInfo(world: ServerWorld): ClientNetworkInfo<*>? {\n            val state = getNetworkState(world)\n            if (state !is ServoNetworkState<*>) return null\n            return ClientServoNetworkInfo().also {\n                state.endpointData.forEach { (pos, data) ->\n                    val info = ClientServoNodeInfo(pos, Object2ObjectOpenHashMap())\n                    data.forEach { (dir, endpointData) ->\n                        info.servos[dir] = endpointData.type\n                    }\n                    it.pipes[pos] = info\n                }\n            }\n        }\n\n        companion object {\n            val ENERGY = object : Type<EnergyNetwork>(NetworkState.ENERGY_KEY) {\n\n                override val factory: NetworkFactory<EnergyNetwork> = ENERGY_NET_FACTORY\n\n                override fun createEmpty(world: ServerWorld): EnergyNetwork = EnergyNetwork(world)\n\n                override fun getNetworkState(world: ServerWorld): NetworkState<EnergyNetwork> = world.energyNetworkState\n            }\n            val FLUID = object : Type<FluidNetwork>(NetworkState.FLUID_KEY) {\n\n                override val factory: NetworkFactory<FluidNetwork> = FLUID_NET_FACTORY\n\n                override fun createEmpty(world: ServerWorld): FluidNetwork = FluidNetwork(world)\n\n                override fun getNetworkState(world: ServerWorld): FluidNetworkState = world.fluidNetworkState\n            }\n            val ITEM = object : Type<ItemNetwork>(NetworkState.ITEM_KEY) {\n\n                override val factory: NetworkFactory<ItemNetwork> = ITEM_NET_FACTORY\n\n                override fun createEmpty(world: ServerWorld): ItemNetwork = ItemNetwork(world)\n\n                override fun getNetworkState(world: ServerWorld): ItemNetworkState = world.itemNetworkState\n            }\n\n            fun valueOf(string: String): Type<*> {\n                return when (string) {\n                    NetworkState.ENERGY_KEY -> ENERGY\n                    NetworkState.FLUID_KEY -> FLUID\n                    NetworkState.ITEM_KEY -> ITEM\n                    else -> throw IllegalArgumentException(\"Unknown network type $string\")\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/NetworkEvents.kt",
    "content": "package me.steven.indrev.networks\n\nimport me.steven.indrev.blockentities.cables.BasePipeBlockEntity\nimport net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents\nimport net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents\nimport net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.server.MinecraftServer\nimport net.minecraft.server.world.ServerWorld\n\nobject NetworkEvents : ServerTickEvents.EndWorldTick, ServerBlockEntityEvents.Load, ServerLifecycleEvents.ServerStopping {\n    override fun onEndTick(world: ServerWorld) {\n        Network.Type.ENERGY.getNetworkState(world).tick(world)\n        Network.Type.FLUID.getNetworkState(world).tick(world)\n        Network.Type.ITEM.getNetworkState(world).tick(world)\n    }\n\n    override fun onServerStopping(server: MinecraftServer) {\n        server.worlds.forEach { world ->\n            Network.Type.ENERGY.getNetworkState(world).markDirty()\n        }\n    }\n\n    override fun onLoad(blockEntity: BlockEntity, world: ServerWorld) {\n        if (blockEntity is BasePipeBlockEntity) {\n            val networkState = blockEntity.pipeType.getNetworkState(world)\n            networkState.queueUpdate(blockEntity.pos.asLong())\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/NetworkState.kt",
    "content": "package me.steven.indrev.networks\n\nimport it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap\nimport it.unimi.dsi.fastutil.longs.LongOpenHashSet\nimport it.unimi.dsi.fastutil.objects.ObjectOpenHashSet\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.PersistentState\n\nopen class NetworkState<T : Network>(val type: Network.Type<T>, val world: ServerWorld) : PersistentState() {\n\n    val networksByPos = Long2ObjectOpenHashMap<T>()\n    val networks = ObjectOpenHashSet<Network>()\n\n    private val queuedUpdates = LongOpenHashSet()\n    val updatedPositions = LongOpenHashSet()\n\n    fun queueUpdate(pos: Long, force: Boolean = false) {\n        if (!networksByPos.contains(pos) || force) queuedUpdates.add(pos)\n    }\n\n    operator fun set(pos: BlockPos, network: Network) {\n        networksByPos[pos.asLong()] = network as T\n    }\n\n    open fun remove(network: Network) {\n        network.pipes.forEach { pos ->\n            networksByPos.remove(pos.asLong())\n            onRemoved(pos)\n        }\n        network.containers.forEach { (pos, _) ->\n            networksByPos.remove(pos.asLong())\n            onRemoved(pos)\n        }\n        networks.remove(network)\n    }\n\n    open fun add(network: Network) {\n        this.networks.add(network)\n    }\n\n    open fun tick(world: ServerWorld) {\n        // this is to avoid a weird CME I cannot reproduce\n        // something is queueing an update while going through the other ones (nothing is ever removed from it until the .clear())\n        // so I copy and clear the original before iterating so if anything is added while iterating, it will be processed in the next tick.\n        // TODO find this CME and fix it :)\n        val copy = LongOpenHashSet(queuedUpdates)\n        queuedUpdates.clear()\n        copy.forEach { pos ->\n            if (!updatedPositions.contains(pos)) {\n                val network = type.factory.deepScan(type, world, BlockPos.fromLong(pos))\n                if (network.pipes.isNotEmpty())\n                    add(network)\n                else\n                    remove(network)\n            }\n        }\n        updatedPositions.clear()\n        world.profiler.push(\"indrev_${type.key}NetworkTick\")\n        networks.forEach { network -> network.tick(world) }\n        world.profiler.pop()\n    }\n\n    open fun onRemoved(pos: BlockPos) {\n    }\n\n    open fun onSet(blockPos: BlockPos, network: T) {\n    }\n\n\n    override fun writeNbt(tag: NbtCompound): NbtCompound {\n\n        return tag\n    }\n\n    companion object {\n        const val ENERGY_KEY = \"indrev_networks\"\n        const val FLUID_KEY = \"indrev_fluid_networks\"\n        const val ITEM_KEY = \"indrev_item_networks\"\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/Node.kt",
    "content": "package me.steven.indrev.networks\n\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\n\ndata class Node(val origin: BlockPos, val target: BlockPos, val dist: Int, val direction: Direction) : Comparable<Node> {\n    override fun compareTo(other: Node): Int {\n        return when {\n            this.dist > other.dist -> 1\n            this.dist < other.dist -> -1\n            else -> 0\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/ServoNetworkState.kt",
    "content": "package me.steven.indrev.networks\n\nimport it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap\nimport it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap\nimport it.unimi.dsi.fastutil.objects.Object2ObjectFunction\nimport it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap\nimport me.steven.indrev.packets.client.SyncNetworkServosPacket\nimport net.fabricmc.fabric.api.networking.v1.PacketByteBufs\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.nbt.NbtList\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport java.util.*\nimport java.util.function.LongFunction\n\nabstract class ServoNetworkState<T : Network>(type: Network.Type<T>, world: ServerWorld) : NetworkState<T>(type, world) {\n    val endpointData = Long2ObjectOpenHashMap<Object2ObjectOpenHashMap<Direction, EndpointData>>()\n    private val recentlyRemoved = Long2ObjectOpenHashMap<Object2ObjectOpenHashMap<Direction, EndpointData>>()\n\n    private val syncedMaps = Object2IntOpenHashMap<UUID>()\n\n    init {\n        syncedMaps.defaultReturnValue(-1)\n    }\n\n    var version = 1\n\n    override fun tick(world: ServerWorld) {\n        super.tick(world)\n\n        sync(world)\n        clearCachedData(false)\n    }\n\n    fun sync(world: ServerWorld) {\n        world.players.forEach { player ->\n            val v = syncedMaps.getInt(player.uuid)\n            if (version > v) {\n                val buf = PacketByteBufs.create()\n                buf.writeString(type.key)\n                type.createClientNetworkInfo(world)?.write(buf)\n                ServerPlayNetworking.send(player, SyncNetworkServosPacket.SYNC_NETWORK_SERVOS, buf)\n                syncedMaps[player.uuid] =version\n            }\n        }\n    }\n\n    fun onDimChange(playerEntity: PlayerEntity) {\n        syncedMaps.removeInt(playerEntity.uuid)\n    }\n\n    override fun onRemoved(pos: BlockPos) {\n        version++\n        super.onRemoved(pos)\n        if (endpointData.containsKey(pos.asLong()))\n            recentlyRemoved[pos.asLong()] = endpointData.remove(pos.asLong())\n    }\n\n    override fun onSet(blockPos: BlockPos, network: T) {\n        version++\n        if (recentlyRemoved.containsKey(blockPos.asLong())) {\n            endpointData[blockPos.asLong()] = recentlyRemoved.remove(blockPos.asLong())\n        }\n    }\n\n    fun hasServo(blockPos: BlockPos, direction: Direction): Boolean {\n        return endpointData.get(blockPos.asLong())?.get(direction)?.type?.let { it != EndpointData.Type.INPUT } == true\n    }\n\n    fun getEndpointData(pos: BlockPos, direction: Direction, createIfAbsent: Boolean = false): EndpointData? {\n        return getEndpointData(pos.asLong(), direction, createIfAbsent)\n    }\n\n    fun getEndpointData(pos: Long, direction: Direction, createIfAbsent: Boolean = false): EndpointData? {\n        return if (createIfAbsent)\n            endpointData.computeIfAbsent(pos, LongFunction { Object2ObjectOpenHashMap() })\n                .computeIfAbsent(direction, Object2ObjectFunction { EndpointData(EndpointData.Type.INPUT, null) })\n        else\n            endpointData.get(pos)?.get(direction)\n    }\n\n    fun removeEndpointData(pos: BlockPos, direction: Direction): EndpointData? {\n        version++\n        val datas = endpointData.get(pos.asLong()) ?: return null\n        val d = datas.remove(direction)\n        if (datas.isEmpty()) endpointData.remove(pos.asLong())\n        return d\n    }\n\n    open fun clearCachedData(importCache: Boolean) {\n        if (importCache) {\n            recentlyRemoved.forEach { e -> endpointData[e.key] = e.value }\n        }\n        this.recentlyRemoved.clear()\n    }\n\n    override fun writeNbt(tag: NbtCompound): NbtCompound {\n        val modesTag = NbtList()\n        endpointData.forEach { (pos, modes) ->\n            val sidesTag = NbtList()\n            modes.forEach { (dir, mode) ->\n                val t = NbtCompound()\n                t.put(dir.ordinal.toString(), mode.writeNbt(NbtCompound()))\n                sidesTag.add(t)\n            }\n            val posTag = NbtCompound()\n            posTag.putLong(\"pos\", pos)\n            posTag.put(\"sides\", sidesTag)\n            modesTag.add(posTag)\n\n        }\n        tag.put(\"modes\", modesTag)\n        return super.writeNbt(tag)\n    }\n\n    companion object {\n        fun <T : Network, P : ServoNetworkState<T>> readNbt(tag: NbtCompound, supplier: () -> P): P {\n            val state = supplier()\n            val modesTag = tag.getList(\"modes\", 10)\n            modesTag.forEach { posTag ->\n                posTag as NbtCompound\n                val pos = posTag.getLong(\"pos\")\n                val map = Object2ObjectOpenHashMap<Direction, EndpointData>()\n                val sidesTag = posTag.getList(\"sides\", 10)\n                sidesTag.forEach { t ->\n                    t as NbtCompound\n                    t.keys.forEach { id ->\n                        val data = EndpointData(EndpointData.Type.INPUT, EndpointData.Mode.NEAREST_FIRST).readNbt(t.getCompound(id))\n                        val dir = Direction.values()[id.toInt()]\n                        map[dir] = data\n                    }\n                }\n\n                state.endpointData[pos] = map\n            }\n\n            return state\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/client/ClientNetworkInfo.kt",
    "content": "package me.steven.indrev.networks.client\n\nimport me.steven.indrev.networks.client.node.ClientNodeInfo\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.minecraft.network.PacketByteBuf\n\ninterface ClientNetworkInfo<T : ClientNodeInfo> {\n\n    fun write(buf: PacketByteBuf)\n\n    @Environment(EnvType.CLIENT)\n    fun read(buf: PacketByteBuf)\n\n    fun createNodes(): List<T>\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/client/ClientNetworkState.kt",
    "content": "package me.steven.indrev.networks.client\n\nimport it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.client.node.ClientNodeInfo\nimport me.steven.indrev.utils.component1\nimport me.steven.indrev.utils.component2\nimport me.steven.indrev.utils.component3\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.util.math.BlockPos\n\nclass ClientNetworkState<T : Network>(val type: Network.Type<T>) {\n\n    private val nodes = Long2ObjectOpenHashMap<ClientNodeInfo>()\n\n    fun processPacket(buf: PacketByteBuf, client: MinecraftClient) {\n\n        val positions = hashSetOf<BlockPos>()\n\n        val info = ClientServoNetworkInfo()\n        info.read(buf)\n\n        info.pipes.forEach { (pos, info) ->\n            val oldInfo = nodes[pos]\n            if (info != oldInfo) {\n                positions.add(BlockPos.fromLong(pos))\n            }\n        }\n        client.execute {\n\n            val before = nodes.clone()\n\n            nodes.clear()\n            nodes.putAll(info.pipes)\n\n            positions.forEach { (x, y, z) ->\n                MinecraftClient.getInstance().worldRenderer.scheduleBlockRenders(x, y, z, x, y, z)\n            }\n\n            before.filterKeys { !positions.contains(BlockPos.fromLong(it)) }.forEach {\n                val (x, y, z) = BlockPos.fromLong(it.key)\n                MinecraftClient.getInstance().worldRenderer.scheduleBlockRenders(x, y, z, x, y, z)\n            }\n\n        }\n    }\n\n    fun get(pos: BlockPos): ClientNodeInfo? = nodes[pos.asLong()]\n\n    fun clear() {\n        nodes.clear()\n    }\n\n    fun add(info: ClientNetworkInfo<*>) {\n        info.createNodes().forEach { nodes[it.pos] = it }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/client/ClientServoNetworkInfo.kt",
    "content": "package me.steven.indrev.networks.client\n\nimport com.google.common.collect.ImmutableList\nimport it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap\nimport it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap\nimport me.steven.indrev.networks.EndpointData\nimport me.steven.indrev.networks.client.node.ClientServoNodeInfo\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.util.math.Direction\n\nclass ClientServoNetworkInfo : ClientNetworkInfo<ClientServoNodeInfo> {\n\n    val pipes = Long2ObjectOpenHashMap<ClientServoNodeInfo>()\n    override fun write(buf: PacketByteBuf) {\n        buf.writeInt(pipes.size)\n        pipes.forEach { (pos, info) ->\n            buf.writeLong(pos)\n            buf.writeByte(info.size)\n            info.forEach { dir, data ->\n                buf.writeByte(dir.id)\n                buf.writeByte(data.ordinal)\n            }\n        }\n    }\n\n    @Environment(EnvType.CLIENT)\n    override fun read(buf: PacketByteBuf) {\n        pipes.clear()\n        val size = buf.readInt()\n        repeat(size) {\n            val pos = buf.readLong()\n            val info = ClientServoNodeInfo(pos, Object2ObjectOpenHashMap())\n            val infoSize = buf.readByte()\n            repeat(infoSize.toInt()) {\n                val dir = Direction.byId(buf.readByte().toInt())\n                val type = EndpointData.Type.VALUES[buf.readByte().toInt()]\n                info.servos[dir] = type\n            }\n            pipes[pos] = info\n\n        }\n    }\n\n    override fun createNodes(): List<ClientServoNodeInfo> {\n        val list = ImmutableList.builder<ClientServoNodeInfo>()\n        pipes.forEach { (_, servos) -> list.add(servos) }\n        return list.build()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/client/node/ClientNodeInfo.kt",
    "content": "package me.steven.indrev.networks.client.node\n\ninterface ClientNodeInfo {\n    val pos: Long\n}\n\ninline fun <reified T : ClientNodeInfo> ClientNodeInfo.to(): T {\n    return this as T\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/client/node/ClientServoNodeInfo.kt",
    "content": "package me.steven.indrev.networks.client.node\n\nimport it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap\nimport me.steven.indrev.networks.EndpointData\nimport net.minecraft.util.math.Direction\n\nopen class ClientServoNodeInfo(override val pos: Long, val servos: Object2ObjectOpenHashMap<Direction, EndpointData.Type>) :\n    ClientNodeInfo {\n\n    val size: Int get() = servos.size\n\n    inline fun forEach(f: (Direction, EndpointData.Type) -> Unit) = servos.forEach { (dir, type) -> f(dir, type) }\n\n    override fun equals(other: Any?): Boolean {\n        return when {\n            other !is ClientServoNodeInfo -> false\n            other.pos != pos -> false\n            other.servos.size != servos.size -> false\n            other.servos != servos -> false\n            else -> true\n        }\n    }\n\n    override fun hashCode(): Int {\n        var result = pos.hashCode()\n        result = 31 * result + servos.hashCode()\n        return result\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/energy/CableEnergyIo.kt",
    "content": "package me.steven.indrev.networks.energy\n\nimport net.fabricmc.fabric.api.transfer.v1.storage.StoragePreconditions\nimport net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext\nimport net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport team.reborn.energy.api.EnergyStorage\n\nclass CableEnergyIo(private val network: EnergyNetwork?, val pos: BlockPos, val direction: Direction?) : EnergyStorage, SnapshotParticipant<Long>() {\n\n    override fun getAmount(): Long = network?.energy ?: 0\n\n    override fun getCapacity(): Long = network?.capacity ?: 0\n\n    override fun insert(maxAmount: Long, transaction: TransactionContext?): Long {\n        if (network == null || direction == null) return 0\n        StoragePreconditions.notNegative(maxAmount)\n        val inserted = maxAmount.coerceAtMost(network.maxCableTransfer).coerceAtMost(capacity - amount)\n        if (inserted > 0) {\n            updateSnapshots(transaction)\n            network.energy += inserted\n            return inserted\n        }\n        return 0\n    }\n\n    override fun extract(maxAmount: Long, transaction: TransactionContext?): Long = 0\n\n    override fun supportsInsertion(): Boolean = true\n\n    override fun supportsExtraction(): Boolean = false\n\n    override fun createSnapshot(): Long {\n        return network?.energy ?: 0\n    }\n\n    override fun readSnapshot(snapshot: Long) {\n        network?.energy = snapshot\n    }\n\n    companion object {\n        val NO_NETWORK = CableEnergyIo(null, BlockPos.ORIGIN, null)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/energy/EnergyNetwork.kt",
    "content": "package me.steven.indrev.networks.energy\n\nimport it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap\nimport it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap\nimport it.unimi.dsi.fastutil.objects.ObjectOpenHashSet\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blocks.machine.pipes.CableBlock\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.utils.energyOf\nimport me.steven.indrev.utils.insert\nimport me.steven.indrev.utils.isLoaded\nimport me.steven.indrev.utils.transaction\nimport net.fabricmc.fabric.api.transfer.v1.transaction.Transaction\nimport net.minecraft.block.Block\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport team.reborn.energy.api.EnergyStorage\nimport java.util.*\n\nopen class EnergyNetwork(\n    world: ServerWorld,\n    val cables: MutableSet<BlockPos> = ObjectOpenHashSet(),\n    val machines: MutableMap<BlockPos, EnumSet<Direction>> = Object2ObjectOpenHashMap()\n) : Network(Type.ENERGY, world, cables, machines) {\n\n    var tier = Tier.MK1\n\n    val maxCableTransfer: Long\n        get() = when (tier) {\n            Tier.MK1 -> IRConfig.cables.cableMk1\n            Tier.MK2 -> IRConfig.cables.cableMk2\n            Tier.MK3 -> IRConfig.cables.cableMk3\n            else -> IRConfig.cables.cableMk4\n        }.toLong()\n\n    val insertables = ObjectOpenHashSet<BlockPos>()\n\n    var energy = 0L\n    val capacity: Long get() = pipes.size * maxCableTransfer\n\n\n    private val maxInputs = Object2LongOpenHashMap<EnergyStorage>()\n    private val storages = mutableListOf<EnergyStorage>()\n\n    override fun tick(world: ServerWorld) {\n        if (energy <= 0) return\n\n        maxInputs.clear()\n        storages.clear()\n\n        insertables.forEach { pos ->\n            if (world.isLoaded(pos)) {\n                machines[pos]?.forEach { dir ->\n                    val storage = energyOf(world, pos, dir)\n                    if (storage != null)\n                        storages.add(storage)\n                }\n            }\n        }\n\n        val totalInput = transaction { tx ->\n            storages.sumOf { energyStorage ->\n                val maxInput = energyStorage.insert(MAX_VALUE, tx)\n                if (maxInput > 0)\n                    maxInputs[energyStorage] = maxInput\n                maxInput\n            }.toDouble()\n        }\n\n        if (totalInput <= 0) return\n\n        transaction { tx ->\n            storages.forEach { energyStorage ->\n                val maxInput = maxInputs.getLong(energyStorage)\n                if (maxInput <= 0) return@forEach\n\n                val toTransfer = ((maxInput / totalInput) * energy).toLong().coerceAtMost(maxCableTransfer).coerceAtMost(energy)\n\n                energy -= energyStorage.insert(toTransfer, tx)\n\n                if (energy <= 0) {\n                    tx.commit()\n                    return\n                }\n            }\n\n            tx.commit()\n        }\n    }\n\n    override fun appendPipe(block: Block, blockPos: BlockPos) {\n        val cable = block as? CableBlock ?: return\n        this.tier = cable.tier\n        super.appendPipe(block, blockPos)\n    }\n\n    companion object {\n        private const val MAX_VALUE = Long.MAX_VALUE - 1L\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/energy/EnergyNetworkState.kt",
    "content": "package me.steven.indrev.networks.energy\n\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.NetworkState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.nbt.NbtHelper\nimport net.minecraft.nbt.NbtList\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\n\nclass EnergyNetworkState(world: ServerWorld) : NetworkState<EnergyNetwork>(Network.Type.ENERGY, world) {\n\n    private var destroyedEnergy = 0L\n\n    val savedEnergy = mutableMapOf<BlockPos, Long>()\n\n    override fun add(network: Network) {\n        super.add(network)\n\n        if (network is EnergyNetwork) {\n            network.energy += destroyedEnergy.coerceAtMost(network.capacity)\n            destroyedEnergy -= network.energy\n\n            if (destroyedEnergy <= 0) destroyedEnergy = 0\n        }\n    }\n\n    override fun remove(network: Network) {\n        super.remove(network)\n\n        if (network is EnergyNetwork) {\n            destroyedEnergy += network.energy\n        }\n    }\n\n    override fun tick(world: ServerWorld) {\n        super.tick(world)\n        destroyedEnergy = 0\n    }\n\n    override fun writeNbt(tag: NbtCompound): NbtCompound {\n        val list = NbtList()\n        networks.forEach { network ->\n            val pos = network.pipes.minByOrNull { it }  ?: return@forEach\n            val networkTag = NbtCompound()\n            networkTag.put(\"Pos\", NbtHelper.fromBlockPos(pos))\n            networkTag.putLong(\"Energy\", (network as EnergyNetwork).energy)\n            list.add(networkTag)\n        }\n        tag.put(\"SavedEnergy\", list)\n        return super.writeNbt(tag)\n    }\n\n\n    companion object {\n        fun readNbt(tag: NbtCompound, supplier: () -> EnergyNetworkState): EnergyNetworkState {\n            val state = supplier()\n            val list = tag.getList(\"SavedEnergy\", 10)\n\n            list.forEach { t ->\n                val networkTag = t as NbtCompound\n                val pos = NbtHelper.toBlockPos(networkTag.getCompound(\"Pos\"))\n                val energy = networkTag.getLong(\"Energy\")\n                state.savedEnergy[pos] = energy\n            }\n\n            return state\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/factory/NetworkFactory.kt",
    "content": "package me.steven.indrev.networks.factory\n\nimport it.unimi.dsi.fastutil.longs.LongOpenHashSet\nimport me.steven.indrev.blockentities.cables.BasePipeBlockEntity\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.NetworkState\nimport net.minecraft.block.BlockState\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.chunk.Chunk\n\ninterface NetworkFactory<T : Network> {\n\n    fun process(\n        network: T,\n        state: NetworkState<T>,\n        world: ServerWorld,\n        pos: BlockPos,\n        direction: Direction,\n        blockState: () -> BlockState\n    ): Boolean\n\n    fun deepScan(\n        scanned: LongOpenHashSet,\n        type: Network.Type<T>,\n        state: NetworkState<T>,\n        network: T,\n        chunk: Chunk,\n        world: ServerWorld,\n        blockPos: BlockPos,\n        source: BlockPos,\n        direction: Direction\n    ) {\n        val blockState by lazy { chunk.getBlockState(blockPos) }\n        val shouldContinue = process(network, state, world, blockPos, direction) { blockState }\n        val longPos = blockPos.asLong()\n        if (blockPos != source && !scanned.add(longPos)) return\n        if (shouldContinue) {\n            if (state.networksByPos.containsKey(longPos)) {\n                val oldNetwork = state.networksByPos[longPos]\n                if (oldNetwork != network)\n                    oldNetwork?.remove()\n            }\n            state.updatedPositions.add(longPos)\n            val blockEntity = chunk.getBlockEntity(blockPos) as? BasePipeBlockEntity ?: return\n            DIRECTIONS.forEach { dir ->\n                if (blockEntity.connections[dir]!!.isConnected()) {\n                    val nPos = blockPos.offset(dir)\n                    if (nPos.x shr 4 == chunk.pos.x && nPos.z shr 4 == chunk.pos.z)\n                        deepScan(scanned, type, state, network, chunk, world, nPos, source, dir)\n                    else\n                        deepScan(scanned, type, state, network, world.getChunk(nPos), world, nPos, source, dir)\n                }\n            }\n        }\n    }\n\n    fun deepScan(\n        type: Network.Type<T>,\n        world: ServerWorld,\n        source: BlockPos\n    ): T {\n        val network = type.createEmpty(world)\n        DIRECTIONS.forEach { direction ->\n            deepScan(LongOpenHashSet(), type, type.getNetworkState(world), network, world.getChunk(source), world, source, source, direction)\n        }\n        return network\n    }\n\n    companion object {\n        val DIRECTIONS = Direction.values()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/factory/factories.kt",
    "content": "package me.steven.indrev.networks.factory\n\nimport me.steven.indrev.blocks.machine.pipes.CableBlock\nimport me.steven.indrev.blocks.machine.pipes.FluidPipeBlock\nimport me.steven.indrev.blocks.machine.pipes.ItemPipeBlock\nimport me.steven.indrev.networks.NetworkState\nimport me.steven.indrev.networks.energy.EnergyNetwork\nimport me.steven.indrev.networks.energy.EnergyNetworkState\nimport me.steven.indrev.networks.fluid.FluidNetwork\nimport me.steven.indrev.networks.item.ItemNetwork\nimport me.steven.indrev.utils.energyOf\nimport me.steven.indrev.utils.fluidStorageOf\nimport me.steven.indrev.utils.itemStorageOf\nimport net.minecraft.block.BlockState\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\n\nval ENERGY_NET_FACTORY: NetworkFactory<EnergyNetwork> = object : NetworkFactory<EnergyNetwork> {\n    override fun process(\n        network: EnergyNetwork,\n        state: NetworkState<EnergyNetwork>,\n        world: ServerWorld,\n        pos: BlockPos,\n        direction: Direction,\n        blockState: () -> BlockState\n    ): Boolean {\n        if (blockState().block is CableBlock) {\n            network.appendPipe(blockState().block, pos.toImmutable())\n\n            if ((state as EnergyNetworkState).savedEnergy.containsKey(pos)) {\n                val energy = state.savedEnergy.remove(pos)!!\n                network.energy += energy\n            }\n            return true\n        } else {\n            val energyOf = energyOf(world, pos, direction.opposite)\n            if (energyOf != null) {\n                network.appendContainer(pos, direction.opposite)\n                if (energyOf.supportsInsertion()) network.insertables.add(pos)\n            }\n        }\n        return false\n    }\n}\n\nval FLUID_NET_FACTORY: NetworkFactory<FluidNetwork> = object : NetworkFactory<FluidNetwork> {\n    override fun process(\n        network: FluidNetwork,\n        state: NetworkState<FluidNetwork>,\n        world: ServerWorld,\n        pos: BlockPos,\n        direction: Direction,\n        blockState: () -> BlockState\n    ): Boolean {\n        if (blockState().block is FluidPipeBlock) {\n            network.appendPipe(blockState().block, pos.toImmutable())\n            state.onSet(pos, network)\n            return true\n        } else if (fluidStorageOf(world, pos, direction.opposite) != null) {\n            network.appendContainer(pos, direction.opposite)\n        }\n        return false\n    }\n}\n\nval ITEM_NET_FACTORY: NetworkFactory<ItemNetwork> = object : NetworkFactory<ItemNetwork> {\n    override fun process(\n        network: ItemNetwork,\n        state: NetworkState<ItemNetwork>,\n        world: ServerWorld,\n        pos: BlockPos,\n        direction: Direction,\n        blockState: () -> BlockState\n    ): Boolean {\n        if (blockState().block is ItemPipeBlock) {\n            network.appendPipe(blockState().block, pos.toImmutable())\n            state.onSet(pos, network)\n            return true\n        } else if (itemStorageOf(world, pos, direction.opposite) != null) {\n            network.appendContainer(pos, direction.opposite)\n        }\n        return false\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/fluid/FluidNetwork.kt",
    "content": "package me.steven.indrev.networks.fluid\n\nimport alexiil.mc.lib.attributes.fluid.filter.FluidFilter\nimport it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap\nimport it.unimi.dsi.fastutil.objects.ObjectOpenHashSet\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blocks.machine.pipes.FluidPipeBlock\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.networks.EndpointData\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.Node\nimport me.steven.indrev.utils.ReusableArrayDeque\nimport me.steven.indrev.utils.bucket\nimport me.steven.indrev.utils.fluidStorageOf\nimport me.steven.indrev.utils.isLoaded\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.Storage\nimport net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil\nimport net.minecraft.block.Block\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport java.util.*\nimport kotlin.collections.component1\nimport kotlin.collections.component2\n\nclass FluidNetwork(\n    world: ServerWorld,\n    pipes: MutableSet<BlockPos> = ObjectOpenHashSet(),\n    containers: MutableMap<BlockPos, EnumSet<Direction>> = Object2ObjectOpenHashMap()\n) : Network(Type.FLUID, world, pipes, containers) {\n\n    var tier = Tier.MK1\n\n    private val maxCableTransfer: Long\n        get() = when (tier) {\n            Tier.MK1 -> IRConfig.cables.fluidPipeMk1\n            Tier.MK2 -> IRConfig.cables.fluidPipeMk2\n            Tier.MK3 -> IRConfig.cables.fluidPipeMk3\n            else -> IRConfig.cables.fluidPipeMk4\n        }.toLong() * bucket\n\n    var lastTransferred: FluidVariant = FluidVariant.blank()\n\n    private val deques = Object2ObjectOpenHashMap<BlockPos, EnumMap<EndpointData.Mode, ReusableArrayDeque<Node>>>()\n\n    private var ticks = 0\n\n    override fun tick(world: ServerWorld) {\n        ticks++\n        if (ticks % 20 != 0) return\n        val state = Type.FLUID.getNetworkState(world) as FluidNetworkState\n        if (isQueueValid()) {\n            containers.forEach { (pos, directions) ->\n                if (!world.isLoaded(pos)) return@forEach\n\n                val nodes = queue[pos] ?: return@forEach\n\n                directions.forEach inner@{ dir ->\n                    val data = state.getEndpointData(pos.offset(dir), dir.opposite) ?: return@inner\n\n                    val filter: (FluidVariant) -> Boolean = { v -> !lastTransferred.isBlank && v == lastTransferred }\n\n                    val deque = getQueue(pos, data, filter, nodes)\n\n                    if (data.type == EndpointData.Type.OUTPUT)\n                        tickOutput(pos, dir, deque, state, filter)\n                    else if (data.type == EndpointData.Type.RETRIEVER)\n                        tickRetriever(pos, dir, deque, state, filter)\n\n                    deque.resetHead()\n                }\n            }\n        }\n        lastTransferred = FluidVariant.blank()\n    }\n\n    private fun getQueue(pos: BlockPos, data: EndpointData, filter: (FluidVariant) -> Boolean, nodes: List<Node>): ReusableArrayDeque<Node> {\n        var queuesByNodes = deques[pos]\n        if (queuesByNodes == null) {\n            queuesByNodes = EnumMap(EndpointData.Mode::class.java)\n            this.deques[pos] = queuesByNodes\n        }\n        var queue = queuesByNodes[data.mode]\n        if (queue == null) {\n            queue = ReusableArrayDeque(nodes)\n            queue.apply(data.mode!!.getFluidSorter(world, data.type) { filter(it) })\n            queuesByNodes[data.mode] = queue\n        }\n        if (data.mode == EndpointData.Mode.ROUND_ROBIN || data.mode == EndpointData.Mode.RANDOM) {\n            queue.apply(data.mode!!.getFluidSorter(world, data.type) { filter(it) })\n        }\n\n        return queue\n    }\n\n    private fun tickOutput(pos: BlockPos, dir: Direction, queue: ReusableArrayDeque<Node>, state: FluidNetworkState, fluidFilter: (FluidVariant) -> Boolean) {\n        val extractable = fluidStorageOf(world, pos, dir)\n        var remaining = maxCableTransfer\n        updateLastTransferred(extractable)\n        while (queue.isNotEmpty() && remaining > 0) {\n            val (_, targetPos, _, targetDir) = queue.removeFirst()\n            if (!world.isLoaded(targetPos)) continue\n            val targetData = state.getEndpointData(targetPos.offset(targetDir), targetDir.opposite)\n            val input = targetData == null || targetData.type == EndpointData.Type.INPUT\n            if (!input) continue\n\n            val insertable = fluidStorageOf(world, targetPos, targetDir)\n            val moved = StorageUtil.move(extractable, insertable, fluidFilter, remaining, null)\n            remaining -= moved\n        }\n    }\n\n    private fun tickRetriever(pos: BlockPos, dir: Direction, queue: ReusableArrayDeque<Node>, state: FluidNetworkState, fluidFilter: (FluidVariant) -> Boolean) {\n        val insertable = fluidStorageOf(world, pos, dir)\n        var remaining = maxCableTransfer\n        while (queue.isNotEmpty() && remaining > 0) {\n            val (_, targetPos, _, targetDir) = queue.removeFirst()\n            if (!world.isLoaded(targetPos)) continue\n            val targetData = state.getEndpointData(targetPos.offset(targetDir), targetDir.opposite)\n            val isRetriever = targetData?.type == EndpointData.Type.RETRIEVER\n            if (isRetriever) continue\n\n            val extractable = fluidStorageOf(world, targetPos, targetDir)\n            updateLastTransferred(extractable)\n            val moved = StorageUtil.move(extractable, insertable, fluidFilter, remaining, null)\n            remaining -= moved\n        }\n    }\n\n    private fun updateLastTransferred(extractable: Storage<FluidVariant>?) {\n        if (lastTransferred.isBlank) {\n            val content = StorageUtil.findExtractableContent(extractable, null)\n            if (content != null) {\n                lastTransferred = content.resource\n            }\n        }\n    }\n\n    override fun appendPipe(block: Block, blockPos: BlockPos) {\n        val cable = block as? FluidPipeBlock ?: return\n        this.tier = cable.tier\n        super.appendPipe(block, blockPos)\n    }\n\n    companion object {\n        private val NO_FLUID_FILTER = FluidFilter { true }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/fluid/FluidNetworkState.kt",
    "content": "package me.steven.indrev.networks.fluid\n\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.ServoNetworkState\nimport net.minecraft.server.world.ServerWorld\n\nclass FluidNetworkState(world: ServerWorld) : ServoNetworkState<FluidNetwork>(Network.Type.FLUID, world)"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/item/ItemFilterData.kt",
    "content": "package me.steven.indrev.networks.item\n\nimport net.fabricmc.fabric.api.transfer.v1.item.ItemVariant\nimport net.minecraft.inventory.Inventories\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.util.collection.DefaultedList\n\nclass ItemFilterData(\n    var whitelist: Boolean,\n    var matchDurability: Boolean,\n    var matchTag: Boolean,\n    val filter: DefaultedList<ItemStack> = DefaultedList.ofSize(9, ItemStack.EMPTY)\n) {\n\n    constructor() : this(false, false, false)\n\n    fun matches(itemStack: ItemVariant): Boolean {\n        if (filter.isEmpty()) return !whitelist\n        val findMatches = filter.filter { it.item == itemStack.item }\n        if (findMatches.isEmpty()) return !whitelist\n        var match = true\n        if (matchDurability) match = findMatches.any { it.damage == itemStack.toStack().damage }\n        if (match && matchTag) match = findMatches.any { it.nbt == itemStack.nbt }\n        if (match) return whitelist\n        return !whitelist\n    }\n\n    fun writeNbt(tag: NbtCompound): NbtCompound {\n        tag.put(\"filter\", Inventories.writeNbt(NbtCompound(), filter))\n        tag.putBoolean(\"w\", whitelist)\n        tag.putBoolean(\"d\", matchDurability)\n        tag.putBoolean(\"mt\", matchTag)\n        return tag\n    }\n\n    fun readNbt(tag: NbtCompound): ItemFilterData {\n        Inventories.readNbt(tag.getCompound(\"filter\"), filter)\n        whitelist = tag.getBoolean(\"w\")\n        matchDurability = tag.getBoolean(\"d\")\n        matchTag = tag.getBoolean(\"mt\")\n        return this\n    }\n\n    companion object {\n        val ACCEPTING_FILTER_DATA = ItemFilterData()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/item/ItemNetwork.kt",
    "content": "package me.steven.indrev.networks.item\n\nimport it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap\nimport it.unimi.dsi.fastutil.objects.ObjectOpenHashSet\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blocks.machine.pipes.ItemPipeBlock\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.networks.EndpointData\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.Node\nimport me.steven.indrev.utils.ReusableArrayDeque\nimport me.steven.indrev.utils.isLoaded\nimport me.steven.indrev.utils.itemStorageOf\nimport net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil\nimport net.minecraft.block.Block\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport java.util.*\nimport kotlin.collections.List\nimport kotlin.collections.MutableMap\nimport kotlin.collections.MutableSet\nimport kotlin.collections.component1\nimport kotlin.collections.component2\nimport kotlin.collections.forEach\nimport kotlin.collections.isNotEmpty\nimport kotlin.collections.set\n\nclass ItemNetwork(\n    world: ServerWorld,\n    pipes: MutableSet<BlockPos> = ObjectOpenHashSet(),\n    containers: MutableMap<BlockPos, EnumSet<Direction>> = Object2ObjectOpenHashMap()\n) : Network(Type.ITEM, world, pipes, containers) {\n\n    var tier = Tier.MK1\n    private val maxCableTransfer: Long\n        get() = when (tier) {\n            Tier.MK1 -> IRConfig.cables.itemPipeMk1\n            Tier.MK2 -> IRConfig.cables.itemPipeMk2\n            Tier.MK3 -> IRConfig.cables.itemPipeMk3\n            else -> IRConfig.cables.itemPipeMk4\n        }.toLong()\n\n    private val deques = Object2ObjectOpenHashMap<BlockPos, EnumMap<EndpointData.Mode, ReusableArrayDeque<Node>>>()\n\n    private var ticks = 0\n\n    override fun tick(world: ServerWorld) {\n        ticks++\n        if (ticks % 20 != 0) return\n        val state = Type.ITEM.getNetworkState(world) as ItemNetworkState\n        if (containers.isEmpty()) return\n        else if (queue.isEmpty())\n            buildQueue()\n        if (queue.isNotEmpty()) {\n            containers.forEach { (pos, directions) ->\n                if (!world.isLoaded(pos)) return@forEach\n                val nodes = queue[pos] ?: return@forEach\n\n                directions.forEach inner@{ dir ->\n                    val data = state.getEndpointData(pos.offset(dir), dir.opposite) ?: return@inner\n                    val filterData = state.getFilterData(pos.offset(dir), dir.opposite)\n                    if (data.type == EndpointData.Type.INPUT) return@inner\n\n                    val deque = getQueue(pos, data, filterData, nodes)\n\n                    if (data.type == EndpointData.Type.OUTPUT)\n                        tickOutput(pos, dir, deque, state, data, filterData)\n                    else if (data.type == EndpointData.Type.RETRIEVER)\n                        tickRetriever(pos, dir, deque, state, data, filterData)\n\n                    deque.resetHead()\n                }\n            }\n        }\n    }\n\n    private fun getQueue(pos: BlockPos, data: EndpointData, filter: ItemFilterData, nodes: List<Node>): ReusableArrayDeque<Node> {\n        var queuesByNodes = deques[pos]\n        if (queuesByNodes == null) {\n            queuesByNodes = EnumMap(EndpointData.Mode::class.java)\n            this.deques[pos] = queuesByNodes\n        }\n        var queue = queuesByNodes[data.mode]\n        if (queue == null) {\n            queue = ReusableArrayDeque(nodes)\n            queue.apply(data.mode!!.getItemSorter(world, data.type) { filter.matches(it) })\n            queuesByNodes[data.mode] = queue\n        }\n\n        if (data.mode == EndpointData.Mode.ROUND_ROBIN || data.mode == EndpointData.Mode.RANDOM) {\n            queue.apply(data.mode!!.getItemSorter(world, data.type) { filter.matches(it) })\n        }\n\n        return queue\n    }\n\n    private fun tickOutput(pos: BlockPos, dir: Direction, queue: ReusableArrayDeque<Node>, state: ItemNetworkState, data: EndpointData, filterData: ItemFilterData) {\n        val extractable = itemStorageOf(world, pos, dir)\n        var remaining = maxCableTransfer\n        while (queue.isNotEmpty() && remaining > 0) {\n            val node = queue.removeFirst()\n            val (_, targetPos, _, targetDir) = node\n            if (!world.isLoaded(targetPos)) continue\n            val targetData = state.getEndpointData(targetPos.offset(targetDir), targetDir.opposite)\n            val input = targetData == null || targetData.type == EndpointData.Type.INPUT\n            if (!input) continue\n            val targetFilterData = state.getFilterData(targetPos.offset(targetDir), targetDir.opposite)\n\n            fun doMove() {\n                val insertable = itemStorageOf(world, targetPos, targetDir)\n                val moved = StorageUtil.move(extractable, insertable, { filterData.matches(it) && targetFilterData.matches(it) }, remaining, null)\n                remaining -= moved\n                if (moved > 0 && remaining > 0) {\n                    doMove()\n                }\n            }\n            doMove()\n        }\n    }\n\n    private fun tickRetriever(pos: BlockPos, dir: Direction, queue: ReusableArrayDeque<Node>, state: ItemNetworkState, data: EndpointData, filterData: ItemFilterData) {\n        val insertable = itemStorageOf(world, pos, dir)\n        var remaining = maxCableTransfer\n        while (queue.isNotEmpty() && remaining > 0) {\n            val node = queue.removeFirst()\n            val (_, targetPos, _, targetDir) = node\n            if (!world.isLoaded(targetPos)) continue\n            val targetData = state.getEndpointData(targetPos.offset(targetDir), targetDir.opposite)\n            val isRetriever = targetData?.type == EndpointData.Type.RETRIEVER\n            if (isRetriever) continue\n            val targetFilterData = state.getFilterData(targetPos.offset(targetDir), targetDir.opposite)\n\n           fun doMove() {\n               val extractable = itemStorageOf(world, targetPos, targetDir)\n               val moved = StorageUtil.move(extractable, insertable, { filterData.matches(it) && targetFilterData.matches(it) }, remaining, null)\n               remaining -= moved\n               if (moved > 0 && remaining > 0)\n                   doMove()\n           }\n            doMove()\n        }\n    }\n\n    override fun appendPipe(block: Block, blockPos: BlockPos) {\n        val cable = block as? ItemPipeBlock ?: return\n        this.tier = cable.tier\n        super.appendPipe(block, blockPos)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/networks/item/ItemNetworkState.kt",
    "content": "package me.steven.indrev.networks.item\n\nimport it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap\nimport it.unimi.dsi.fastutil.objects.Object2ObjectFunction\nimport it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.ServoNetworkState\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.nbt.NbtList\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport java.util.function.LongFunction\n\nclass ItemNetworkState(world: ServerWorld) : ServoNetworkState<ItemNetwork>(Network.Type.ITEM, world) {\n\n    val filters = Long2ObjectOpenHashMap<Object2ObjectOpenHashMap<Direction, ItemFilterData>>()\n    private val recentlyRemovedFilters = Long2ObjectOpenHashMap<Object2ObjectOpenHashMap<Direction, ItemFilterData>>()\n\n    override fun onRemoved(pos: BlockPos) {\n        super.onRemoved(pos)\n        if (filters.containsKey(pos.asLong()))\n            recentlyRemovedFilters[pos.asLong()] = filters.remove(pos.asLong())\n    }\n\n    override fun onSet(blockPos: BlockPos, network: ItemNetwork) {\n        super.onSet(blockPos, network)\n        if (recentlyRemovedFilters.containsKey(blockPos.asLong())) {\n            filters[blockPos.asLong()] = recentlyRemovedFilters.remove(blockPos.asLong())\n        }\n    }\n\n    override fun clearCachedData(importCache: Boolean) {\n        super.clearCachedData(importCache)\n        if (importCache) {\n            recentlyRemovedFilters.forEach { e -> filters[e.key] = e.value }\n        }\n        recentlyRemovedFilters.clear()\n    }\n\n    fun getFilterData(pos: BlockPos, direction: Direction, createIfAbsent: Boolean = false): ItemFilterData {\n        return if (createIfAbsent)\n            filters.computeIfAbsent(pos.asLong(), LongFunction { Object2ObjectOpenHashMap() }).computeIfAbsent(direction, Object2ObjectFunction { ItemFilterData() })\n        else\n            filters.get(pos.asLong())?.get(direction) ?: ItemFilterData.ACCEPTING_FILTER_DATA\n    }\n\n    override fun writeNbt(tag: NbtCompound): NbtCompound {\n        val filtersTag = NbtList()\n        filters.forEach { (pos, modes) ->\n            val sidesTag = NbtList()\n            modes.forEach { (dir, filterData) ->\n                if (filterData != ItemFilterData.ACCEPTING_FILTER_DATA) {\n                    val t = NbtCompound()\n                    t.put(dir.ordinal.toString(), filterData.writeNbt(NbtCompound()))\n                    sidesTag.add(t)\n                }\n            }\n            val posTag = NbtCompound()\n            posTag.putLong(\"pos\", pos)\n            posTag.put(\"sides\", sidesTag)\n            filtersTag.add(posTag)\n\n        }\n        tag.put(\"filters\", filtersTag)\n        return super.writeNbt(tag)\n    }\n\n    companion object {\n        fun readNbt(tag: NbtCompound, supplier: () -> ItemNetworkState): ItemNetworkState {\n            val state = supplier()\n            val modesTag = tag.getList(\"filters\", 10)\n            modesTag.forEach { posTag ->\n                posTag as NbtCompound\n                val pos = posTag.getLong(\"pos\")\n                val map = Object2ObjectOpenHashMap<Direction, ItemFilterData>()\n                val sidesTag = posTag.getList(\"sides\", 10)\n                sidesTag.forEach { t ->\n                    t as NbtCompound\n                    t.keys.forEach { id ->\n                        val data = ItemFilterData().readNbt(t.getCompound(id))\n                        val dir = Direction.values()[id.toInt()]\n                        map[dir] = data\n                    }\n                }\n\n                state.filters[pos] = map\n            }\n\n            ServoNetworkState.readNbt(tag) { state }\n\n            return state\n\n        }\n    }\n\n\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/PacketRegistry.kt",
    "content": "package me.steven.indrev.packets\n\nimport me.steven.indrev.packets.client.*\nimport me.steven.indrev.packets.common.*\n\nobject PacketRegistry {\n    fun registerServer() {\n        ConfigureIOPackets.register()\n        DataCardWriteStartPacket.register()\n        GuiPropertySyncPacket.registerServer()\n        FluidGuiHandInteractionPacket.register()\n        ItemPipePackets.register()\n        SelectModuleOnWorkbenchPacket.register()\n        ToggleFactoryStackSplittingPacket.register()\n        ToggleGamerAxePacket.register()\n        UpdateAOEMachineRangePacket.register()\n        UpdateMiningDrillBlockBlacklistPacket.register()\n        UpdateKnobValue.register()\n        UpdateModularToolLevelPacket.register()\n        UpdateRancherConfigPacket.register()\n    }\n\n    fun registerClient() {\n        ClientItemPipePackets.register()\n        GuiPropertySyncPacket.register()\n        MachineStateUpdatePacket.register()\n        MiningRigSpawnBlockParticlesPacket.register()\n        SyncAppliedModulesPacket.register()\n        SyncConfigPacket.register()\n        SyncNetworkServosPacket.register()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/client/ClientItemPipePackets.kt",
    "content": "package me.steven.indrev.packets.client\n\nimport me.steven.indrev.gui.screenhandlers.pipes.PipeFilterScreen\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\n\nobject ClientItemPipePackets {\n\n    val UPDATE_FILTER_SLOT_S2C_PACKET = identifier(\"update_filter_s2c\") \n\n     fun register() {\n        ClientPlayNetworking.registerGlobalReceiver(UPDATE_FILTER_SLOT_S2C_PACKET) { client, _, buf, _ ->\n            val slotIndex = buf.readInt()\n            val stack = buf.readItemStack()\n            client.execute {\n                val screen = client.currentScreen as? PipeFilterScreen ?: return@execute\n                val controller = screen.screenHandler\n                controller.backingList[slotIndex] = stack\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/client/GuiPropertySyncPacket.kt",
    "content": "package me.steven.indrev.packets.client\n\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\n\nobject GuiPropertySyncPacket {\n\n    val SYNC_PROPERTY = identifier(\"sync_property\")\n    val C2S_REQUEST_PROPERTIES = identifier(\"client_request\")\n\n     fun register() {\n        ClientPlayNetworking.registerGlobalReceiver(SYNC_PROPERTY) { client, _, buf, _ ->\n            val syncId = buf.readInt()\n            val property = buf.readInt()\n\n            val handler = client.player!!.currentScreenHandler\n            if (handler.syncId == syncId && handler is IRGuiScreenHandler) {\n                val prop = handler.component?.properties?.get(property) ?: return@registerGlobalReceiver\n                prop.fromPacket(buf)\n                handler.onSyncedProperty(property, prop)\n            } else {\n                IndustrialRevolution.LOGGER.warn(\"Received sync packet for unknown screen type @ $handler\")\n            }\n        }\n    }\n\n    fun registerServer() {\n        ServerPlayNetworking.registerGlobalReceiver(C2S_REQUEST_PROPERTIES) { server, player, _, buf, _ ->\n            val syncId = buf.readInt()\n\n            val handler = player!!.currentScreenHandler\n            if (handler.syncId == syncId && handler is IRGuiScreenHandler) {\n                handler.component?.properties?.forEach { it.markDirty() }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/client/MachineStateUpdatePacket.kt",
    "content": "package me.steven.indrev.packets.client\n\nimport me.steven.indrev.blockentities.GlobalStateController\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\n\nobject MachineStateUpdatePacket {\n\n    val UPDATE_PACKET_ID = identifier(\"global_state_update\") \n\n     fun register() {\n        ClientPlayNetworking.registerGlobalReceiver(UPDATE_PACKET_ID) { client, _, buf, _ ->\n            val pos = buf.readBlockPos()\n            val workingState = buf.readBoolean()\n            client.execute {\n                GlobalStateController.workingStateTracker[pos.asLong()] = workingState\n                GlobalStateController.queueUpdate(pos)\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/client/MiningRigSpawnBlockParticlesPacket.kt",
    "content": "package me.steven.indrev.packets.client\n\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.world.ClientWorld\nimport net.minecraft.sound.SoundCategory\nimport net.minecraft.util.registry.Registry\n\nobject MiningRigSpawnBlockParticlesPacket {\n\n    val BLOCK_BREAK_PACKET = identifier(\"miner_drill_block_particle\") \n\n     fun register() {\n        ClientPlayNetworking.registerGlobalReceiver(BLOCK_BREAK_PACKET) { client, _, buf, _ ->\n            val pos = buf.readBlockPos().down()\n            val blockRawId = buf.readInt()\n            val block = Registry.BLOCK.get(blockRawId)\n            client.execute {\n                MinecraftClient.getInstance().particleManager.addBlockBreakParticles(pos, block.defaultState)\n                val blockSoundGroup = block.getSoundGroup(block.defaultState)\n                (client.player!!.world as ClientWorld).playSound(\n                    pos,\n                    blockSoundGroup.breakSound,\n                    SoundCategory.BLOCKS,\n                    (blockSoundGroup.getVolume() + 1.0f) / 4.0f,\n                    blockSoundGroup.getPitch() * 0.8f,\n                    false\n                )\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/client/SyncAppliedModulesPacket.kt",
    "content": "package me.steven.indrev.packets.client\n\nimport me.steven.indrev.api.IRPlayerEntityExtension\nimport me.steven.indrev.tools.modular.ArmorModule\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\n\nobject SyncAppliedModulesPacket {\n\n    val SYNC_MODULE_PACKET = identifier(\"sync_module\") \n\n     fun register() {\n        ClientPlayNetworking.registerGlobalReceiver(SYNC_MODULE_PACKET) { client, _, buf, _ ->\n            val size = buf.readInt()\n            val modules = hashMapOf<ArmorModule, Int>()\n            for (index in 0 until size) {\n                val ordinal = buf.readInt()\n                val module = ArmorModule.values()[ordinal]\n                val level = buf.readInt()\n                modules[module] = level\n            }\n            val durability = buf.readDouble()\n            val isRegenerating = buf.readBoolean()\n            client.execute {\n                val player = client.player!!\n                if (player is IRPlayerEntityExtension) {\n                    (player.getAppliedModules() as MutableMap<*, *>).clear()\n                    modules.forEach(player::applyModule)\n                    player.shieldDurability = durability\n                    player.isRegenerating = isRegenerating\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/client/SyncConfigPacket.kt",
    "content": "package me.steven.indrev.packets.client\n\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\nimport net.fabricmc.fabric.api.networking.v1.PacketByteBufs\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\nimport net.minecraft.server.network.ServerPlayerEntity\n\nobject SyncConfigPacket {\n\n    val SYNC_CONFIG_PACKET = identifier(\"sync_config_packet\") \n\n     fun register() {\n        ClientPlayNetworking.registerGlobalReceiver(SYNC_CONFIG_PACKET) { _, _, buf, _ ->\n            IRConfig.readFromServer(buf)\n        }\n    }\n\n    fun sendConfig(playerEntity: ServerPlayerEntity) {\n        val buf = PacketByteBufs.create()\n        IRConfig.writeToClient(buf)\n        ServerPlayNetworking.send(playerEntity, SYNC_CONFIG_PACKET, buf)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/client/SyncNetworkServosPacket.kt",
    "content": "package me.steven.indrev.packets.client\n\nimport it.unimi.dsi.fastutil.objects.Object2ObjectFunction\nimport me.steven.indrev.IndustrialRevolutionClient\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.client.ClientNetworkState\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking\n\nobject SyncNetworkServosPacket {\n\n    val SYNC_NETWORK_SERVOS = identifier(\"sync_network_servos\") \n\n     fun register() {\n        ClientPlayNetworking.registerGlobalReceiver(SYNC_NETWORK_SERVOS) { client, _, buf, _ ->\n            val type = Network.Type.valueOf(buf.readString())\n            val state = IndustrialRevolutionClient.CLIENT_NETWORK_STATE.computeIfAbsent(type, Object2ObjectFunction { ClientNetworkState(type) })\n            state.processPacket(buf, client)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/ConfigureIOPackets.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.blockentities.GlobalStateController\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\nimport net.minecraft.util.math.Direction\n\nobject ConfigureIOPackets  {\n\n    val UPDATE_MACHINE_SIDE_PACKET_ID = identifier(\"update_machine_side\")\n    val UPDATE_AUTO_OPERATION_PACKET_ID = identifier(\"update_auto_pull_push\") \n\n     fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(UPDATE_MACHINE_SIDE_PACKET_ID) { server, player, _, buf, _ ->\n            val type = buf.readEnumConstant(ConfigurationType::class.java)\n            val pos = buf.readBlockPos()\n            val dir = Direction.byId(buf.readInt())\n            val mode = TransferMode.values()[buf.readInt()]\n            server.execute {\n                val world = player.world\n                val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return@execute\n                blockEntity.getCurrentConfiguration(type)[dir] = mode\n                blockEntity.markDirty()\n                GlobalStateController.update(world, pos, false)\n                world.updateNeighbors(pos, blockEntity.cachedState.block)\n            }\n        }\n\n        ServerPlayNetworking.registerGlobalReceiver(UPDATE_AUTO_OPERATION_PACKET_ID) { server, player, _, buf, _ ->\n            val type = buf.readEnumConstant(ConfigurationType::class.java)\n            val opType = buf.readByte()\n            val pos = buf.readBlockPos()\n            val value = buf.readBoolean()\n            server.execute {\n                val world = player.world\n                val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return@execute\n                if (opType.toInt() == 0)\n                    blockEntity.getCurrentConfiguration(type).autoPush = value\n                else\n                    blockEntity.getCurrentConfiguration(type).autoPull = value\n                blockEntity.markDirty()\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/DataCardWriteStartPacket.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.blockentities.miningrig.DataCardWriterBlockEntity\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\n\nobject DataCardWriteStartPacket {\n\n    val START_PACKET = identifier(\"write_data_card_start\")\n\n    fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(START_PACKET) { server, player, _, buf, _ ->\n            val pos = buf.readBlockPos()\n            server.execute {\n                val blockEntity = player.world.getBlockEntity(pos) as? DataCardWriterBlockEntity ?: return@execute\n                blockEntity.start()\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/FluidGuiHandInteractionPacket.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.gui.screenhandlers.IRGuiScreenHandler\nimport me.steven.indrev.utils.identifier\nimport me.steven.indrev.utils.isLoaded\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\nimport net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage\nimport net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil\n\n\nobject FluidGuiHandInteractionPacket  {\n\n    val FLUID_CLICK_PACKET = identifier(\"fluid_widget_click\") \n\n     fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(FLUID_CLICK_PACKET) { server, player, _, buf, _ ->\n            val tank = buf.readInt()\n            val world = player.world\n            val screenHandler = player.currentScreenHandler as? IRGuiScreenHandler ?: return@registerGlobalReceiver\n            server.execute {\n                screenHandler.ctx.run { _, pos ->\n                    if (world.isLoaded(pos)) {\n                        val blockEntity = world.getBlockEntity(pos) as? MachineBlockEntity<*> ?: return@run\n                        val fluidComponent = blockEntity.fluidComponent ?: return@run\n                        val handStorage = ContainerItemContext.ofPlayerCursor(player, screenHandler).find(FluidStorage.ITEM)\n                        val res = StorageUtil.move(handStorage, fluidComponent[tank], { true }, Long.MAX_VALUE, null)\n                        if (res == 0L)\n                            StorageUtil.move(fluidComponent[tank], handStorage, { true }, Long.MAX_VALUE, null)\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/ItemPipePackets.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.networks.EndpointData\nimport me.steven.indrev.networks.Network\nimport me.steven.indrev.networks.item.ItemNetworkState\nimport me.steven.indrev.packets.client.ClientItemPipePackets\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.networking.v1.PacketByteBufs\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.Direction\n\nobject ItemPipePackets  {\n\n    val CLICK_FILTER_SLOT_PACKET = identifier(\"click_filter_slot\")\n    val CHANGE_FILTER_MODE_PACKET = identifier(\"change_whitelist_mode\")\n    val CHANGE_SERVO_MODE_PACKET = identifier(\"change_servo_mode\") \n\n     fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(CLICK_FILTER_SLOT_PACKET) { server, player, _, buf, _ ->\n            val slotIndex = buf.readInt()\n            val dir = buf.readEnumConstant(Direction::class.java)\n            val pos = buf.readBlockPos()\n            server.execute {\n                val cursorStack = player.currentScreenHandler.cursorStack\n                val state = Network.Type.ITEM.getNetworkState(player.world as ServerWorld) as? ItemNetworkState ?: return@execute\n                val data = state.getFilterData(pos, dir)\n                if (cursorStack.isEmpty) data.filter[slotIndex] = ItemStack.EMPTY\n                else data.filter[slotIndex] = cursorStack.copy().also { it.count = 1 }\n                state.markDirty()\n                val syncPacket = PacketByteBufs.create()\n                syncPacket.writeInt(slotIndex)\n                syncPacket.writeItemStack(data.filter[slotIndex])\n                ServerPlayNetworking.send(player, ClientItemPipePackets.UPDATE_FILTER_SLOT_S2C_PACKET, syncPacket)\n            }\n        }\n\n        ServerPlayNetworking.registerGlobalReceiver(CHANGE_FILTER_MODE_PACKET) { server, player, _, buf, _ ->\n            val dir = buf.readEnumConstant(Direction::class.java)\n            val pos = buf.readBlockPos()\n            val field = buf.readInt()\n            val value = buf.readBoolean()\n\n            server.execute {\n                val state = Network.Type.ITEM.getNetworkState(player.world as ServerWorld) as? ItemNetworkState ?: return@execute\n                val data = state.getFilterData(pos, dir, true)\n                when (field) {\n                    0 -> data.whitelist = value\n                    1 -> data.matchDurability = value\n                    2 -> data.matchTag = value\n                    else -> return@execute\n                }\n                state.markDirty()\n            }\n        }\n\n        ServerPlayNetworking.registerGlobalReceiver(CHANGE_SERVO_MODE_PACKET) { server, player, _, buf, _ ->\n            val dir = buf.readEnumConstant(Direction::class.java)\n            val pos = buf.readBlockPos()\n            val mode = buf.readEnumConstant(EndpointData.Mode::class.java)\n\n            server.execute {\n                val state = Network.Type.ITEM.getNetworkState(player.world as ServerWorld) as? ItemNetworkState ?: return@execute\n                val data = state.getEndpointData(pos, dir, true) ?: return@execute\n                data.mode = mode\n                state.markDirty()\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/SelectModuleOnWorkbenchPacket.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.blockentities.modularworkbench.ModularWorkbenchBlockEntity\nimport me.steven.indrev.gui.screenhandlers.machines.ModularWorkbenchScreenHandler\nimport me.steven.indrev.recipes.machines.ModuleRecipe\nimport me.steven.indrev.utils.getRecipes\nimport me.steven.indrev.utils.identifier\nimport me.steven.indrev.utils.isLoaded\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\n\nobject SelectModuleOnWorkbenchPacket  {\n\n    val MODULE_SELECT_PACKET = identifier(\"module_select_packet\") \n\n     fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(MODULE_SELECT_PACKET) { server, player, _, buf, _ ->\n            val syncId = buf.readInt()\n            val recipeId = buf.readIdentifier()\n            val pos = buf.readBlockPos()\n            val screenHandler =\n                player.currentScreenHandler as? ModularWorkbenchScreenHandler ?: return@registerGlobalReceiver\n            if (syncId != screenHandler.syncId) return@registerGlobalReceiver\n            server.execute {\n                val world = player.world\n                if (world.isLoaded(pos)) {\n                    val recipe = server.recipeManager.getRecipes(ModuleRecipe.TYPE)[recipeId]!!\n                    screenHandler.layoutSlots(recipe)\n                    val blockEntity = world.getBlockEntity(pos) as? ModularWorkbenchBlockEntity ?: return@execute\n                    blockEntity.selectedRecipe = recipeId\n                    blockEntity.markDirty()\n                    blockEntity.sync()\n                }\n            }\n        }\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/ToggleFactoryStackSplittingPacket.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.blockentities.crafters.CraftingMachineBlockEntity\nimport me.steven.indrev.utils.identifier\nimport me.steven.indrev.utils.isLoaded\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\n\nobject ToggleFactoryStackSplittingPacket  {\n\n    val SPLIT_STACKS_PACKET = identifier(\"split_stacks_packet\") \n\n     fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(SPLIT_STACKS_PACKET) { server, player, _, buf, _ ->\n            val pos = buf.readBlockPos()\n            server.execute {\n                val world = player.world\n                if (world.isLoaded(pos)) {\n                    val blockEntity = world.getBlockEntity(pos) as? CraftingMachineBlockEntity<*> ?: return@execute\n                    blockEntity.isSplitOn = !blockEntity.isSplitOn\n                    if (blockEntity.isSplitOn) blockEntity.splitStacks()\n                }\n            }\n        }\n\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/ToggleGamerAxePacket.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.registry.IRItemRegistry\nimport me.steven.indrev.utils.energyOf\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.TypedActionResult\n\nobject ToggleGamerAxePacket {\n    val PACKET_ID = identifier(\"toggle_gamer_axe\")\n\n    fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(PACKET_ID) { server, player, _, _, _ ->\n            server.execute {\n                val stack = player.mainHandStack\n                if (stack.isOf(IRItemRegistry.GAMER_AXE_ITEM)) {\n                    val tag = stack?.orCreateNbt\n                    if (tag?.contains(\"Active\") == false || tag?.contains(\"Progress\") == false) {\n                        tag.putBoolean(\"Active\", true)\n                        tag.putFloat(\"Progress\", 0f)\n                    } else if (tag?.contains(\"Active\") == true) {\n                        val active = !tag.getBoolean(\"Active\")\n                        val itemIo = energyOf(stack)!!\n                        if (itemIo.amount > 0) {\n                            stack.orCreateNbt.putBoolean(\"Active\", active)\n                            val color = if (active) Formatting.GREEN else Formatting.RED\n                            player.sendMessage(LiteralText(\"\").append(stack.name).formatted(stack.rarity.formatting).append(\": \").append(TranslatableText(\"item.indrev.gamer_axe.$active\").formatted(color, Formatting.BOLD)), true)\n                        } else {\n                            player.sendMessage(LiteralText(\"Not enough energy!\"), true)\n                        }\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/UpdateAOEMachineRangePacket.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.blockentities.farms.AOEMachineBlockEntity\nimport me.steven.indrev.utils.identifier\nimport me.steven.indrev.utils.isLoaded\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\n\nobject UpdateAOEMachineRangePacket  {\n\n    val UPDATE_VALUE_PACKET_ID = identifier(\"update_value_packet\") \n\n     fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(UPDATE_VALUE_PACKET_ID) { server, player, _, buf, _ ->\n            val value = buf.readInt()\n            val pos = buf.readBlockPos()\n            val world = player.world\n            server.execute {\n                if (world.isLoaded(pos)) {\n                    val blockEntity = world.getBlockEntity(pos) as? AOEMachineBlockEntity<*> ?: return@execute\n                    blockEntity.range = value\n                    blockEntity.markDirty()\n                    blockEntity.sync()\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/UpdateKnobValue.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.blockentities.generators.SteamTurbineBlockEntity\nimport me.steven.indrev.utils.identifier\nimport me.steven.indrev.utils.isLoaded\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\n\nobject UpdateKnobValue {\n\n    val UPDATE_EFFICIENCY_PACKET = identifier(\"update_steam_turbine_efficiency\")\n\n    fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(UPDATE_EFFICIENCY_PACKET) { server, player, _, buf, _ ->\n            val pos = buf.readBlockPos()\n            val efficiency = buf.readFloat()\n            server.execute {\n                val world = player.world\n                if (world.isLoaded(pos)) {\n                    val blockEntity = world.getBlockEntity(pos) as? SteamTurbineBlockEntity ?: return@execute\n                    blockEntity.efficiency = efficiency.toDouble()\n                    blockEntity.markDirty()\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/UpdateMiningDrillBlockBlacklistPacket.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.tools.modular.DrillModule\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtHelper\nimport net.minecraft.nbt.NbtList\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.server.MinecraftServer\nimport net.minecraft.server.network.ServerPlayerEntity\nimport net.minecraft.util.math.BlockPos\n\nobject UpdateMiningDrillBlockBlacklistPacket {\n\n    val UPDATE_BLACKLIST_PACKET = identifier(\"update_drill_blacklist\")\n\n    fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(UPDATE_BLACKLIST_PACKET) { server, player, _, buf, _ ->\n            val mode = Mode.values()[buf.readInt()]\n            mode.process(buf, server, player)\n        }\n    }\n\n    enum class Mode(val process: (PacketByteBuf, MinecraftServer, ServerPlayerEntity) -> Unit) {\n        SINGLE({ buf, server, player ->\n            val pos = buf.readBlockPos()\n            server.execute {\n                val stack = player.mainHandStack\n                val nbt = stack.orCreateNbt.getList(\"BlacklistedPositions\", 10)\n\n                val posNbt = NbtHelper.fromBlockPos(pos)\n\n                if (nbt.contains(posNbt)) nbt.remove(posNbt)\n                else nbt.add(posNbt)\n\n                stack.nbt!!.put(\"BlacklistedPositions\", nbt)\n            }\n        }),\n        FLIP_Y({ _, server, player ->\n            server.execute {\n                val stack = player.mainHandStack\n                val flipped = DrillModule.getBlacklistedPositions(stack).map { BlockPos(it.x, -it.y, it.z) }\n                update(stack, flipped)\n            }\n        }),\n        FLIP_X({ _, server, player ->\n            server.execute {\n                val stack = player.mainHandStack\n                val flipped = DrillModule.getBlacklistedPositions(stack).map { BlockPos(-it.x, it.y, it.z) }\n                update(stack, flipped)\n            }\n        }),\n        ROT_X_90_CLOCKWISE({ _, server, player ->\n            server.execute {\n                val stack = player.mainHandStack\n                val flipped = DrillModule.getBlacklistedPositions(stack).map { BlockPos(it.y, -it.x, it.z) }\n                update(stack, flipped)\n            }\n        }),\n        ROT_X_90_COUNTERCLOCKWISE({ _, server, player ->\n            server.execute {\n                val stack = player.mainHandStack\n                val flipped = DrillModule.getBlacklistedPositions(stack).map { BlockPos(-it.y, it.x, it.z) }\n                update(stack, flipped)\n            }\n        }),\n        CLEAR({ _, server, player ->\n            server.execute {\n                val stack = player.mainHandStack\n                update(stack, emptyList())\n            }\n        }),\n    }\n\n    private fun update(stack: ItemStack, blacklist: List<BlockPos>) {\n        if (blacklist.isEmpty()) {\n            stack.removeSubNbt(\"BlacklistedPositions\")\n            return\n        }\n\n        val tagList = NbtList()\n        blacklist.map { pos -> NbtHelper.fromBlockPos(pos) }.forEach { tagList.add(it) }\n\n        stack.nbt!!.put(\"BlacklistedPositions\", tagList)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/UpdateModularToolLevelPacket.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\n\nobject UpdateModularToolLevelPacket  {\n\n    val UPDATE_MODULAR_TOOL_LEVEL = identifier(\"update_modular_level\") \n\n     fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(UPDATE_MODULAR_TOOL_LEVEL) { server, player, _, buf, _ ->\n            val key = buf.readString(32767)\n            val value = buf.readInt()\n            val slot = buf.readInt()\n            server.execute {\n                val stack = player.inventory.getStack(slot)\n                if (!stack.isEmpty) {\n                    val tag = stack.getOrCreateSubNbt(\"selected\")\n                    tag.putInt(key, value)\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/packets/common/UpdateRancherConfigPacket.kt",
    "content": "package me.steven.indrev.packets.common\n\nimport me.steven.indrev.blockentities.farms.RancherBlockEntity\nimport me.steven.indrev.utils.identifier\nimport me.steven.indrev.utils.isLoaded\nimport net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking\n\nobject UpdateRancherConfigPacket  {\n\n    val SYNC_RANCHER_CONFIG = identifier(\"rancher_sync_config\") \n\n     fun register() {\n        ServerPlayNetworking.registerGlobalReceiver(SYNC_RANCHER_CONFIG) { server, player, _, buf, _ ->\n            val pos = buf.readBlockPos()\n            val feedBabies = buf.readBoolean()\n            val mateAdults = buf.readBoolean()\n            val matingLimit = buf.readInt()\n            val killAfter = buf.readInt()\n            server.execute {\n                val world = player.world\n                if (world.isLoaded(pos)) {\n                    val blockEntity = world.getBlockEntity(pos) as? RancherBlockEntity ?: return@execute\n                    blockEntity.feedBabies = feedBabies\n                    blockEntity.mateAdults = mateAdults\n                    blockEntity.matingLimit = matingLimit\n                    blockEntity.killAfter = killAfter\n                    blockEntity.markDirty()\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/IRecipeGetter.kt",
    "content": "package me.steven.indrev.recipes\n\nimport me.steven.indrev.utils.IRFluidTank\nimport me.steven.indrev.utils.asMutableList\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.minecraft.inventory.Inventory\nimport net.minecraft.item.ItemStack\nimport net.minecraft.recipe.Recipe\nimport net.minecraft.server.world.ServerWorld\n\ninterface IRecipeGetter<T : Recipe<Inventory>>  {\n    fun getMatchingRecipe(world: ServerWorld, itemStack: ItemStack): Collection<T>\n\n    fun getMatchingRecipe(world: ServerWorld, fluidInput: FluidVariant): Collection<T>\n\n    fun getMatchingRecipe(world: ServerWorld, stacks: List<ItemStack>, fluids: List<IRFluidTank>): Collection<T> {\n        return when {\n            stacks.isEmpty() && fluids.isEmpty() -> emptyList()\n            stacks.isEmpty() -> fluids.flatMap { getMatchingRecipe(world, it.resource) }\n            fluids.isEmpty() -> stacks.flatMap { getMatchingRecipe(world, it) }\n            else -> stacks.flatMap { getMatchingRecipe(world, it) }.asMutableList().also { results ->\n                results.addAll(fluids.flatMap { getMatchingRecipe(world, it.resource) })\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/SelfRemainderRecipe.kt",
    "content": "package me.steven.indrev.recipes\n\nimport com.google.gson.JsonObject\nimport me.steven.indrev.FabricRecipeRemainder\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.inventory.CraftingInventory\nimport net.minecraft.item.ItemStack\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.recipe.Ingredient\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.recipe.ShapelessRecipe\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.JsonHelper\nimport net.minecraft.util.collection.DefaultedList\n\nclass SelfRemainderRecipe(id: Identifier, group: String, output: ItemStack, ingredients: DefaultedList<Ingredient>) :\n    ShapelessRecipe(id, group, output, ingredients) {\n    override fun craft(input: CraftingInventory): ItemStack {\n        return output.copy()\n    }\n\n    override fun getRemainder(input: CraftingInventory): DefaultedList<ItemStack> {\n        val defaultedList = DefaultedList.ofSize(input.size(), ItemStack.EMPTY)\n        for (i in 0 until input.size()) {\n            val invStack: ItemStack = input.getStack(i)\n            if (invStack.item is FabricRecipeRemainder) {\n                val remainder =\n                    (invStack.item as FabricRecipeRemainder).getRemainder(invStack.copy(), input, null)\n                defaultedList[i] = remainder\n            }\n        }\n        return defaultedList\n    }\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"selfremainder\")\n        val SERIALIZER = Serializer()\n\n        class Serializer : ShapelessRecipe.Serializer() {\n            override fun read(identifier: Identifier, jsonObject: JsonObject): SelfRemainderRecipe {\n                val shaped = super.read(identifier, jsonObject)!!\n                val group = JsonHelper.getString(jsonObject, \"group\", \"\")\n                return SelfRemainderRecipe(shaped.id, group!!, shaped.output, shaped.ingredients)\n            }\n\n            override fun read(identifier: Identifier?, packetByteBuf: PacketByteBuf?): SelfRemainderRecipe {\n                val shaped = super.read(identifier, packetByteBuf)\n                return SelfRemainderRecipe(shaped.id, shaped.group, shaped.output, shaped.ingredients)\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/CompressorRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass CompressorRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val ticks: Int\n) : IRRecipe {\n\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"compress\")\n        val TYPE = IRRecipeType<CompressorRecipe>(IDENTIFIER)\n        val SERIALIZER = Serializer()\n\n        class Serializer : IRRecipe.IRRecipeSerializer<CompressorRecipe>({ id, input, output, ticks -> CompressorRecipe(id,  input, output, ticks) })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/CondenserRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.IRFluidAmount\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass CondenserRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val fluidInput: Array<IRFluidAmount>,\n    override val ticks: Int,\n) : IRFluidRecipe() {\n    override val fluidOutput: Array<IRFluidAmount> = emptyArray()\n\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"condenser\")\n        val TYPE = IRRecipeType<CondenserRecipe>(IDENTIFIER)\n        val SERIALIZER = Serializer()\n\n        class Serializer : IRFluidRecipeSerializer<CondenserRecipe>({ id, ingredients, output, fluidInput, _, ticks -> CondenserRecipe(id, ingredients, output,\n            fluidInput, ticks) })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/DistillerRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.IRFluidAmount\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass DistillerRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val fluidInput: Array<IRFluidAmount>,\n    override val ticks: Int,\n) : IRFluidRecipe() {\n    override val fluidOutput: Array<IRFluidAmount> = emptyArray()\n\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"distiller\")\n        val TYPE = IRRecipeType<DistillerRecipe>(IDENTIFIER)\n        val SERIALIZER = Serializer()\n\n        class Serializer : IRFluidRecipeSerializer<DistillerRecipe>({ id, ingredients, output, fluidInput, _, ticks -> DistillerRecipe(id, ingredients, output, fluidInput!!, ticks) })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/ElectrolysisRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.IRFluidAmount\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass ElectrolysisRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val fluidInput: Array<IRFluidAmount>,\n    override val fluidOutput: Array<IRFluidAmount>,\n    override val ticks: Int\n) : IRFluidRecipe() {\n\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"electrolysis\")\n        val TYPE = IRRecipeType<ElectrolysisRecipe>(IDENTIFIER)\n        val SERIALIZER = Serializer()\n\n        class Serializer : IRFluidRecipeSerializer<ElectrolysisRecipe>({ id, ingredients, output, fluidInput, fluidOutput, ticks -> ElectrolysisRecipe(id, ingredients, output, fluidInput, fluidOutput, ticks) })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/FluidInfuserRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.IRFluidAmount\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass FluidInfuserRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val fluidInput: Array<IRFluidAmount>,\n    override val fluidOutput: Array<IRFluidAmount>,\n    override val ticks: Int,\n) : IRFluidRecipe() {\n\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"fluid_infuse\")\n        val TYPE = IRRecipeType<FluidInfuserRecipe>(IDENTIFIER)\n        val SERIALIZER = Serializer()\n\n        class Serializer : IRFluidRecipeSerializer<FluidInfuserRecipe>({ id, ingredients, output, fluidInput, fluidOutput, ticks -> FluidInfuserRecipe(id, ingredients, output, fluidInput, fluidOutput, ticks) })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/IRFluidRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport com.google.gson.JsonObject\nimport me.steven.indrev.components.CraftingComponent\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.*\nimport net.minecraft.item.ItemStack\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.recipe.Ingredient\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nabstract class IRFluidRecipe : IRRecipe {\n    abstract val fluidInput: Array<IRFluidAmount>\n    abstract val fluidOutput: Array<IRFluidAmount>\n\n    override fun matches(inv: List<ItemStack>, fluidVolume: List<IRFluidTank>): Boolean {\n        return when {\n            fluidInput.isNotEmpty() -> fluidVolume.isNotEmpty() && fluidInput.indices.all { index ->\n                val vol = fluidVolume[index]\n                vol.resource == fluidInput[index].resource && vol.amount >= fluidInput[index].amount() } && super.matches(inv, fluidVolume)\n            else -> super.matches(inv, fluidVolume)\n        }\n    }\n\n    override fun canStart(component: CraftingComponent<*>): Boolean {\n        val fluidComponent = component.fluidComponent!!\n        val outputTankVolume = fluidComponent.outputTanks\n        if (fluidOutput.isNotEmpty() && fluidOutput.indices.any { index ->\n                val vol = fluidComponent[outputTankVolume[index]]\n                !vol.isEmpty && (vol.resource != fluidOutput[index].resource || vol.amount + fluidOutput[index].amount() > fluidComponent.limit)\n            }\n        )\n            return false\n        return super.canStart(component)\n    }\n\n    open class IRFluidRecipeSerializer<T : IRFluidRecipe>(private val factory: (Identifier, Array<InputEntry>, Array<OutputEntry>, Array<IRFluidAmount>, Array<IRFluidAmount>, Int) -> T) : RecipeSerializer<T> {\n        override fun read(id: Identifier, buf: PacketByteBuf): T {\n            val ticks = buf.readInt()\n            val inputFluidsSize = buf.readInt()\n            val inputFluids = mutableListOf<IRFluidAmount>()\n            (0 until inputFluidsSize).forEach { _ ->\n                inputFluids.add(fromPacket(buf))\n            }\n\n            val outputFluidsSize = buf.readInt()\n            val outputFluids = mutableListOf<IRFluidAmount>()\n            (0 until outputFluidsSize).forEach { _ ->\n                outputFluids.add(fromPacket(buf))\n            }\n            val size = buf.readInt()\n            val ingredients = mutableListOf<InputEntry>()\n            (0 until size).forEach { _ ->\n                val ingredient = Ingredient.fromPacket(buf)\n                val count = buf.readInt()\n                ingredients.add(InputEntry(ingredient, count))\n            }\n            val outputSize = buf.readInt()\n            val output = mutableListOf<OutputEntry>()\n            (0 until outputSize).forEach { _ ->\n                val stack = buf.readItemStack()\n                val chance = buf.readDouble()\n                output.add(OutputEntry(stack, chance))\n            }\n            return factory(id, ingredients.toTypedArray(), output.toTypedArray(), inputFluids.toTypedArray(), outputFluids.toTypedArray(), ticks)\n        }\n\n        override fun read(id: Identifier, json: JsonObject): T {\n            val ingredients = IRRecipe.ingredientsFromElement(json[\"ingredients\"])\n            val ticks = json.get(\"processTime\").asInt\n            val output = IRRecipe.itemStacksFromElement(json[\"output\"])\n            val fluidInputJson = json.get(\"fluidInput\")\n            val fluidInput = if (fluidInputJson == null) emptyArray() else getFluidFromJson(fluidInputJson)\n\n            val fluidOutputJson = json.get(\"fluidOutput\")\n            val fluidOutput = if (fluidOutputJson == null) emptyArray() else getFluidFromJson(fluidOutputJson)\n\n            return factory(id, ingredients, output, fluidInput, fluidOutput, ticks)\n        }\n\n        override fun write(buf: PacketByteBuf, recipe: T) {\n            buf.writeInt(recipe.ticks)\n            buf.writeInt(recipe.fluidInput.size)\n            recipe.fluidInput.forEach { fluidInput ->\n                fluidInput.toPacket(buf)\n            }\n            buf.writeInt(recipe.fluidOutput.size)\n            recipe.fluidOutput.forEach { fluidOutput ->\n                fluidOutput.toPacket(buf)\n            }\n            buf.writeInt(recipe.input.size)\n            recipe.input.forEach { (ingredient, count) ->\n                ingredient.write(buf)\n                buf.writeInt(count)\n            }\n            buf.writeInt(recipe.outputs.size)\n            recipe.outputs.forEach { (stack, chance) ->\n                buf.writeItemStack(stack)\n                buf.writeDouble(chance)\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/IRRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport com.google.gson.JsonArray\nimport com.google.gson.JsonElement\nimport com.google.gson.JsonObject\nimport me.steven.indrev.components.CraftingComponent\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.IRFluidTank\nimport me.steven.indrev.utils.asMutableList\nimport net.minecraft.inventory.Inventory\nimport net.minecraft.item.ItemStack\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.recipe.Ingredient\nimport net.minecraft.recipe.Recipe\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.JsonHelper\nimport net.minecraft.util.collection.DefaultedList\nimport net.minecraft.util.registry.Registry\nimport net.minecraft.world.World\nimport java.util.*\n\ninterface IRRecipe : Recipe<Inventory> {\n    val identifier: Identifier\n    val input: Array<InputEntry>\n    val outputs: Array<OutputEntry>\n    val ticks: Int\n\n    override fun getId(): Identifier = identifier\n\n    @Deprecated(\"Unsupported method for Industrial Revolution's recipes\", replaceWith = ReplaceWith(\"craft(Random)\"), DeprecationLevel.ERROR)\n    override fun craft(inv: Inventory?): ItemStack = throw IllegalArgumentException(\"Unsupported method for Industrial Revolution's recipes\")\n    @Deprecated(\"Unsupported method for Industrial Revolution's recipes\", replaceWith = ReplaceWith(\"output\"), DeprecationLevel.ERROR)\n    override fun getOutput(): ItemStack = outputs.firstOrNull()?.stack ?: ItemStack.EMPTY\n    @Deprecated(\"Unsupported method for Industrial Revolution's recipes\", replaceWith = ReplaceWith(\"input\"), DeprecationLevel.ERROR)\n    override fun getIngredients(): DefaultedList<Ingredient> = DefaultedList.of()\n    @Deprecated(\"Unsupported method for Industrial Revolution's recipes\", replaceWith = ReplaceWith(\"matches(Inventory, FluidVolume?)\"), DeprecationLevel.ERROR)\n    override fun matches(inv: Inventory?, world: World?): Boolean = throw IllegalArgumentException(\"Unsupported method for Industrial Revolution's recipes\")\n\n    override fun getType(): IRRecipeType<*>\n\n    fun canStart(component: CraftingComponent<*>): Boolean {\n        return component.outputSlots!!.isEmpty() || outputs.all { component.fits(it.stack) }\n    }\n\n    override fun isEmpty(): Boolean {\n        return input.isEmpty() || input.any { entry -> entry.ingredient.matchingStacks.isEmpty() }\n    }\n\n    override fun isIgnoredInRecipeBook(): Boolean = true\n\n    fun craft(random: Random?): List<ItemStack> {\n        val produced = ArrayList<ItemStack>(outputs.size)\n        outputs.forEach { (stack, chance) ->\n            if (chance >= 1.0 || random != null && random.nextDouble() > chance) produced.add(stack.copy())\n        }\n        return produced\n    }\n\n    fun matches(inv: List<ItemStack>, fluidVolume: List<IRFluidTank>): Boolean {\n        if (inv.isEmpty()) return true\n        else if (inv.size == 1 && input.size == 1) return matches(inv.first(), fluidVolume)\n        val remainder = input.map { it.copy() }.asMutableList()\n        for (stack in inv) {\n            val result = remainder.firstOrNull { (ingredient, count) -> ingredient.test(stack) && stack.count >= count } ?: continue\n            result.count -= stack.count\n            if (result.count <= 0) remainder.remove(result)\n        }\n        return remainder.isEmpty()\n    }\n\n    fun matches(stack: ItemStack, fluidVolume: List<IRFluidTank>): Boolean {\n        assert(input.size == 1)\n        val (ingredient, count) = input.first()\n        if (ingredient.test(stack) && stack.count >= count) return true\n        return false\n    }\n\n    open class IRRecipeSerializer<T : IRRecipe>(private val factory: (Identifier, Array<InputEntry>, Array<OutputEntry>, Int) -> T) : RecipeSerializer<T> {\n        override fun read(id: Identifier, json: JsonObject): T {\n            val ingredients = ingredientsFromElement(json[\"ingredients\"])\n            val ticks = json.get(\"processTime\").asInt\n            val output = itemStacksFromElement(json[\"output\"])\n            return factory(id, ingredients, output, ticks)\n        }\n\n        override fun read(id: Identifier, buf: PacketByteBuf): T {\n            val size = buf.readInt()\n            val ingredients = mutableListOf<InputEntry>()\n            (0 until size).forEach { _ ->\n                val ingredient = Ingredient.fromPacket(buf)\n                val count = buf.readInt()\n                ingredients.add(InputEntry(ingredient, count))\n            }\n            val ticks = buf.readInt()\n            val outputSize = buf.readInt()\n            val output = mutableListOf<OutputEntry>()\n            (0 until outputSize).forEach { _ ->\n                val stack = buf.readItemStack()\n                val chance = buf.readDouble()\n                output.add(OutputEntry(stack, chance))\n            }\n            return factory(id, ingredients.toTypedArray(), output.toTypedArray(), ticks)\n        }\n\n        override fun write(buf: PacketByteBuf, recipe: T) {\n            buf.writeInt(recipe.input.size)\n            recipe.input.forEach { (ingredient, count) ->\n                ingredient.write(buf)\n                buf.writeInt(count)\n            }\n            buf.writeInt(recipe.ticks)\n            buf.writeInt(recipe.outputs.size)\n            recipe.outputs.forEach { (stack, chance) ->\n                buf.writeItemStack(stack)\n                buf.writeDouble(chance)\n            }\n        }\n    }\n\n    companion object {\n\n        fun ingredientsFromElement(jsonElement: JsonElement?): Array<InputEntry> {\n            return when (jsonElement) {\n                is JsonArray ->\n                    jsonElement.map { ingredientFromJson(it.asJsonObject) }.toTypedArray()\n                is JsonObject ->\n                    arrayOf(ingredientFromJson(jsonElement))\n                null -> return emptyArray()\n                else -> throw IllegalArgumentException(\"expected json object or array at 'ingredients', received $jsonElement\")\n            }\n        }\n\n        fun itemStacksFromElement(jsonElement: JsonElement?): Array<OutputEntry> {\n            return when (jsonElement) {\n                is JsonArray ->\n                    jsonElement.map { itemStackFromJson(it.asJsonObject)!! }.sortedByDescending { it.chance }.toTypedArray()\n                is JsonObject ->\n                    listOf(itemStackFromJson(jsonElement) ?: return emptyArray()).toTypedArray()\n                null -> return emptyArray()\n                else -> throw IllegalArgumentException(\"expected json object or array at 'ingredients', received $jsonElement\")\n            }\n        }\n\n        fun ingredientFromJson(json: JsonObject): InputEntry {\n            val ing = Ingredient.fromJson(json)\n            val count = JsonHelper.getInt(json, \"count\", 1)\n            return InputEntry(ing, count)\n        }\n\n        fun itemStackFromJson(json: JsonObject): OutputEntry? {\n            val itemId = json.get(\"item\").asString\n            if (itemId == \"empty\") return null\n            val item = Registry.ITEM.get(Identifier(itemId))\n            val output = ItemStack { item }\n            if (output.isEmpty) println(\"empty $itemId\")\n            output.count = JsonHelper.getInt(json, \"count\", 1)\n            val chance = JsonHelper.getFloat(json, \"chance\", 1f).toDouble()\n            return OutputEntry(output, chance)\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/IRRecipeType.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport com.google.common.collect.HashMultimap\nimport com.google.common.collect.ImmutableList\nimport com.google.common.collect.Multimap\nimport me.steven.indrev.recipes.IRecipeGetter\nimport me.steven.indrev.utils.getRecipes\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.recipe.RecipeType\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.Identifier\n\nclass IRRecipeType<T : IRRecipe>(val id: Identifier) : IRecipeGetter<T>, RecipeType<T> {\n\n    private val recipeCache: Multimap<Item, T> = HashMultimap.create()\n    private val fluidOnlyRecipeCache:  Multimap<FluidVariant, T> = HashMultimap.create()\n\n    override fun getMatchingRecipe(world: ServerWorld, itemStack: ItemStack): Collection<T> {\n        if (itemStack.isEmpty) return emptyList()\n        else if (recipeCache.containsKey(itemStack.item)) return recipeCache[itemStack.item]\n        val matches = ImmutableList.copyOf(\n            world.recipeManager.getRecipes(this).values\n                .filter { recipe -> recipe.input.any { it.ingredient.test(itemStack) } }\n        )\n        recipeCache.putAll(itemStack.item, matches)\n        return matches\n    }\n\n    override fun getMatchingRecipe(world: ServerWorld, fluidInput: FluidVariant): Collection<T> {\n        if (fluidOnlyRecipeCache.containsKey(fluidInput)) return fluidOnlyRecipeCache[fluidInput]\n        val matches = ImmutableList.copyOf(\n            world.recipeManager.getRecipes(this).values\n                .filter { recipe -> recipe is IRFluidRecipe && recipe.fluidInput.any { it.resource == fluidInput } }\n        )\n        fluidOnlyRecipeCache.putAll(fluidInput, matches)\n        return matches\n    }\n\n    fun clearCache() {\n        recipeCache.clear()\n        fluidOnlyRecipeCache.clear()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/InfuserRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass InfuserRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val ticks: Int\n) : IRRecipe {\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"infuse\")\n        val TYPE = IRRecipeType<InfuserRecipe>(IDENTIFIER)\n        val SERIALIZER = Serializer()\n\n        class Serializer : IRRecipe.IRRecipeSerializer<InfuserRecipe>({ id, input, output, ticks -> InfuserRecipe(id, input, output, ticks) })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/LaserRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass LaserRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val ticks: Int\n) : IRRecipe {\n\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"laser\")\n        val TYPE = IRRecipeType<LaserRecipe>(IDENTIFIER)\n        val SERIALIZER = Serializer()\n\n        class Serializer : IRRecipe.IRRecipeSerializer<LaserRecipe>({ id, input, output, ticks -> LaserRecipe(id,  input, output, ticks) })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/ModuleRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass ModuleRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val ticks: Int\n) : IRRecipe {\n\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"modules\")\n        val TYPE = IRRecipeType<ModuleRecipe>(IDENTIFIER)\n        val SERIALIZER = Serializer()\n\n        class Serializer : IRRecipe.IRRecipeSerializer<ModuleRecipe>({ id, input, output, ticks -> ModuleRecipe(id,  input, output, ticks) })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/PulverizerRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass PulverizerRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val ticks: Int\n) : IRRecipe {\n\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"pulverize\")\n        val TYPE = IRRecipeType<PulverizerRecipe>(IDENTIFIER)\n        val SERIALIZER = Serializer()\n\n        class Serializer : IRRecipe.IRRecipeSerializer<PulverizerRecipe>({ id, input, output, ticks -> PulverizerRecipe(id,  input, output, ticks) })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/RecyclerRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass RecyclerRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val ticks: Int\n) : IRRecipe {\n\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"recycle\")\n        val TYPE = IRRecipeType<RecyclerRecipe>(IDENTIFIER)\n        val SERIALIZER = Serializer()\n\n        class Serializer : IRRecipe.IRRecipeSerializer<RecyclerRecipe>({ id, input, output, ticks -> RecyclerRecipe(id, input, output, ticks) })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/SawmillRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass SawmillRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val ticks: Int\n) : IRRecipe {\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    companion object {\n        val IDENTIFIER = identifier(\"sawmill\")\n        val TYPE = IRRecipeType<SawmillRecipe>(IDENTIFIER)\n        val SERIALIZER = IRRecipe.IRRecipeSerializer(::SawmillRecipe)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/SmelterRecipe.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport me.steven.indrev.recipes.machines.entries.InputEntry\nimport me.steven.indrev.recipes.machines.entries.OutputEntry\nimport me.steven.indrev.utils.IRFluidAmount\nimport me.steven.indrev.utils.identifier\nimport net.minecraft.recipe.RecipeSerializer\nimport net.minecraft.util.Identifier\n\nclass SmelterRecipe(\n    override val identifier: Identifier,\n    override val input: Array<InputEntry>,\n    override val outputs: Array<OutputEntry>,\n    override val fluidOutput: Array<IRFluidAmount>,\n    override val ticks: Int,\n) : IRFluidRecipe() {\n    override val fluidInput: Array<IRFluidAmount> = emptyArray()\n\n    override fun getType(): IRRecipeType<*> = TYPE\n\n    override fun fits(width: Int, height: Int): Boolean = true\n\n    override fun getSerializer(): RecipeSerializer<*> = SERIALIZER\n\n    companion object {\n        val IDENTIFIER = identifier(\"smelter\")\n        val TYPE = IRRecipeType<SmelterRecipe>(IDENTIFIER)\n        val SERIALIZER = Serializer()\n\n        class Serializer : IRFluidRecipeSerializer<SmelterRecipe>({ id, ingredients, output, _, fluidOutput, ticks -> SmelterRecipe(id, ingredients, output,\n            fluidOutput, ticks) })\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/VanillaCookingRecipeCachedGetter.kt",
    "content": "package me.steven.indrev.recipes.machines\n\nimport com.google.common.collect.HashMultimap\nimport com.google.common.collect.Multimap\nimport me.steven.indrev.recipes.IRecipeGetter\nimport me.steven.indrev.utils.getRecipes\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.recipe.AbstractCookingRecipe\nimport net.minecraft.recipe.RecipeType\nimport net.minecraft.server.world.ServerWorld\n\nclass VanillaCookingRecipeCachedGetter<T : AbstractCookingRecipe>(private val type: RecipeType<T>) : IRecipeGetter<T> {\n\n    private val recipeCache: Multimap<Item, T> = HashMultimap.create()\n\n    override fun getMatchingRecipe(world: ServerWorld, itemStack: ItemStack): Collection<T> {\n        if (itemStack.isEmpty) return emptyList()\n        else if (recipeCache.containsKey(itemStack.item)) return recipeCache[itemStack.item]\n        val matches = world.recipeManager.getRecipes(type).values\n            .filter { recipe -> recipe.input.test(itemStack) }\n        recipeCache.putAll(itemStack.item, matches)\n        return matches\n    }\n\n    override fun getMatchingRecipe(world: ServerWorld, fluidInput: FluidVariant): Collection<T> = emptyList()\n\n    companion object {\n        val SMELTING = VanillaCookingRecipeCachedGetter(RecipeType.SMELTING)\n        val SMOKING = VanillaCookingRecipeCachedGetter(RecipeType.SMOKING)\n        val BLASTING = VanillaCookingRecipeCachedGetter(RecipeType.BLASTING)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/entries/InputEntry.kt",
    "content": "package me.steven.indrev.recipes.machines.entries\n\nimport net.minecraft.recipe.Ingredient\n\ndata class InputEntry(val ingredient: Ingredient, var count: Int)"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/recipes/machines/entries/OutputEntry.kt",
    "content": "package me.steven.indrev.recipes.machines.entries\n\nimport net.minecraft.item.ItemStack\n\ndata class OutputEntry(val stack: ItemStack, val chance: Double)"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/registry/IRBlockRegistry.kt",
    "content": "package me.steven.indrev.registry\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.cables.BasePipeBlockEntity\nimport me.steven.indrev.blockentities.miningrig.DrillBlockEntity\nimport me.steven.indrev.blockentities.farms.BiomassComposterBlockEntity\nimport me.steven.indrev.blockentities.generators.SteamTurbineBlockEntity\nimport me.steven.indrev.blockentities.generators.SteamTurbineSteamInputValveBlockEntity\nimport me.steven.indrev.blockentities.laser.CapsuleBlockEntity\nimport me.steven.indrev.blockentities.solarpowerplant.*\nimport me.steven.indrev.blockentities.storage.CabinetBlockEntity\nimport me.steven.indrev.blockentities.storage.TankBlockEntity\nimport me.steven.indrev.blocks.HeliostatBlock\nimport me.steven.indrev.blocks.machine.CapsuleBlock\nimport me.steven.indrev.blocks.machine.DrillBlock\nimport me.steven.indrev.blocks.machine.pipes.BasePipeBlock\nimport me.steven.indrev.blocks.machine.pipes.CableBlock\nimport me.steven.indrev.blocks.machine.pipes.FluidPipeBlock\nimport me.steven.indrev.blocks.machine.pipes.ItemPipeBlock\nimport me.steven.indrev.blocks.machine.solarpowerplant.*\nimport me.steven.indrev.blocks.misc.*\nimport me.steven.indrev.networks.energy.CableEnergyIo\nimport me.steven.indrev.utils.*\nimport net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings\nimport net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder\nimport net.fabricmc.fabric.api.registry.FlammableBlockRegistry\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage\nimport net.fabricmc.fabric.api.transfer.v1.item.ItemStorage\nimport net.minecraft.block.Block\nimport net.minecraft.block.Blocks\nimport net.minecraft.block.MapColor\nimport net.minecraft.block.Material\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.item.BlockItem\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.sound.BlockSoundGroup\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.registry.Registry\nimport team.reborn.energy.api.EnergyStorage\n\n@Suppress(\"MemberVisibilityCanBePrivate\")\nobject IRBlockRegistry {\n\n    fun registerAll() {\n\n        identifier(\"sulfur_crystal\").block(SULFUR_CRYSTAL_CLUSTER).item(IRItemRegistry.SULFUR_CRYSTAL_ITEM)\n\n        identifier(\"planks\").block(PLANKS).item(BlockItem(PLANKS, itemSettings()))\n        identifier(\"plank_block\").block(PLANK_BLOCK).item(BlockItem(PLANK_BLOCK, itemSettings()))\n        FlammableBlockRegistry.getDefaultInstance().add(PLANKS, 10, 40)\n        FlammableBlockRegistry.getDefaultInstance().add(PLANK_BLOCK, 10, 40)\n\n        identifier(\"biomass_composter\").block(BIOMASS_COMPOSTER_BLOCK).item(BIOMASS_COMPOSTER_ITEM).blockEntityType(BIOMASS_COMPOSTER_BLOCK_ENTITY)\n\n        FluidStorage.SIDED.registerForBlockEntity({ be, c ->\n            if (c != Direction.UP) be.fluidInv\n            else null\n        }, BIOMASS_COMPOSTER_BLOCK_ENTITY)\n\n        ItemStorage.SIDED.registerForBlockEntity({ be, c ->\n            if (c != Direction.UP) be.itemInv\n            else null\n        }, BIOMASS_COMPOSTER_BLOCK_ENTITY)\n\n        identifier(\"wither_proof_obsidian\").block(WITHER_PROOF_OBSIDIAN).item(BlockItem(WITHER_PROOF_OBSIDIAN, itemSettings()))\n\n        identifier(\"machine_block\").block(MACHINE_BLOCK).item(BlockItem(MACHINE_BLOCK, itemSettings()))\n\n        identifier(\"controller\").block(CONTROLLER).item(BlockItem(CONTROLLER, itemSettings()))\n        identifier(\"frame\").block(FRAME).item(BlockItem(FRAME, itemSettings()))\n        identifier(\"duct\").block(DUCT).item(BlockItem(DUCT, itemSettings()))\n        identifier(\"silo\").block(SILO).item(BlockItem(SILO, itemSettings()))\n        identifier(\"warning_strobe\").block(WARNING_STROBE).item(BlockItem(WARNING_STROBE, itemSettings()))\n        identifier(\"intake\").block(INTAKE).item(BlockItem(INTAKE, itemSettings()))\n        identifier(\"cabinet\").block(CABINET).item(BlockItem(CABINET, itemSettings())).blockEntityType(CABINET_BLOCK_ENTITY_TYPE)\n\n        identifier(\"drill_top\").block(DRILL_TOP)\n        identifier(\"drill_middle\").block(DRILL_MIDDLE)\n        identifier(\"drill_bottom\").block(DRILL_BOTTOM).item(BlockItem(DRILL_BOTTOM, itemSettings()))\n        identifier(\"drill\").blockEntityType(DRILL_BLOCK_ENTITY_TYPE)\n\n        identifier(\"tank\").block(TANK_BLOCK).item(IRItemRegistry.TANK_BLOCK_ITEM).blockEntityType(TANK_BLOCK_ENTITY)\n\n        FluidStorage.SIDED.registerForBlockEntity({ be, _ ->\n            val combinedStorage = TankBlockEntity.CombinedTankStorage()\n            TankBlock.findAllTanks(be.world!!.getChunk(be.pos), be.world!!.getBlockState(be.pos), be.pos, mutableSetOf(), combinedStorage)\n            combinedStorage\n        }, TANK_BLOCK_ENTITY)\n\n        identifier(\"capsule\").block(CAPSULE_BLOCK).item(IRItemRegistry.CAPSULE_BLOCK_ITEM).blockEntityType(CAPSULE_BLOCK_ENTITY)\n\n        identifier(\"fluid_pipe_mk1\").block(FLUID_PIPE_MK1)\n        identifier(\"fluid_pipe_mk2\").block(FLUID_PIPE_MK2)\n        identifier(\"fluid_pipe_mk3\").block(FLUID_PIPE_MK3)\n        identifier(\"fluid_pipe_mk4\").block(FLUID_PIPE_MK4)\n\n        identifier(\"item_pipe_mk1\").block(ITEM_PIPE_MK1)\n        identifier(\"item_pipe_mk2\").block(ITEM_PIPE_MK2)\n        identifier(\"item_pipe_mk3\").block(ITEM_PIPE_MK3)\n        identifier(\"item_pipe_mk4\").block(ITEM_PIPE_MK4)\n\n        identifier(\"cable_mk1\").block(CABLE_MK1).blockEntityType(COVERABLE_BLOCK_ENTITY_TYPE_MK1)\n        identifier(\"cable_mk2\").block(CABLE_MK2).blockEntityType(COVERABLE_BLOCK_ENTITY_TYPE_MK2)\n        identifier(\"cable_mk3\").block(CABLE_MK3).blockEntityType(COVERABLE_BLOCK_ENTITY_TYPE_MK3)\n        identifier(\"cable_mk4\").block(CABLE_MK4).blockEntityType(COVERABLE_BLOCK_ENTITY_TYPE_MK4)\n\n        EnergyStorage.SIDED.registerForBlocks({ world, pos, _, be, dir ->\n            if (world is ServerWorld && be is BasePipeBlockEntity) {\n                val energyNetwork = world.energyNetworkState.networksByPos[pos.asLong()]\n                if (energyNetwork != null && be.connections[dir]?.isConnected() == true) {\n                    return@registerForBlocks CableEnergyIo(energyNetwork, pos, dir)\n                }\n            }\n            CableEnergyIo.NO_NETWORK\n        }, CABLE_MK1, CABLE_MK2, CABLE_MK3, CABLE_MK4)\n\n        identifier(\"heliostat\")\n            .block(HELIOSTAT_BLOCK)\n            .item(HELIOSTAT_BLOCK_ITEM)\n            .blockEntityType(HELIOSTAT_BLOCK_ENTITY)\n\n        identifier(\"resistant_glass\")\n            .block(RESISTANT_GLASS_BLOCK)\n            .item(RESISTANT_GLASS_BLOCK_ITEM)\n\n        identifier(\"solar_receiver\")\n            .block(SOLAR_RECEIVER_BLOCK)\n            .item(SOLAR_RECEIVER_BLOCK_ITEM)\n            .blockEntityType(SOLAR_RECEIVER_BLOCK_ENTITY)\n\n        identifier(\"fluid_valve\").block(FLUID_VALVE).item(FLUID_VALVE_ITEM)\n\n        FluidStorage.SIDED.registerForBlocks({ world, pos, _, _, dir ->\n            val aboveBlockEntity = world.getBlockEntity(pos.up()) as? SolarPowerPlantTowerBlockEntity\n            return@registerForBlocks aboveBlockEntity?.fluidComponent?.getCachedSide(dir)\n        }, FLUID_VALVE)\n\n        identifier(\"steam_turbine_steam_input_valve\")\n            .block(STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK)\n            .item(STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ITEM)\n            .blockEntityType(STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ENTITY)\n\n        FluidStorage.SIDED.registerForBlockEntity({ be, _ -> be.getSteamTurbine()?.fluidComponent }, STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ENTITY)\n\n        identifier(\"steam_turbine_energy_output\").block(STEAM_TURBINE_ENERGY_OUTPUT).item(STEAM_TURBINE_ENERGY_OUTPUT_ITEM)\n\n        EnergyStorage.SIDED.registerForBlocks({ world, pos, _, _, s ->\n            val turbineBlockEntity = world.getBlockEntity(pos.up()) as? SteamTurbineBlockEntity\n            if (turbineBlockEntity?.multiblockComponent?.isBuilt(world, pos.up(), turbineBlockEntity.cachedState) == true)\n                turbineBlockEntity.storage.getSideStorage(s)\n            else\n                null\n        }, STEAM_TURBINE_ENERGY_OUTPUT)\n\n        identifier(\"steam_turbine_casing\").block(STEAM_TURBINE_CASING_BLOCK).item(STEAM_TURBINE_CASING_BLOCK_ITEM)\n        identifier(\"steam_turbine_rotor\").block(STEAM_TURBINE_ROTOR_BLOCK).item(STEAM_TURBINE_ROTOR_BLOCK_ITEM)\n        identifier(\"steam_turbine_pressure_valve\").block(STEAM_TURBINE_PRESSURE_VALVE_BLOCK).item(STEAM_TURBINE_PRESSURE_VALVE_BLOCK_ITEM)\n\n        identifier(\"solar_power_plant_tower\")\n            .block(SOLAR_POWER_PLANT_TOWER_BLOCK)\n            .item(SOLAR_POWER_PLANT_TOWER_BLOCK_ITEM)\n            .blockEntityType(SOLAR_POWER_PLANT_TOWER_BLOCK_ENTITY)\n    }\n\n    val SULFUR_CRYSTAL_CLUSTER = SulfurCrystalBlock(FabricBlockSettings.of(Material.METAL).sounds(BlockSoundGroup.GLASS).requiresTool().strength(3f, 3f))\n\n    val NIKOLITE_ORE = { Registry.BLOCK.get(identifier(\"nikolite_ore\")) }\n    val TIN_ORE = { Registry.BLOCK.get(identifier(\"tin_ore\")) }\n    val LEAD_ORE = { Registry.BLOCK.get(identifier(\"lead_ore\")) }\n    val SILVER_ORE = { Registry.BLOCK.get(identifier(\"silver_ore\")) }\n    val TUNGSTEN_ORE = { Registry.BLOCK.get(identifier(\"tungsten_ore\")) }\n\n    val DEEPSLATE_NIKOLITE_ORE = { Registry.BLOCK.get(identifier(\"deepslate_nikolite_ore\")) }\n    val DEEPSLATE_TIN_ORE = { Registry.BLOCK.get(identifier(\"deepslate_tin_ore\")) }\n    val DEEPSLATE_LEAD_ORE = { Registry.BLOCK.get(identifier(\"deepslate_lead_ore\")) }\n    val DEEPSLATE_SILVER_ORE = { Registry.BLOCK.get(identifier(\"deepslate_silver_ore\")) }\n    val DEEPSLATE_TUNGSTEN_ORE = { Registry.BLOCK.get(identifier(\"deepslate_tungsten_ore\")) }\n\n    val MACHINE_BLOCK = Block(\n        FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F)\n    )\n    val PLANKS = PlankBlock(\n        FabricBlockSettings.of(Material.WOOD, MapColor.BROWN).strength(2.0f).sounds(BlockSoundGroup.WOOD)\n    )\n    val PLANK_BLOCK = Block(\n        FabricBlockSettings.of(Material.WOOD, MapColor.BROWN).strength(3F, 6F).sounds(BlockSoundGroup.WOOD)\n    )\n\n    val BIOMASS_COMPOSTER_BLOCK = BiomassComposterBlock()\n    val BIOMASS_COMPOSTER_ITEM = BlockItem(BIOMASS_COMPOSTER_BLOCK, itemSettings())\n    val BIOMASS_COMPOSTER_BLOCK_ENTITY = FabricBlockEntityTypeBuilder.create(::BiomassComposterBlockEntity, BIOMASS_COMPOSTER_BLOCK).build()\n\n    val WITHER_PROOF_OBSIDIAN = Block(\n        FabricBlockSettings.of(Material.STONE, MapColor.BLACK).requiresTool().strength(50.0F, 1200.0F).sounds(BlockSoundGroup.STONE)\n    )\n\n    val CONTROLLER = HorizontalFacingBlock(\n        FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F)\n    )\n    val DUCT = DuctBlock(\n        FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F)\n    )\n    val FRAME = Block(\n        FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F)\n    )\n    val SILO = Block(\n        FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F)\n    )\n    val WARNING_STROBE = WarningStrobeBlock(\n        FabricBlockSettings.of(Material.METAL).requiresTool().luminance(15).nonOpaque().strength(3F, 6F)\n    )\n    val INTAKE =  HorizontalFacingBlock(\n        FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F)\n    )\n    val CABINET = CabinetBlock(\n        FabricBlockSettings.of(Material.METAL).requiresTool().strength(3F, 6F)\n    )\n    val CABINET_BLOCK_ENTITY_TYPE: BlockEntityType<CabinetBlockEntity> = FabricBlockEntityTypeBuilder.create(::CabinetBlockEntity, CABINET).build(null)\n\n    val DRILL_TOP = DrillBlock.TopDrillBlock(\n        FabricBlockSettings.of(Material.METAL).requiresTool().nonOpaque().strength(3F, 6F)\n    )\n    val DRILL_MIDDLE = DrillBlock.MiddleDrillBlock(\n        FabricBlockSettings.of(Material.METAL).requiresTool().nonOpaque().strength(3F, 6F)\n    )\n    val DRILL_BOTTOM = DrillBlock.BottomDrillBlock(\n        FabricBlockSettings.of(Material.METAL).requiresTool().nonOpaque().strength(3F, 6F)\n    )\n    val DRILL_BLOCK_ENTITY_TYPE: BlockEntityType<DrillBlockEntity> = BlockEntityType.Builder.create(::DrillBlockEntity, DRILL_BOTTOM).build(null)\n\n    val TANK_BLOCK = TankBlock(FabricBlockSettings.of(Material.GLASS).nonOpaque().requiresTool().strength(1f, 1f))\n\n    val TANK_BLOCK_ENTITY: BlockEntityType<TankBlockEntity> = BlockEntityType.Builder.create(::TankBlockEntity, TANK_BLOCK).build(null)\n\n    val CAPSULE_BLOCK = CapsuleBlock()\n\n    val CAPSULE_BLOCK_ENTITY: BlockEntityType<CapsuleBlockEntity> = BlockEntityType.Builder.create(::CapsuleBlockEntity, CAPSULE_BLOCK).build(null)\n\n    val FLUID_PIPE_MK1 = FluidPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK1)\n    val FLUID_PIPE_MK2 = FluidPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK2)\n    val FLUID_PIPE_MK3 = FluidPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK3)\n    val FLUID_PIPE_MK4 = FluidPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK4)\n\n    val ITEM_PIPE_MK1 = ItemPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK1)\n    val ITEM_PIPE_MK2 = ItemPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK2)\n    val ITEM_PIPE_MK3 = ItemPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK3)\n    val ITEM_PIPE_MK4 = ItemPipeBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK4)\n\n    val CABLE_MK1 = CableBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK1)\n    val CABLE_MK2 = CableBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK2)\n    val CABLE_MK3 = CableBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK3)\n    val CABLE_MK4 = CableBlock(FabricBlockSettings.copyOf(Blocks.IRON_BLOCK).nonOpaque().solidBlock { _, _, _ -> false }.requiresTool(), Tier.MK4)\n\n    val COVERABLE_BLOCK_ENTITY_TYPE_MK1 = FabricBlockEntityTypeBuilder.create({ pos, state -> BasePipeBlockEntity((state.block as BasePipeBlock).type, Tier.MK1, pos, state) }, FLUID_PIPE_MK1, ITEM_PIPE_MK1, CABLE_MK1).build(null)\n\n    val COVERABLE_BLOCK_ENTITY_TYPE_MK2 = FabricBlockEntityTypeBuilder.create({ pos, state -> BasePipeBlockEntity((state.block as BasePipeBlock).type, Tier.MK2, pos, state) }, FLUID_PIPE_MK2, ITEM_PIPE_MK2, CABLE_MK2).build(null)\n\n    val COVERABLE_BLOCK_ENTITY_TYPE_MK3 = FabricBlockEntityTypeBuilder.create({ pos, state -> BasePipeBlockEntity((state.block as BasePipeBlock).type, Tier.MK3, pos, state) }, FLUID_PIPE_MK3, ITEM_PIPE_MK3, CABLE_MK3).build(null)\n\n    val COVERABLE_BLOCK_ENTITY_TYPE_MK4 = FabricBlockEntityTypeBuilder.create({ pos, state -> BasePipeBlockEntity((state.block as BasePipeBlock).type, Tier.MK4, pos, state) }, FLUID_PIPE_MK4, ITEM_PIPE_MK4, CABLE_MK4).build(null)\n\n    val HELIOSTAT_BLOCK = HeliostatBlock(\n        FabricBlockSettings.of(Material.METAL).requiresTool().nonOpaque().strength(3F, 6F)\n    )\n    val HELIOSTAT_BLOCK_ITEM = BlockItem(HELIOSTAT_BLOCK, itemSettings())\n    val HELIOSTAT_BLOCK_ENTITY = BlockEntityType.Builder.create(::HeliostatBlockEntity, HELIOSTAT_BLOCK).build(null)\n\n    val SOLAR_RECEIVER_BLOCK = SolarReceiverBlock(\n        FabricBlockSettings.of(Material.METAL).requiresTool().nonOpaque().strength(3F, 6F)\n    )\n    val SOLAR_RECEIVER_BLOCK_ITEM = BlockItem(SOLAR_RECEIVER_BLOCK, itemSettings())\n    val SOLAR_RECEIVER_BLOCK_ENTITY = BlockEntityType.Builder.create(::SolarReceiverBlockEntity, SOLAR_RECEIVER_BLOCK).build(null)\n\n    val STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK = SteamTurbineSteamInputValveBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F))\n    val STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ITEM = BlockItem(STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK, itemSettings())\n    val STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK_ENTITY = BlockEntityType.Builder.create(::SteamTurbineSteamInputValveBlockEntity, STEAM_TURBINE_STEAM_INPUT_VALVE_BLOCK).build(null)\n\n    val RESISTANT_GLASS_BLOCK = Block(\n        FabricBlockSettings.of(Material.GLASS).requiresTool().nonOpaque().strength(3F, 6F)\n    )\n    val RESISTANT_GLASS_BLOCK_ITEM = BlockItem(RESISTANT_GLASS_BLOCK, itemSettings())\n\n    val FLUID_VALVE = FluidValveBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F))\n    val FLUID_VALVE_ITEM = BlockItem(FLUID_VALVE, itemSettings())\n\n    val STEAM_TURBINE_ENERGY_OUTPUT = HorizontalFacingBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F))\n    val STEAM_TURBINE_ENERGY_OUTPUT_ITEM = BlockItem(STEAM_TURBINE_ENERGY_OUTPUT, itemSettings())\n\n    val STEAM_TURBINE_CASING_BLOCK = Block(FabricBlockSettings.of(Material.METAL).strength(3F, 6F))\n    val STEAM_TURBINE_CASING_BLOCK_ITEM = BlockItem(STEAM_TURBINE_CASING_BLOCK, itemSettings())\n\n    val STEAM_TURBINE_ROTOR_BLOCK = VerticalFacingBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F))\n    val STEAM_TURBINE_ROTOR_BLOCK_ITEM = BlockItem(STEAM_TURBINE_ROTOR_BLOCK, itemSettings())\n\n    val STEAM_TURBINE_PRESSURE_VALVE_BLOCK = HorizontalFacingBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F))\n    val STEAM_TURBINE_PRESSURE_VALVE_BLOCK_ITEM = BlockItem(STEAM_TURBINE_PRESSURE_VALVE_BLOCK, itemSettings())\n\n    val SOLAR_POWER_PLANT_TOWER_BLOCK = SolarPowerPlantTowerBlock(FabricBlockSettings.of(Material.METAL).strength(3F, 6F))\n    val SOLAR_POWER_PLANT_TOWER_BLOCK_ITEM = BlockItem(SOLAR_POWER_PLANT_TOWER_BLOCK, itemSettings())\n    val SOLAR_POWER_PLANT_TOWER_BLOCK_ENTITY = BlockEntityType.Builder.create(::SolarPowerPlantTowerBlockEntity, SOLAR_POWER_PLANT_TOWER_BLOCK).build(null)\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/registry/IRFluidFuelRegistry.kt",
    "content": "package me.steven.indrev.registry\n\nimport it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap\nimport net.minecraft.fluid.Fluid\n\nobject IRFluidFuelRegistry {\n    private val registry = Object2ObjectOpenHashMap<Fluid, FluidFuelInfo>()\n\n    fun register(fluid: Fluid, info: FluidFuelInfo) {\n        registry[fluid] = info\n    }\n\n    fun register(fluid: Fluid, burnTime: Int, combustionTemperature: Int, ratio: Int, consumptionRatio: Long) {\n        registry[fluid] = FluidFuelInfo(burnTime, combustionTemperature, ratio, consumptionRatio)\n    }\n\n    fun isFuel(fluid: Fluid) = registry.contains(fluid)\n\n    fun get(fluid: Fluid) = registry[fluid]\n\n    data class FluidFuelInfo(val burnTime: Int, val combustionTemperature: Int, val generationRatio: Int, val consumptionRatio: Long)\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/registry/IRFluidRegistry.kt",
    "content": "package me.steven.indrev.registry\n\nimport me.steven.indrev.blocks.misc.AcidFluidBlock\nimport me.steven.indrev.datagen.utils.*\nimport me.steven.indrev.fluids.BaseFluid\nimport me.steven.indrev.utils.*\nimport net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings\nimport net.fabricmc.fabric.api.`object`.builder.v1.block.FabricMaterialBuilder\nimport net.minecraft.block.FluidBlock\nimport net.minecraft.block.MapColor\nimport net.minecraft.block.Material\nimport net.minecraft.item.BucketItem\nimport net.minecraft.item.Items\n\n@Suppress(\"MemberVisibilityCanBePrivate\")\nobject IRFluidRegistry {\n    fun registerAll() {\n\n        COOLANT_IDENTIFIER.block(COOLANT)\n        identifier(\"${COOLANT_IDENTIFIER.path}_still\").fluid(COOLANT_STILL)\n        identifier(\"${COOLANT_IDENTIFIER.path}_flowing\").fluid(COOLANT_FLOWING)\n        identifier(\"${COOLANT_IDENTIFIER.path}_bucket\").item(COOLANT_BUCKET)\n\n        MOLTEN_NETHERITE_IDENTIFIER.block(MOLTEN_NETHERITE)\n        identifier(\"${MOLTEN_NETHERITE_IDENTIFIER.path}_still\").fluid(MOLTEN_NETHERITE_STILL)\n        identifier(\"${MOLTEN_NETHERITE_IDENTIFIER.path}_flowing\").fluid(MOLTEN_NETHERITE_FLOWING)\n        identifier(\"${MOLTEN_NETHERITE_IDENTIFIER.path}_bucket\").item(MOLTEN_NETHERITE_BUCKET)\n\n        MOLTEN_IRON_IDENTIFIER.block(MOLTEN_IRON)\n        identifier(\"${MOLTEN_IRON_IDENTIFIER.path}_still\").fluid(MOLTEN_IRON_STILL)\n        identifier(\"${MOLTEN_IRON_IDENTIFIER.path}_flowing\").fluid(MOLTEN_IRON_FLOWING)\n        identifier(\"${MOLTEN_IRON_IDENTIFIER.path}_bucket\").item(MOLTEN_IRON_BUCKET)\n\n        MOLTEN_GOLD_IDENTIFIER.block(MOLTEN_GOLD)\n        identifier(\"${MOLTEN_GOLD_IDENTIFIER.path}_still\").fluid(MOLTEN_GOLD_STILL)\n        identifier(\"${MOLTEN_GOLD_IDENTIFIER.path}_flowing\").fluid(MOLTEN_GOLD_FLOWING)\n        identifier(\"${MOLTEN_GOLD_IDENTIFIER.path}_bucket\").item(MOLTEN_GOLD_BUCKET)\n\n        MOLTEN_COPPER_IDENTIFIER.block(MOLTEN_COPPER)\n        identifier(\"${MOLTEN_COPPER_IDENTIFIER.path}_still\").fluid(MOLTEN_COPPER_STILL)\n        identifier(\"${MOLTEN_COPPER_IDENTIFIER.path}_flowing\").fluid(MOLTEN_COPPER_FLOWING)\n        identifier(\"${MOLTEN_COPPER_IDENTIFIER.path}_bucket\").item(MOLTEN_COPPER_BUCKET)\n\n        MOLTEN_TIN_IDENTIFIER.block(MOLTEN_TIN)\n        identifier(\"${MOLTEN_TIN_IDENTIFIER.path}_still\").fluid(MOLTEN_TIN_STILL)\n        identifier(\"${MOLTEN_TIN_IDENTIFIER.path}_flowing\").fluid(MOLTEN_TIN_FLOWING)\n        identifier(\"${MOLTEN_TIN_IDENTIFIER.path}_bucket\").item(MOLTEN_TIN_BUCKET)\n\n        MOLTEN_LEAD_IDENTIFIER.block(MOLTEN_LEAD)\n        identifier(\"${MOLTEN_LEAD_IDENTIFIER.path}_still\").fluid(MOLTEN_LEAD_STILL)\n        identifier(\"${MOLTEN_LEAD_IDENTIFIER.path}_flowing\").fluid(MOLTEN_LEAD_FLOWING)\n        identifier(\"${MOLTEN_LEAD_IDENTIFIER.path}_bucket\").item(MOLTEN_LEAD_BUCKET)\n\n        MOLTEN_SILVER_IDENTIFIER.block(MOLTEN_SILVER)\n        identifier(\"${MOLTEN_SILVER_IDENTIFIER.path}_still\").fluid(MOLTEN_SILVER_STILL)\n        identifier(\"${MOLTEN_SILVER_IDENTIFIER.path}_flowing\").fluid(MOLTEN_SILVER_FLOWING)\n        identifier(\"${MOLTEN_SILVER_IDENTIFIER.path}_bucket\").item(MOLTEN_SILVER_BUCKET)\n\n        SULFURIC_ACID_IDENTIFIER.block(SULFURIC_ACID)\n        identifier(\"${SULFURIC_ACID_IDENTIFIER.path}_still\").fluid(SULFURIC_ACID_STILL)\n        identifier(\"${SULFURIC_ACID_IDENTIFIER.path}_flowing\").fluid(SULFURIC_ACID_FLOWING)\n        identifier(\"${SULFURIC_ACID_IDENTIFIER.path}_bucket\").item(SULFURIC_ACID_BUCKET)\n\n        TOXIC_MUD_IDENTIFIER.block(TOXIC_MUD)\n        identifier(\"${TOXIC_MUD_IDENTIFIER.path}_still\").fluid(TOXIC_MUD_STILL)\n        identifier(\"${TOXIC_MUD_IDENTIFIER.path}_flowing\").fluid(TOXIC_MUD_FLOWING)\n        identifier(\"${TOXIC_MUD_IDENTIFIER.path}_bucket\").item(TOXIC_MUD_BUCKET)\n\n        HYDROGEN_IDENTIFIER.block(HYDROGEN)\n        identifier(\"${HYDROGEN_IDENTIFIER.path}_still\").fluid(HYDROGEN_STILL)\n        identifier(\"${HYDROGEN_IDENTIFIER.path}_flowing\").fluid(HYDROGEN_FLOWING)\n        identifier(\"${HYDROGEN_IDENTIFIER.path}_bucket\").item(HYDROGEN_BUCKET)\n        IRFluidFuelRegistry.register(HYDROGEN_STILL, 10, 900, 4, 100 * 81)\n\n        OXYGEN_IDENTIFIER.block(OXYGEN)\n        identifier(\"${OXYGEN_IDENTIFIER.path}_still\").fluid(OXYGEN_STILL)\n        identifier(\"${OXYGEN_IDENTIFIER.path}_flowing\").fluid(OXYGEN_FLOWING)\n        identifier(\"${OXYGEN_IDENTIFIER.path}_bucket\").item(OXYGEN_BUCKET)\n\n        METHANE_IDENTIFIER.block(METHANE)\n        identifier(\"${METHANE_IDENTIFIER.path}_still\").fluid(METHANE_STILL)\n        identifier(\"${METHANE_IDENTIFIER.path}_flowing\").fluid(METHANE_FLOWING)\n        identifier(\"${METHANE_IDENTIFIER.path}_bucket\").item(METHANE_BUCKET)\n        IRFluidFuelRegistry.register(METHANE_STILL, 60, 900, 128, 250 * 81)\n\n\n        STEAM_IDENTIFIER.block(STEAM)\n        identifier(\"${STEAM_IDENTIFIER.path}_still\").fluid(STEAM_STILL)\n        identifier(\"${STEAM_IDENTIFIER.path}_flowing\").fluid(STEAM_FLOWING)\n        identifier(\"${STEAM_IDENTIFIER.path}_bucket\").item(STEAM_BUCKET)\n    }\n\n    val COOLANT_IDENTIFIER = identifier(\"coolant\")\n    val COOLANT_STILL: BaseFluid.Still =\n        BaseFluid.Still(COOLANT_IDENTIFIER, { COOLANT }, { COOLANT_BUCKET }, 0x0C2340) { COOLANT_FLOWING }\n    val COOLANT_FLOWING =\n        BaseFluid.Flowing(COOLANT_IDENTIFIER, { COOLANT }, { COOLANT_BUCKET }, 0x0C2340) { COOLANT_STILL }\n    val COOLANT_BUCKET = BucketItem(COOLANT_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val COOLANT = object : FluidBlock(COOLANT_STILL, FabricBlockSettings.of(Material.LAVA)) {}\n\n    val MOLTEN_NETHERITE_IDENTIFIER = identifier(\"molten_netherite\")\n    val MOLTEN_NETHERITE_STILL: BaseFluid.Still = BaseFluid.Still(MOLTEN_NETHERITE_IDENTIFIER, { MOLTEN_NETHERITE }, { MOLTEN_NETHERITE_BUCKET }, NETHERITE_SCRAP_BASE.toInt()) { MOLTEN_NETHERITE_FLOWING }\n    val MOLTEN_NETHERITE_FLOWING = BaseFluid.Flowing(MOLTEN_NETHERITE_IDENTIFIER, { MOLTEN_NETHERITE }, { MOLTEN_NETHERITE_BUCKET }, NETHERITE_SCRAP_BASE.toInt()) { MOLTEN_NETHERITE_STILL }\n    val MOLTEN_NETHERITE_BUCKET = BucketItem(MOLTEN_NETHERITE_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val MOLTEN_NETHERITE = object : FluidBlock(MOLTEN_NETHERITE_STILL, FabricBlockSettings.of(Material.LAVA)) {}\n\n    val MOLTEN_IRON_IDENTIFIER = identifier(\"molten_iron\")\n    val MOLTEN_IRON_STILL: BaseFluid.Still = BaseFluid.Still(MOLTEN_IRON_IDENTIFIER, { MOLTEN_IRON }, { MOLTEN_IRON_BUCKET }, 0x7A0019) { MOLTEN_IRON_FLOWING }\n    val MOLTEN_IRON_FLOWING = BaseFluid.Flowing(MOLTEN_IRON_IDENTIFIER, { MOLTEN_IRON }, { MOLTEN_IRON_BUCKET }, 0x7A0019) { MOLTEN_IRON_STILL }\n    val MOLTEN_IRON_BUCKET = BucketItem(MOLTEN_IRON_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val MOLTEN_IRON = object : FluidBlock(MOLTEN_IRON_STILL, FabricBlockSettings.of(Material.LAVA)) {}\n\n    val MOLTEN_GOLD_IDENTIFIER = identifier(\"molten_gold\")\n    val MOLTEN_GOLD_STILL: BaseFluid.Still = BaseFluid.Still(MOLTEN_GOLD_IDENTIFIER, { MOLTEN_GOLD }, { MOLTEN_GOLD_BUCKET }, 0xFFCC00) { MOLTEN_GOLD_FLOWING }\n    val MOLTEN_GOLD_FLOWING = BaseFluid.Flowing(MOLTEN_GOLD_IDENTIFIER, { MOLTEN_GOLD }, { MOLTEN_GOLD_BUCKET }, 0xFFCC00) { MOLTEN_GOLD_STILL }\n    val MOLTEN_GOLD_BUCKET = BucketItem(MOLTEN_GOLD_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val MOLTEN_GOLD = object : FluidBlock(MOLTEN_GOLD_STILL, FabricBlockSettings.of(Material.LAVA)) {}\n\n    val MOLTEN_COPPER_IDENTIFIER = identifier(\"molten_copper\")\n    val MOLTEN_COPPER_STILL: BaseFluid.Still = BaseFluid.Still(MOLTEN_COPPER_IDENTIFIER, { MOLTEN_COPPER }, { MOLTEN_COPPER_BUCKET }, COPPER_BASE.toInt()) { MOLTEN_COPPER_FLOWING }\n    val MOLTEN_COPPER_FLOWING = BaseFluid.Flowing(MOLTEN_COPPER_IDENTIFIER, { MOLTEN_COPPER }, { MOLTEN_COPPER_BUCKET }, COPPER_BASE.toInt()) { MOLTEN_COPPER_STILL }\n    val MOLTEN_COPPER_BUCKET = BucketItem(MOLTEN_COPPER_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val MOLTEN_COPPER = object : FluidBlock(MOLTEN_COPPER_STILL, FabricBlockSettings.of(Material.LAVA)) {}\n\n    val MOLTEN_TIN_IDENTIFIER = identifier(\"molten_tin\")\n    val MOLTEN_TIN_STILL: BaseFluid.Still =\n        BaseFluid.Still(MOLTEN_TIN_IDENTIFIER, { MOLTEN_TIN }, { MOLTEN_TIN_BUCKET }, TIN_BASE.toInt()) { MOLTEN_TIN_FLOWING }\n    val MOLTEN_TIN_FLOWING =\n        BaseFluid.Flowing(MOLTEN_TIN_IDENTIFIER, { MOLTEN_TIN }, { MOLTEN_TIN_BUCKET }, TIN_BASE.toInt()) { MOLTEN_TIN_STILL }\n    val MOLTEN_TIN_BUCKET = BucketItem(MOLTEN_TIN_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val MOLTEN_TIN = object : FluidBlock(MOLTEN_TIN_STILL, FabricBlockSettings.of(Material.LAVA)) {}\n\n    val MOLTEN_LEAD_IDENTIFIER = identifier(\"molten_lead\")\n    val MOLTEN_LEAD_STILL: BaseFluid.Still =\n        BaseFluid.Still(MOLTEN_LEAD_IDENTIFIER, { MOLTEN_LEAD }, { MOLTEN_LEAD_BUCKET }, LEAD_BASE.toInt()) { MOLTEN_LEAD_FLOWING }\n    val MOLTEN_LEAD_FLOWING =\n        BaseFluid.Flowing(MOLTEN_LEAD_IDENTIFIER, { MOLTEN_LEAD }, { MOLTEN_LEAD_BUCKET }, LEAD_BASE.toInt()) { MOLTEN_LEAD_STILL }\n    val MOLTEN_LEAD_BUCKET = BucketItem(MOLTEN_LEAD_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val MOLTEN_LEAD = object : FluidBlock(MOLTEN_LEAD_STILL, FabricBlockSettings.of(Material.LAVA)) {}\n\n    val MOLTEN_SILVER_IDENTIFIER = identifier(\"molten_silver\")\n    val MOLTEN_SILVER_STILL: BaseFluid.Still =\n        BaseFluid.Still(MOLTEN_SILVER_IDENTIFIER, { MOLTEN_SILVER }, { MOLTEN_SILVER_BUCKET }, SILVER_BASE.toInt()) { MOLTEN_SILVER_FLOWING }\n    val MOLTEN_SILVER_FLOWING =\n        BaseFluid.Flowing(MOLTEN_SILVER_IDENTIFIER, { MOLTEN_SILVER }, { MOLTEN_SILVER_BUCKET }, SILVER_BASE.toInt()) { MOLTEN_SILVER_STILL }\n    val MOLTEN_SILVER_BUCKET = BucketItem(MOLTEN_SILVER_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val MOLTEN_SILVER = object : FluidBlock(MOLTEN_SILVER_STILL, FabricBlockSettings.of(Material.LAVA)) {}\n\n    val ACID_MATERIAL: Material =\n        FabricMaterialBuilder(MapColor.GREEN).allowsMovement().lightPassesThrough().notSolid().replaceable()\n            .liquid().build()\n    val MUD_MATERIAL: Material =\n        FabricMaterialBuilder(MapColor.BROWN).allowsMovement().lightPassesThrough().notSolid().replaceable()\n            .liquid().build()\n\n    val SULFURIC_ACID_IDENTIFIER = identifier(\"sulfuric_acid\")\n    val SULFURIC_ACID_STILL: BaseFluid.Still = BaseFluid.Still(SULFURIC_ACID_IDENTIFIER, { SULFURIC_ACID }, { SULFURIC_ACID_BUCKET }, 0x003D1E) { SULFURIC_ACID_FLOWING }\n    val SULFURIC_ACID_FLOWING = BaseFluid.Flowing(SULFURIC_ACID_IDENTIFIER, { SULFURIC_ACID }, { SULFURIC_ACID_BUCKET }, 0x003D1E) { SULFURIC_ACID_STILL }\n    val SULFURIC_ACID_BUCKET = BucketItem(SULFURIC_ACID_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val SULFURIC_ACID = AcidFluidBlock(SULFURIC_ACID_STILL, FabricBlockSettings.of(ACID_MATERIAL).ticksRandomly())\n\n    val TOXIC_MUD_IDENTIFIER = identifier(\"toxic_mud\")\n    val TOXIC_MUD_STILL: BaseFluid.Still =\n        BaseFluid.Still(TOXIC_MUD_IDENTIFIER, { TOXIC_MUD }, { TOXIC_MUD_BUCKET }, 0x5c3b0e) { TOXIC_MUD_FLOWING }\n    val TOXIC_MUD_FLOWING =\n        BaseFluid.Flowing(TOXIC_MUD_IDENTIFIER, { TOXIC_MUD }, { TOXIC_MUD_BUCKET }, 0x5c3b0e) { TOXIC_MUD_STILL }\n    val TOXIC_MUD_BUCKET = BucketItem(TOXIC_MUD_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val TOXIC_MUD = object : FluidBlock(TOXIC_MUD_STILL, FabricBlockSettings.of(MUD_MATERIAL)) {}\n\n    val HYDROGEN_IDENTIFIER = identifier(\"hydrogen\")\n    val HYDROGEN_STILL: BaseFluid.Still =\n        BaseFluid.Still(HYDROGEN_IDENTIFIER, { HYDROGEN }, { HYDROGEN_BUCKET }, -1) { HYDROGEN_FLOWING }\n    val HYDROGEN_FLOWING =\n        BaseFluid.Flowing(HYDROGEN_IDENTIFIER, { HYDROGEN }, { HYDROGEN_BUCKET }, -1) { HYDROGEN_STILL }\n    val HYDROGEN_BUCKET = BucketItem(HYDROGEN_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val HYDROGEN = object : FluidBlock(HYDROGEN_STILL, FabricBlockSettings.of(Material.LAVA)) {}\n\n    val OXYGEN_IDENTIFIER = identifier(\"oxygen\")\n    val OXYGEN_STILL: BaseFluid.Still =\n        BaseFluid.Still(OXYGEN_IDENTIFIER, { OXYGEN }, { OXYGEN_BUCKET }, 0xfcfccf) { OXYGEN_FLOWING }\n    val OXYGEN_FLOWING =\n        BaseFluid.Flowing(OXYGEN_IDENTIFIER, { OXYGEN }, { OXYGEN_BUCKET }, 0xfcfccf) { OXYGEN_STILL }\n    val OXYGEN_BUCKET = BucketItem(OXYGEN_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val OXYGEN = object : FluidBlock(OXYGEN_STILL, FabricBlockSettings.of(Material.LAVA)) {}\n\n    val METHANE_IDENTIFIER = identifier(\"methane\")\n    val METHANE_STILL: BaseFluid.Still =\n        BaseFluid.Still(METHANE_IDENTIFIER, { METHANE }, { METHANE_BUCKET }, 0xe8ffbf) { METHANE_FLOWING }\n    val METHANE_FLOWING =\n        BaseFluid.Flowing(METHANE_IDENTIFIER, { METHANE }, { METHANE_BUCKET }, 0xe8ffbf) { METHANE_STILL }\n    val METHANE_BUCKET = BucketItem(METHANE_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val METHANE = object : FluidBlock(METHANE_STILL, FabricBlockSettings.of(Material.LAVA)) {}\n\n    val STEAM_IDENTIFIER = identifier(\"steam\")\n    val STEAM_STILL: BaseFluid.Still = BaseFluid.Still(STEAM_IDENTIFIER, { STEAM }, { STEAM_BUCKET }, -1) { STEAM_FLOWING }\n    val STEAM_FLOWING = BaseFluid.Flowing(STEAM_IDENTIFIER, { STEAM }, { STEAM_BUCKET }, -1) { STEAM_STILL }\n    val STEAM_BUCKET = BucketItem(STEAM_STILL, itemSettings().recipeRemainder(Items.BUCKET).maxCount(1))\n    val STEAM = object : FluidBlock(STEAM_STILL, FabricBlockSettings.of(Material.WATER)) {}\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/registry/IRItemRegistry.kt",
    "content": "package me.steven.indrev.registry\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.armor.IRArmorMaterial\nimport me.steven.indrev.blocks.misc.NikoliteOreBlock\nimport me.steven.indrev.items.armor.*\nimport me.steven.indrev.items.energy.*\nimport me.steven.indrev.items.misc.*\nimport me.steven.indrev.items.upgrade.Enhancer\nimport me.steven.indrev.items.upgrade.IREnhancerItem\nimport me.steven.indrev.networks.EndpointData\nimport me.steven.indrev.tools.IRToolMaterial\nimport me.steven.indrev.tools.modular.ArmorModule\nimport me.steven.indrev.tools.modular.DrillModule\nimport me.steven.indrev.tools.modular.GamerAxeModule\nimport me.steven.indrev.tools.modular.MiningToolModule\nimport me.steven.indrev.utils.*\nimport net.minecraft.client.item.TooltipContext\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.item.*\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.Rarity\nimport net.minecraft.util.registry.Registry\nimport net.minecraft.world.World\nimport team.reborn.energy.api.EnergyStorage\nimport team.reborn.energy.impl.SimpleItemEnergyStorageImpl\n\n@Suppress(\"MemberVisibilityCanBePrivate\")\nobject IRItemRegistry {\n    fun registerAll() {\n\n        identifier(\"guide_book\").item(GUIDE_BOOK)\n\n        MaterialHelper(\"tin\") {\n            withItems(\"dust\", \"ingot\", \"plate\", \"nugget\", \"chunk\", \"purified_ore\")\n            withBlock()\n            withOre()\n            withTools(\n                IRBasicPickaxe(IRToolMaterial.TIN, 1, -1.0f, itemSettings()),\n                IRBasicAxe(IRToolMaterial.TIN, 4.5f, -2.0f, itemSettings()),\n                IRBasicShovel(IRToolMaterial.TIN, 1.5f, -0.7f, itemSettings()),\n                IRBasicSword(IRToolMaterial.TIN, 3, -1.5f, itemSettings()),\n                IRBasicHoe(IRToolMaterial.TIN, 2, -0.5f, itemSettings())\n            )\n            withArmor(IRArmorMaterial.TIN)\n        }.register()\n\n        MaterialHelper(\"copper\") {\n            withItems(\"dust\", \"plate\", \"nugget\", \"chunk\", \"purified_ore\")\n            withTools(\n                IRBasicPickaxe(IRToolMaterial.COPPER, 1, -1.0f, itemSettings()),\n                IRBasicAxe(IRToolMaterial.COPPER, 4.5f, -2.0f, itemSettings()),\n                IRBasicShovel(IRToolMaterial.COPPER, 1.5f, -0.7f, itemSettings()),\n                IRBasicSword(IRToolMaterial.COPPER, 3, -1.5f, itemSettings()),\n                IRBasicHoe(IRToolMaterial.COPPER, 2, -0.5f, itemSettings())\n            )\n            withArmor(IRArmorMaterial.COPPER)\n        }.register()\n\n        MaterialHelper(\"steel\") {\n            withItems(\"dust\", \"ingot\", \"plate\", \"nugget\")\n            withBlock()\n            withTools(\n                IRBasicPickaxe(IRToolMaterial.STEEL, 2, -1.8f, itemSettings()),\n                IRBasicAxe(IRToolMaterial.STEEL, 7f, -3.1f, itemSettings()),\n                IRBasicShovel(IRToolMaterial.STEEL, 1.0f, -1.0f, itemSettings()),\n                IRBasicSword(IRToolMaterial.STEEL, 5, -2.0f, itemSettings()),\n                IRBasicHoe(IRToolMaterial.STEEL, 3, -1.5f, itemSettings())\n            )\n            withArmor(IRArmorMaterial.STEEL)\n        }.register()\n\n        MaterialHelper(\"iron\") { withItems(\"dust\", \"plate\", \"chunk\", \"purified_ore\") }.register()\n\n        MaterialHelper(\"netherite_scrap\") { withItems(\"dust\", \"chunk\", \"purified_ore\") }.register()\n\n        MaterialHelper(\"nikolite\") {\n            withItems(\"dust\", \"ingot\")\n            withOre(false) { settings -> NikoliteOreBlock(settings) }\n        }.register()\n\n        MaterialHelper(\"enriched_nikolite\") { withItems(\"dust\", \"ingot\") }.register()\n\n        MaterialHelper(\"diamond\") { withItems(\"dust\") }.register()\n\n        MaterialHelper(\"gold\") { withItems(\"dust\", \"plate\", \"chunk\", \"purified_ore\") }.register()\n\n        MaterialHelper(\"coal\") { withItems(\"dust\") }.register()\n\n        MaterialHelper(\"sulfur\") {\n            withItems(\"dust\")\n        }.register()\n\n        MaterialHelper(\"lead\") {\n            withItems(\"dust\", \"ingot\", \"plate\", \"nugget\", \"chunk\", \"purified_ore\")\n            withBlock()\n            withOre()\n            withTools(\n                IRBasicPickaxe(IRToolMaterial.LEAD, 1, -1.0f, itemSettings()),\n                IRBasicAxe(IRToolMaterial.LEAD, 3.8f, -2.9f, itemSettings()),\n                IRBasicShovel(IRToolMaterial.LEAD, 1.5f, -0.7f, itemSettings()),\n                IRBasicSword(IRToolMaterial.LEAD, 3, -1.5f, itemSettings()),\n                IRBasicHoe(IRToolMaterial.LEAD, 2, -0.5f, itemSettings())\n            )\n            withArmor(IRArmorMaterial.LEAD)\n        }.register()\n\n        MaterialHelper(\"bronze\") {\n            withItems(\"dust\", \"ingot\", \"plate\", \"nugget\")\n            withBlock()\n            withTools(\n                IRBasicPickaxe(IRToolMaterial.BRONZE, 1, -1.0f, itemSettings()),\n                IRBasicAxe(IRToolMaterial.BRONZE, 5f, -2.0f, itemSettings()),\n                IRBasicShovel(IRToolMaterial.BRONZE, 1.5f, -0.7f, itemSettings()),\n                IRBasicSword(IRToolMaterial.BRONZE, 3, -1.5f, itemSettings()),\n                IRBasicHoe(IRToolMaterial.BRONZE, 2, -0.5f, itemSettings())\n            )\n            withArmor(IRArmorMaterial.BRONZE)\n        }.register()\n\n        MaterialHelper(\"silver\") {\n            withItems(\"dust\", \"ingot\", \"plate\", \"nugget\", \"chunk\", \"purified_ore\")\n            withBlock()\n            withOre()\n            withTools(\n                IRBasicPickaxe(IRToolMaterial.SILVER, 1, -1.0f, itemSettings()),\n                IRBasicAxe(IRToolMaterial.SILVER, 3.5f, -2.5f, itemSettings()),\n                IRBasicShovel(IRToolMaterial.SILVER, 1.5f, -0.7f, itemSettings()),\n                IRBasicSword(IRToolMaterial.SILVER, 3, -1.5f, itemSettings()),\n                IRBasicHoe(IRToolMaterial.SILVER, 2, -0.5f, itemSettings())\n            )\n            withArmor(IRArmorMaterial.SILVER)\n        }.register()\n\n        MaterialHelper(\"tungsten\") {\n            withItems(\"dust\", \"ingot\", \"plate\", \"nugget\", \"purified_ore\")\n            withBlock()\n            withOre()\n        }.register()\n\n        MaterialHelper(\"electrum\") {\n            withItems(\"dust\", \"ingot\", \"plate\", \"nugget\")\n            withBlock()\n        }.register()\n\n        MaterialHelper.register()\n\n        identifier(\"soot\").item(SOOT)\n        identifier(\"carbon_fiber_plate\").item(DEFAULT_ITEM())\n        identifier(\"carbon_fiber_rod\").item(DEFAULT_ITEM())\n\n        identifier(\"sawdust\").item(DEFAULT_ITEM())\n\n        identifier(\"hammer\").item(HAMMER)\n\n        identifier(\"stone_drill_head\").item(STONE_DRILL_HEAD)\n        identifier(\"iron_drill_head\").item(IRON_DRILL_HEAD)\n        identifier(\"diamond_drill_head\").item(DIAMOND_DRILL_HEAD)\n        identifier(\"netherite_drill_head\").item(NETHERITE_DRILL_HEAD)\n\n        identifier(\"mining_drill_mk1\").item(MINING_DRILL_MK1)\n        identifier(\"mining_drill_mk2\").item(MINING_DRILL_MK2)\n        identifier(\"mining_drill_mk3\").item(MINING_DRILL_MK3)\n        identifier(\"mining_drill_mk4\").item(MINING_DRILL_MK4)\n\n        EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, 4000, Tier.MK1.io, Tier.MK1.io) }, MINING_DRILL_MK1)\n        EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, 8000, Tier.MK2.io, Tier.MK2.io) }, MINING_DRILL_MK2)\n        EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, 16000, Tier.MK3.io, Tier.MK3.io) }, MINING_DRILL_MK3)\n        EnergyStorage.ITEM.registerForItems({ _, ctx -> SimpleItemEnergyStorageImpl.createSimpleStorage(ctx, 32000, Tier.MK4.io, Tier.MK4.io) }, MINING_DRILL_MK4)\n\n        identifier(\"battery\").item(BATTERY)\n\n        identifier(\"circuit_mk1\").item(DEFAULT_ITEM())\n        identifier(\"circuit_mk2\").item(DEFAULT_ITEM())\n        identifier(\"circuit_mk3\").item(DEFAULT_ITEM())\n        identifier(\"circuit_mk4\").item(DEFAULT_ITEM())\n\n        identifier(\"fan\").item(FAN)\n        identifier(\"cooler_cell\").item(COOLER_CELL)\n        identifier(\"heatsink\").item(HEATSINK)\n        identifier(\"heat_coil\").item(HEAT_COIL)\n\n        identifier(\"ore_data_card\").item(ORE_DATA_CARD)\n\n        identifier(\"empty_enhancer\").item(DEFAULT_ITEM())\n        identifier(\"buffer_enhancer\").item(BUFFER_ENHANCER)\n        identifier(\"speed_enhancer\").item(SPEED_UPGRADE)\n        identifier(\"blast_furnace_enhancer\").item(BLAST_FURNACE_UPGRADE)\n        identifier(\"smoker_enhancer\").item(SMOKER_UPGRADE)\n        identifier(\"damage_enhancer\").item(DAMAGE_UPGRADE)\n\n        identifier(\"energy_reader\").item(ENERGY_READER)\n\n        identifier(\"tier_upgrade_mk2\").item(TIER_UPGRADE_MK2)\n        identifier(\"tier_upgrade_mk3\").item(TIER_UPGRADE_MK3)\n        identifier(\"tier_upgrade_mk4\").item(TIER_UPGRADE_MK4)\n\n        identifier(\"biomass\").item(BIOMASS)\n        identifier(\"untanned_leather\").item(DEFAULT_ITEM())\n\n        identifier(\"wrench\").item(WRENCH)\n        identifier(\"screwdriver\").item(SCREWDRIVER)\n\n        identifier(\"jetpack_mk1\").item(JETPACK_MK1)\n        identifier(\"jetpack_mk2\").item(JETPACK_MK2)\n        identifier(\"jetpack_mk3\").item(JETPACK_MK3)\n        identifier(\"jetpack_mk4\").item(JETPACK_MK4)\n\n        identifier(\"carbon_fiber_helmet_frame\").item(DEFAULT_ITEM())\n        identifier(\"carbon_fiber_chest_frame\").item(DEFAULT_ITEM())\n        identifier(\"carbon_fiber_legs_frame\").item(DEFAULT_ITEM())\n        identifier(\"carbon_fiber_boots_frame\").item(DEFAULT_ITEM())\n\n        identifier(\"modular_armor_helmet\").item(MODULAR_ARMOR_HELMET)\n        identifier(\"modular_armor_chest\").item(MODULAR_ARMOR_CHEST)\n        identifier(\"modular_armor_legs\").item(MODULAR_ARMOR_LEGGINGS)\n        identifier(\"modular_armor_boots\").item(MODULAR_ARMOR_BOOTS)\n\n        identifier(\"module_protection\").item(PROTECTION_MODULE_ITEM)\n        identifier(\"module_speed\").item(SPEED_MODULE_ITEM)\n        identifier(\"module_jump_boost\").item(JUMP_BOOST_MODULE_ITEM)\n        identifier(\"module_night_vision\").item(NIGHT_VISION_MODULE_ITEM)\n        identifier(\"module_breathing\").item(BREATHING_MODULE_ITEM)\n        identifier(\"module_feather_falling\").item(FEATHER_FALLING_MODULE_ITEM)\n        identifier(\"module_auto_feeder\").item(AUTO_FEEDER_MODULE_ITEM)\n        identifier(\"module_charger\").item(CHARGER_MODULE_ITEM)\n        identifier(\"module_solar_panel\").item(SOLAR_PANEL_MODULE_ITEM)\n        identifier(\"module_piglin_tricker\").item(PIGLIN_TRICKER_MODULE_ITEM)\n        identifier(\"module_elytra\").item(ELYTRA_MODULE_ITEM)\n        identifier(\"module_jetpack\").item(JETPACK_MODULE_ITEM)\n        identifier(\"module_magnet\").item(MAGNET_MODULE)\n        identifier(\"module_water_affinity\").item(WATER_AFFINITY_MODULE)\n        identifier(\"module_fire_resistance\").item(FIRE_RESISTANCE_MODULE_ITEM)\n        identifier(\"module_range\").item(RANGE_MODULE_ITEM)\n        identifier(\"module_efficiency\").item(EFFICIENCY_MODULE_ITEM)\n        identifier(\"module_fortune\").item(FORTUNE_MODULE_ITEM)\n        identifier(\"module_silk_touch\").item(SILK_TOUCH_MODULE_ITEM)\n        identifier(\"module_controlled_destruction\").item(CONTROLLED_DESTRUCTION_MODULE_ITEM)\n        identifier(\"module_matter_projector\").item(MATTER_PROJECTOR_MODULE_ITEM)\n        identifier(\"module_looting\").item(LOOTING_MODULE_ITEM)\n        identifier(\"module_fire_aspect\").item(FIRE_ASPECT_MODULE_ITEM)\n        identifier(\"module_sharpness\").item(SHARPNESS_MODULE_ITEM)\n        //identifier(\"module_reach\").item(REACH_MODULE_ITEM)\n\n        identifier(\"module_color_pink\").item(PINK_MODULE_ITEM)\n        identifier(\"module_color_red\").item(RED_MODULE_ITEM)\n        identifier(\"module_color_purple\").item(PURPLE_MODULE_ITEM)\n        identifier(\"module_color_blue\").item(BLUE_MODULE_ITEM)\n        identifier(\"module_color_cyan\").item(CYAN_MODULE_ITEM)\n        identifier(\"module_color_green\").item(GREEN_MODULE_ITEM)\n        identifier(\"module_color_yellow\").item(YELLOW_MODULE_ITEM)\n        identifier(\"module_color_orange\").item(ORANGE_MODULE_ITEM)\n        identifier(\"module_color_black\").item(BLACK_MODULE_ITEM)\n        identifier(\"module_color_brown\").item(BROWN_MODULE_ITEM)\n\n        identifier(\"portable_charger\").item(PORTABLE_CHARGER_ITEM)\n\n        identifier(\"gamer_axe\").item(GAMER_AXE_ITEM)\n\n        identifier(\"modular_core\").item(MODULAR_CORE)\n        identifier(\"modular_core_activated\").item(MODULAR_CORE_ACTIVATED)\n\n        identifier(\"fluid_pipe_mk1\").item(FLUID_PIPE_ITEM_MK1)\n        identifier(\"fluid_pipe_mk2\").item(FLUID_PIPE_ITEM_MK2)\n        identifier(\"fluid_pipe_mk3\").item(FLUID_PIPE_ITEM_MK3)\n        identifier(\"fluid_pipe_mk4\").item(FLUID_PIPE_ITEM_MK4)\n\n        identifier(\"item_pipe_mk1\").item(ITEM_PIPE_ITEM_MK1)\n        identifier(\"item_pipe_mk2\").item(ITEM_PIPE_ITEM_MK2)\n        identifier(\"item_pipe_mk3\").item(ITEM_PIPE_ITEM_MK3)\n        identifier(\"item_pipe_mk4\").item(ITEM_PIPE_ITEM_MK4)\n\n        identifier(\"cable_mk1\").item(CABLE_ITEM_MK1)\n        identifier(\"cable_mk2\").item(CABLE_ITEM_MK2)\n        identifier(\"cable_mk3\").item(CABLE_ITEM_MK3)\n        identifier(\"cable_mk4\").item(CABLE_ITEM_MK4)\n\n        identifier(\"servo_retriever\").item(SERVO_RETRIEVER)\n        identifier(\"servo_output\").item(SERVO_OUTPUT)\n\n        identifier(\"reinforced_elytra\").item(REINFORCED_ELYTRA)\n    }\n\n    private val DEFAULT_ITEM: () -> Item = { Item(itemSettings()) }\n\n    val GUIDE_BOOK = IRGuideBookItem(itemSettings())\n\n    val BATTERY = IRBatteryItem(itemSettings(), 4096)\n\n    val HAMMER = IRCraftingToolItem(itemSettings().maxDamage(32))\n\n    val STEEL_INGOT = { Registry.ITEM.get(identifier(\"steel_ingot\")) }\n    val STEEL_PLATE = { Registry.ITEM.get(identifier(\"steel_plate\")) }\n    val COPPER_INGOT = { Registry.ITEM.get(identifier(\"copper_ingot\")) }\n    val TIN_INGOT = { Registry.ITEM.get(identifier(\"tin_ingot\")) }\n    val LEAD_INGOT = { Registry.ITEM.get(identifier(\"lead_ingot\")) }\n    val BRONZE_INGOT = { Registry.ITEM.get(identifier(\"bronze_ingot\")) }\n    val SILVER_INGOT = { Registry.ITEM.get(identifier(\"silver_ingot\")) }\n    val ENRICHED_NIKOLITE_DUST = { Registry.ITEM.get(identifier(\"enriched_nikolite_dust\")) }\n\n    val BIOMASS = DEFAULT_ITEM()\n\n    val FAN = Item(itemSettings().maxDamage(128))\n    val COOLER_CELL = Item(itemSettings().maxDamage(512))\n    val HEATSINK = Item(itemSettings().maxDamage(1536))\n    val HEAT_COIL = object : Item(itemSettings().maxDamage(128)) {\n        override fun appendTooltip(\n            stack: ItemStack?,\n            world: World?,\n            tooltip: MutableList<Text>?,\n            context: TooltipContext?\n        ) {\n            tooltip?.add(TranslatableText(\"item.indrev.heat_coil.tooltip\").formatted(Formatting.BLUE))\n        }\n    }\n\n    val TIER_UPGRADE_MK2 = IRMachineUpgradeItem(itemSettings(), Tier.MK1, Tier.MK2)\n    val TIER_UPGRADE_MK3 = IRMachineUpgradeItem(itemSettings(), Tier.MK2, Tier.MK3)\n    val TIER_UPGRADE_MK4 = IRMachineUpgradeItem(itemSettings(), Tier.MK3, Tier.MK4)\n\n    val MINING_DRILL_MK1 =\n        IRMiningDrillItem(ToolMaterials.STONE, Tier.MK1, 4000.0, 6f, itemSettings())\n    val MINING_DRILL_MK2 =\n        IRMiningDrillItem(ToolMaterials.IRON, Tier.MK2, 8000.0, 10f, itemSettings())\n    val MINING_DRILL_MK3 =\n        IRMiningDrillItem(ToolMaterials.DIAMOND, Tier.MK3, 16000.0, 14f, itemSettings())\n    val MINING_DRILL_MK4 = IRModularDrillItem(\n        ToolMaterials.NETHERITE, Tier.MK4, 32000.0, 16f, itemSettings().fireproof()\n    )\n\n    val ORE_DATA_CARD = OreDataCardItem()\n\n    val ENERGY_READER = IREnergyReaderItem(itemSettings())\n\n    val SOOT = DEFAULT_ITEM()\n\n    val SULFUR_CRYSTAL_ITEM = DEFAULT_ITEM()\n\n    val STONE_DRILL_HEAD = Item(itemSettings().maxDamage(256))\n    val IRON_DRILL_HEAD = Item(itemSettings().maxDamage(1024))\n    val DIAMOND_DRILL_HEAD = Item(itemSettings().maxDamage(2048))\n    val NETHERITE_DRILL_HEAD = Item(itemSettings().maxDamage(4096))\n\n    val BUFFER_ENHANCER = IREnhancerItem(itemSettings().maxCount(32), Enhancer.BUFFER)\n    val SPEED_UPGRADE = IREnhancerItem(itemSettings().maxCount(32), Enhancer.SPEED)\n    val BLAST_FURNACE_UPGRADE = IREnhancerItem(itemSettings().maxCount(1), Enhancer.BLAST_FURNACE)\n    val SMOKER_UPGRADE = IREnhancerItem(itemSettings().maxCount(1), Enhancer.SMOKER)\n    val DAMAGE_UPGRADE = IREnhancerItem(itemSettings().maxCount(1), Enhancer.DAMAGE)\n\n    val WRENCH = object : Item(itemSettings().maxCount(1)) {\n        override fun useOnBlock(context: ItemUsageContext): ActionResult {\n            val state = context.world.getBlockState(context.blockPos)\n            val blockEntity = context.world.getBlockEntity(context.blockPos)\n            return wrench(context.world, context.blockPos, state, blockEntity, context.player, context.stack)\n        }\n    }\n    val SCREWDRIVER = object : Item(itemSettings().maxCount(1)) {\n        override fun useOnBlock(context: ItemUsageContext): ActionResult {\n            val blockEntity = context.world.getBlockEntity(context.blockPos)\n            val state = context.world.getBlockState(context.blockPos)\n            return screwdriver(context.world, context.blockPos, state, blockEntity, context.player, context.stack)\n        }\n    }\n\n    val JETPACK_MK1 = JetpackItem(Tier.MK1)\n    val JETPACK_MK2 = JetpackItem(Tier.MK2)\n    val JETPACK_MK3 = JetpackItem(Tier.MK3)\n    val JETPACK_MK4 = JetpackItem(Tier.MK4)\n\n    val MODULAR_ARMOR_HELMET = IRModularArmorItem(EquipmentSlot.HEAD, 250000, itemSettings().rarity(Rarity.EPIC).customDamage(EnergyDamageHandler))\n    val MODULAR_ARMOR_CHEST = IRModularArmorItem(EquipmentSlot.CHEST, 250000, itemSettings().rarity(Rarity.EPIC).customDamage(EnergyDamageHandler))\n    val MODULAR_ARMOR_LEGGINGS = IRModularArmorItem(EquipmentSlot.LEGS, 250000, itemSettings().rarity(Rarity.EPIC).customDamage(EnergyDamageHandler))\n    val MODULAR_ARMOR_BOOTS = IRModularArmorItem(EquipmentSlot.FEET, 250000, itemSettings().rarity(Rarity.EPIC).customDamage(EnergyDamageHandler))\n\n    val PROTECTION_MODULE_ITEM = IRModuleItem(ArmorModule.PROTECTION, itemSettings().maxCount(1))\n    val SPEED_MODULE_ITEM = IRModuleItem(ArmorModule.SPEED, itemSettings().maxCount(1))\n    val JUMP_BOOST_MODULE_ITEM = IRModuleItem(ArmorModule.JUMP_BOOST, itemSettings().maxCount(1))\n    val BREATHING_MODULE_ITEM = IRModuleItem(ArmorModule.BREATHING, itemSettings().maxCount(1))\n    val NIGHT_VISION_MODULE_ITEM = IRModuleItem(ArmorModule.NIGHT_VISION, itemSettings().maxCount(1))\n    val FEATHER_FALLING_MODULE_ITEM = IRModuleItem(ArmorModule.FEATHER_FALLING, itemSettings().maxCount(1))\n    val AUTO_FEEDER_MODULE_ITEM = IRModuleItem(ArmorModule.AUTO_FEEDER, itemSettings().maxCount(1))\n    val CHARGER_MODULE_ITEM = IRModuleItem(ArmorModule.CHARGER, itemSettings().maxCount(1))\n    val SOLAR_PANEL_MODULE_ITEM = IRModuleItem(ArmorModule.SOLAR_PANEL, itemSettings().maxCount(1))\n    val PIGLIN_TRICKER_MODULE_ITEM = IRModuleItem(ArmorModule.PIGLIN_TRICKER, itemSettings().maxCount(1))\n    val ELYTRA_MODULE_ITEM = IRModuleItem(ArmorModule.ELYTRA, itemSettings().maxCount(1))\n    val JETPACK_MODULE_ITEM = IRModuleItem(ArmorModule.JETPACK, itemSettings().maxCount(1))\n    val MAGNET_MODULE = IRModuleItem(ArmorModule.MAGNET, itemSettings().maxCount(1))\n    val WATER_AFFINITY_MODULE = IRModuleItem(ArmorModule.WATER_AFFINITY, itemSettings().maxCount(1))\n    val FIRE_RESISTANCE_MODULE_ITEM = IRModuleItem(ArmorModule.FIRE_RESISTANCE, itemSettings().maxCount(1))\n    val SILK_TOUCH_MODULE_ITEM = IRModuleItem(DrillModule.SILK_TOUCH, itemSettings().maxCount(1))\n    val CONTROLLED_DESTRUCTION_MODULE_ITEM = IRModuleItem(DrillModule.CONTROLLED_DESTRUCTION, itemSettings().maxCount(1))\n    val MATTER_PROJECTOR_MODULE_ITEM = IRModuleItem(DrillModule.MATTER_PROJECTOR, itemSettings().maxCount(1))\n    val FORTUNE_MODULE_ITEM = IRModuleItem(DrillModule.FORTUNE, itemSettings().maxCount(1))\n    val RANGE_MODULE_ITEM = IRModuleItem(DrillModule.RANGE, itemSettings().maxCount(1))\n //   val REACH_MODULE_ITEM = IRModuleItem(GamerAxeModule.REACH, itemSettings().maxCount(1))\n    val EFFICIENCY_MODULE_ITEM = IRModuleItem(MiningToolModule.EFFICIENCY, itemSettings().maxCount(1))\n    val LOOTING_MODULE_ITEM = IRModuleItem(GamerAxeModule.LOOTING, itemSettings().maxCount(1))\n    val FIRE_ASPECT_MODULE_ITEM = IRModuleItem(GamerAxeModule.FIRE_ASPECT, itemSettings().maxCount(1))\n    val SHARPNESS_MODULE_ITEM = IRModuleItem(GamerAxeModule.SHARPNESS, itemSettings().maxCount(1))\n\n    val PINK_MODULE_ITEM = IRColorModuleItem(0xFF74DD, itemSettings().maxCount(1))\n    val RED_MODULE_ITEM = IRColorModuleItem(0xFF747C, itemSettings().maxCount(1))\n    val PURPLE_MODULE_ITEM = IRColorModuleItem(0xD174FF, itemSettings().maxCount(1))\n    val BLUE_MODULE_ITEM = IRColorModuleItem(0x7974FF, itemSettings().maxCount(1))\n    val CYAN_MODULE_ITEM = IRColorModuleItem(0x74FFFC, itemSettings().maxCount(1))\n    val GREEN_MODULE_ITEM = IRColorModuleItem(0x7BFF74, itemSettings().maxCount(1))\n    val YELLOW_MODULE_ITEM = IRColorModuleItem(0xF7FF74, itemSettings().maxCount(1))\n    val ORANGE_MODULE_ITEM = IRColorModuleItem(0xFFA674, itemSettings().maxCount(1))\n    val BLACK_MODULE_ITEM = IRColorModuleItem(0x424242, itemSettings().maxCount(1))\n    val BROWN_MODULE_ITEM = IRColorModuleItem(0x935F42, itemSettings().maxCount(1))\n\n    val PORTABLE_CHARGER_ITEM = IRPortableChargerItem(itemSettings(), 250000)\n\n    val GAMER_AXE_ITEM =\n        IRGamerAxeItem(ToolMaterials.NETHERITE, 10000, Tier.MK4, 4f, -2f, itemSettings().rarity(Rarity.EPIC).customDamage(EnergyDamageHandler))\n\n    val TANK_BLOCK_ITEM = BlockItem(IRBlockRegistry.TANK_BLOCK, itemSettings())\n\n    val CAPSULE_BLOCK_ITEM = BlockItem(IRBlockRegistry.CAPSULE_BLOCK, itemSettings().maxCount(1))\n\n    val MODULAR_CORE: Item = object : Item(itemSettings().maxCount(1)), IREnergyItem {\n        override fun appendTooltip(\n            stack: ItemStack?,\n            world: World?,\n            tooltip: MutableList<Text>?,\n            context: TooltipContext?\n        ) {\n            buildEnergyTooltip(stack, tooltip)\n        }\n    }\n    val MODULAR_CORE_ACTIVATED = object : Item(itemSettings().maxCount(1)) {\n        override fun hasGlint(stack: ItemStack?): Boolean = true\n    }\n\n    val FLUID_PIPE_ITEM_MK1 = BlockItem(IRBlockRegistry.FLUID_PIPE_MK1, itemSettings())\n    val FLUID_PIPE_ITEM_MK2 = BlockItem(IRBlockRegistry.FLUID_PIPE_MK2, itemSettings())\n    val FLUID_PIPE_ITEM_MK3 = BlockItem(IRBlockRegistry.FLUID_PIPE_MK3, itemSettings())\n    val FLUID_PIPE_ITEM_MK4 = BlockItem(IRBlockRegistry.FLUID_PIPE_MK4, itemSettings())\n\n    val ITEM_PIPE_ITEM_MK1 = BlockItem(IRBlockRegistry.ITEM_PIPE_MK1, itemSettings())\n    val ITEM_PIPE_ITEM_MK2 = BlockItem(IRBlockRegistry.ITEM_PIPE_MK2, itemSettings())\n    val ITEM_PIPE_ITEM_MK3 = BlockItem(IRBlockRegistry.ITEM_PIPE_MK3, itemSettings())\n    val ITEM_PIPE_ITEM_MK4 = BlockItem(IRBlockRegistry.ITEM_PIPE_MK4, itemSettings())\n\n    val CABLE_ITEM_MK1 = BlockItem(IRBlockRegistry.CABLE_MK1, itemSettings())\n    val CABLE_ITEM_MK2 = BlockItem(IRBlockRegistry.CABLE_MK2, itemSettings())\n    val CABLE_ITEM_MK3 = BlockItem(IRBlockRegistry.CABLE_MK3, itemSettings())\n    val CABLE_ITEM_MK4 = BlockItem(IRBlockRegistry.CABLE_MK4, itemSettings())\n\n    val SERVO_RETRIEVER = IRServoItem(itemSettings().maxCount(16), EndpointData.Type.RETRIEVER)\n    val SERVO_OUTPUT = IRServoItem(itemSettings().maxCount(16), EndpointData.Type.OUTPUT)\n\n    val REINFORCED_ELYTRA = ReinforcedElytraItem()\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/registry/IRModelManagers.kt",
    "content": "package me.steven.indrev.registry\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blocks.machine.DrillHeadModel\nimport me.steven.indrev.blocks.models.PumpPipeBakedModel\nimport me.steven.indrev.blocks.models.pipes.CableModel\nimport me.steven.indrev.blocks.models.pipes.FluidPipeModel\nimport me.steven.indrev.blocks.models.pipes.ItemPipeModel\nimport me.steven.indrev.items.models.TankItemBakedModel\nimport me.steven.indrev.utils.EmptyModel\nimport me.steven.indrev.utils.hide\nimport me.steven.indrev.utils.identifier\nimport net.fabricmc.fabric.api.client.model.ExtraModelProvider\nimport net.fabricmc.fabric.api.client.model.ModelProviderContext\nimport net.fabricmc.fabric.api.client.model.ModelVariantProvider\nimport net.minecraft.client.render.model.UnbakedModel\nimport net.minecraft.client.util.ModelIdentifier\nimport net.minecraft.resource.ResourceManager\nimport net.minecraft.util.Identifier\nimport java.util.function.Consumer\n\nobject IRModelManagers : ModelVariantProvider, ExtraModelProvider {\n\n    private val CABLE_MODELS = arrayOf(\n        CableModel(Tier.MK1), CableModel(Tier.MK2), CableModel(Tier.MK3), CableModel(Tier.MK4)\n    )\n\n    private val ITEM_PIPE_MODELS = arrayOf(\n        ItemPipeModel(Tier.MK1), ItemPipeModel(Tier.MK2), ItemPipeModel(Tier.MK3), ItemPipeModel(Tier.MK4)\n    )\n\n    private val FLUID_PIPE_MODELS = arrayOf(\n        FluidPipeModel(Tier.MK1), FluidPipeModel(Tier.MK2), FluidPipeModel(Tier.MK3), FluidPipeModel(Tier.MK4)\n    )\n\n    override fun loadModelVariant(resourceId: ModelIdentifier, ctx: ModelProviderContext?): UnbakedModel? {\n        if (resourceId.namespace != \"indrev\") return null\n        val path = resourceId.path\n        val variant = resourceId.variant\n        val id = Identifier(resourceId.namespace, resourceId.path)\n        return when {\n            hide(id) -> EmptyModel\n            path == \"drill_head\" -> DrillHeadModel(resourceId.variant)\n            path == \"pump_pipe\" -> PumpPipeBakedModel()\n            path == \"tank\" && variant == \"inventory\" -> TankItemBakedModel()\n            path.startsWith(\"cable_mk\") -> CABLE_MODELS[path.last().toString().toInt() - 1]\n            path.startsWith(\"item_pipe_mk\") -> ITEM_PIPE_MODELS[path.last().toString().toInt() - 1]\n            path.startsWith(\"fluid_pipe_mk\") -> FLUID_PIPE_MODELS[path.last().toString().toInt() - 1]\n            MachineRegistry.MAP.containsKey(id) -> MachineRegistry.MAP[id]?.modelProvider?.get(Tier.values()[(path.last().toString().toIntOrNull() ?: 4) - 1])?.invoke(id.path.replace(\"_creative\", \"_mk4\"))\n            else -> return null\n        }\n    }\n\n    override fun provideExtraModels(manager: ResourceManager?, out: Consumer<Identifier>) {\n        out.accept(ModelIdentifier(identifier(\"drill_head\"), \"stone\"))\n        out.accept(ModelIdentifier(identifier(\"drill_head\"), \"iron\"))\n        out.accept(ModelIdentifier(identifier(\"drill_head\"), \"diamond\"))\n        out.accept(ModelIdentifier(identifier(\"drill_head\"), \"netherite\"))\n        out.accept(ModelIdentifier(identifier(\"pump_pipe\"), \"\"))\n        out.accept(ModelIdentifier(identifier(\"composting\"), \"\"))\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/registry/MachineRegistry.kt",
    "content": "package me.steven.indrev.registry\n\nimport me.steven.indrev.api.machines.Tier\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blockentities.crafters.*\nimport me.steven.indrev.blockentities.farms.*\nimport me.steven.indrev.blockentities.generators.*\nimport me.steven.indrev.blockentities.laser.LaserBlockEntity\nimport me.steven.indrev.blockentities.miningrig.DataCardWriterBlockEntity\nimport me.steven.indrev.blockentities.miningrig.MiningRigBlockEntity\nimport me.steven.indrev.blockentities.modularworkbench.ModularWorkbenchBlockEntity\nimport me.steven.indrev.blockentities.storage.ChargePadBlockEntity\nimport me.steven.indrev.blockentities.storage.LazuliFluxContainerBlockEntity\nimport me.steven.indrev.blocks.machine.*\nimport me.steven.indrev.blocks.machine.solarpowerplant.SteamTurbineBlock\nimport me.steven.indrev.blocks.models.LazuliFluxContainerBakedModel\nimport me.steven.indrev.blocks.models.MachineBakedModel\nimport me.steven.indrev.blocks.models.MinerBakedModel\nimport me.steven.indrev.config.IConfig\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.gui.screenhandlers.machines.*\nimport me.steven.indrev.items.energy.MachineBlockItem\nimport me.steven.indrev.utils.*\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings\nimport net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder\nimport net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap\nimport net.fabricmc.fabric.api.client.rendereregistry.v1.BlockEntityRendererRegistry\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.Storage\nimport net.fabricmc.loader.api.FabricLoader\nimport net.minecraft.block.Block\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.Material\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.client.render.block.entity.BlockEntityRenderer\nimport net.minecraft.client.render.model.UnbakedModel\nimport net.minecraft.client.util.SpriteIdentifier\nimport net.minecraft.item.BlockItem\nimport net.minecraft.screen.PlayerScreenHandler\nimport net.minecraft.sound.BlockSoundGroup\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport team.reborn.energy.api.EnergyStorage\nimport java.util.*\n\nclass MachineRegistry(private val key: String, val upgradeable: Boolean = true, vararg val tiers: Tier = Tier.values()) {\n\n    private val configs: MutableMap<Tier, IConfig> = EnumMap(Tier::class.java)\n    private val blocks: MutableMap<Tier, Block> = EnumMap(Tier::class.java)\n    val blockEntities: MutableMap<Tier, BlockEntityType<*>> = EnumMap(Tier::class.java)\n\n    @Environment(EnvType.CLIENT)\n    val modelProvider: MutableMap<Tier, (String) -> UnbakedModel?> = EnumMap(Tier::class.java)\n\n    fun blockProvider(blockProvider: MachineRegistry.(Tier) -> Block): MachineRegistry {\n        tiers.forEach { tier ->\n            val block = blockProvider(this, tier)\n            if (FabricLoader.getInstance().environmentType == EnvType.CLIENT)\n                BlockRenderLayerMap.INSTANCE.putBlock(block, RenderLayer.getCutout())\n            val blockItem =\n                if (block is MachineBlock) MachineBlockItem(block, itemSettings())\n                else BlockItem(block, itemSettings())\n            identifier(\"${key}_${tier.id}\").apply {\n                block(block)\n                item(blockItem)\n                if (block is MachineBlock) {\n                    MAP[this] = this@MachineRegistry\n                    if (block.config != null) configs[tier] = block.config\n                }\n            }\n            blocks[tier] = block\n        }\n        return this\n    }\n\n    fun blockEntityProvider(entityProvider: (Tier) -> (BlockPos, BlockState) -> BlockEntity): MachineRegistry {\n        tiers.forEach { tier ->\n            val blockEntityType =\n                FabricBlockEntityTypeBuilder.create(entityProvider(tier), block(tier)).build(null)\n            identifier(\"${key}_${tier.id}\").apply {\n                blockEntityType(blockEntityType)\n            }\n            blockEntities[tier] = blockEntityType\n        }\n        return this\n    }\n\n    fun forEachBlockEntity(action: (Tier, BlockEntityType<*>) -> Unit) = blockEntities.forEach(action)\n\n    fun forEachBlock(action: (Tier, Block) -> Unit) = blocks.forEach(action)\n\n    fun blockEntityType(tier: Tier) = blockEntities[tier]\n        ?: throw IllegalStateException(\"invalid tier for machine $key\")\n\n    fun config(tier: Tier) = configs[tier]\n        ?: throw java.lang.IllegalStateException(\"invalid tier for machine $key\")\n\n    fun block(tier: Tier) = blocks[tier]\n        ?: throw java.lang.IllegalStateException(\"invalid tier for machine $key\")\n\n    fun modelProvider(provider: (Tier) -> (String) -> UnbakedModel?): MachineRegistry {\n\n        if (FabricLoader.getInstance().environmentType == EnvType.CLIENT) {\n            tiers.forEach { tier -> modelProvider[tier] = provider(tier) }\n        }\n\n        return this\n    }\n\n    fun defaultModelProvider(hasWorkingState: Boolean = true): MachineRegistry {\n        if (FabricLoader.getInstance().environmentType == EnvType.CLIENT)\n            modelProvider { tier ->\n                { id ->\n                    MachineBakedModel(id).also {\n                        it.tierOverlay(tier)\n                        if (hasWorkingState)\n                            it.workingOverlayIds.add(\n                                SpriteIdentifier(\n                                    PlayerScreenHandler.BLOCK_ATLAS_TEXTURE,\n                                    identifier(\"block/${id.replace(Regex(\"_mk[0-4]\"), \"\")}_on\")\n                                )\n                            )\n                    }\n                }\n            }\n        return this\n    }\n\n    fun noModelProvider(): MachineRegistry {\n        if (FabricLoader.getInstance().environmentType == EnvType.CLIENT)\n            modelProvider { { null } }\n        return this\n    }\n\n    fun energyProvider(provider: (Tier) -> (BlockEntity, Direction) -> EnergyStorage?): MachineRegistry {\n        blockEntities.forEach { (tier, type) ->\n            EnergyStorage.SIDED.registerForBlockEntities(provider(tier), type)\n        }\n        return this\n    }\n\n    fun defaultEnergyProvider(): MachineRegistry = energyProvider { { be, side -> (be as? MachineBlockEntity<*>)?.storage?.getSideStorage(side) } }\n\n    fun fluidStorageProvider(provider: (Tier) -> (BlockEntity, Direction) -> Storage<FluidVariant>?): MachineRegistry {\n        blockEntities.forEach { (tier, type) ->\n            FluidStorage.SIDED.registerForBlockEntities(provider(tier), type)\n        }\n        return this\n    }\n\n    fun defaultFluidStorageProvider(): MachineRegistry {\n        blockEntities.forEach { (_, type) ->\n            FluidStorage.SIDED.registerForBlockEntities({ be, dir ->  (be as MachineBlockEntity<*>).fluidComponent?.getCachedSide(dir) }, type)\n        }\n        return this\n    }\n\n    @Suppress(\"UNCHECKED_CAST\")\n    @Environment(EnvType.CLIENT)\n    fun <T : BlockEntity> registerBlockEntityRenderer(renderer: () -> BlockEntityRenderer<T>) {\n        blockEntities.forEach { (_, type) ->\n            BlockEntityRendererRegistry.INSTANCE.register(type as BlockEntityType<T>) { _ -> renderer() }\n        }\n    }\n\n    @Environment(EnvType.CLIENT)\n    fun setRenderLayer(layer: RenderLayer) {\n        blocks.forEach { (_, block) -> BlockRenderLayerMap.INSTANCE.putBlock(block, layer) }\n    }\n\n    companion object {\n\n        val MAP = hashMapOf<Identifier, MachineRegistry>()\n\n        private val SETTINGS = {\n            FabricBlockSettings.of(Material.METAL)\n                .sounds(BlockSoundGroup.METAL)\n                .requiresTool()\n                .strength(5.0f, 6.0f)\n        }\n\n        val COAL_GENERATOR_REGISTRY = MachineRegistry(\"coal_generator\", false, Tier.MK1)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this, SETTINGS(), tier, IRConfig.generators.coalGenerator, ::CoalGeneratorScreenHandler\n                )\n            }\n            .blockEntityProvider { { pos, state -> CoalGeneratorBlockEntity(pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider()\n\n        val SOLAR_GENERATOR_REGISTRY = MachineRegistry(\"solar_generator\", false, Tier.MK1, Tier.MK3)\n            .blockProvider { tier ->\n                MachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    when (tier) {\n                        Tier.MK1 -> IRConfig.generators.solarGeneratorMk1\n                        else -> IRConfig.generators.solarGeneratorMk3\n                    }, ::SolarGeneratorScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> SolarGeneratorBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider()\n\n        val BIOMASS_GENERATOR_REGISTRY = MachineRegistry(\"biomass_generator\", false, Tier.MK3)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this, SETTINGS(), tier, IRConfig.generators.biomassGenerator, ::BiomassGeneratorScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> BiomassGeneratorBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider()\n\n        val HEAT_GENERATOR_REGISTRY = MachineRegistry(\"heat_generator\", false, Tier.MK4)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this, SETTINGS().nonOpaque(), tier, IRConfig.generators.heatGenerator, ::HeatGeneratorScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> HeatGeneratorBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultFluidStorageProvider()\n            .noModelProvider()\n\n        val GAS_BURNING_GENERATOR_REGISTRY = MachineRegistry(\"gas_generator\", false, Tier.MK4)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this, SETTINGS().nonOpaque(), tier, IRConfig.generators.gasGenerator, ::GasBurningGeneratorScreenHandler\n                )\n            }\n            .blockEntityProvider { { pos, state -> GasBurningGeneratorBlockEntity(pos, state) } }\n            .defaultEnergyProvider()\n            .defaultFluidStorageProvider()\n            .defaultModelProvider()\n\n        val LAZULI_FLUX_CONTAINER_REGISTRY = MachineRegistry(\"lazuli_flux_container\", false)\n            .blockProvider { tier -> LazuliFluxContainerBlock(this, SETTINGS(), tier) }\n            .blockEntityProvider { tier -> { pos, state -> LazuliFluxContainerBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .modelProvider { { id -> LazuliFluxContainerBakedModel(id) } }\n\n        val ELECTRIC_FURNACE_REGISTRY = MachineRegistry(\"electric_furnace\")\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(), tier,\n                    when (tier) {\n                        Tier.MK1 -> IRConfig.machines.electricFurnaceMk1\n                        Tier.MK2 -> IRConfig.machines.electricFurnaceMk2\n                        Tier.MK3 -> IRConfig.machines.electricFurnaceMk3\n                        else -> IRConfig.machines.electricFurnaceMk4\n                    }, ::ElectricFurnaceScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> ElectricFurnaceBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .modelProvider { tier ->\n                { id ->\n                    MachineBakedModel(id).also {\n                        it.workingOverlayIds.add(blockSpriteId(\"block/electric_furnace_emissive_on\"))\n                        it.tierOverlay(tier)\n                    }\n                }\n            }\n\n        val PULVERIZER_REGISTRY = MachineRegistry(\"pulverizer\")\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    when (tier) {\n                        Tier.MK1 -> IRConfig.machines.pulverizerMk1\n                        Tier.MK2 -> IRConfig.machines.pulverizerMk2\n                        Tier.MK3 -> IRConfig.machines.pulverizerMk3\n                        else -> IRConfig.machines.pulverizerMk4\n                    }, ::PulverizerScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> PulverizerBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider()\n\n        val COMPRESSOR_REGISTRY = MachineRegistry(\"compressor\")\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    when (tier) {\n                        Tier.MK1 -> IRConfig.machines.compressorMk1\n                        Tier.MK2 -> IRConfig.machines.compressorMk2\n                        Tier.MK3 -> IRConfig.machines.compressorMk3\n                        else -> IRConfig.machines.compressorMk4\n                    }, ::CompressorScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> CompressorBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider()\n\n        val SOLID_INFUSER_REGISTRY = MachineRegistry(\"solid_infuser\")\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    when (tier) {\n                        Tier.MK1 -> IRConfig.machines.infuserMk1\n                        Tier.MK2 -> IRConfig.machines.infuserMk2\n                        Tier.MK3 -> IRConfig.machines.infuserMk3\n                        else -> IRConfig.machines.infuserMk4\n                    }, ::SolidInfuserScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> SolidInfuserBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .modelProvider { tier ->\n                { id ->\n                    MachineBakedModel(id).also {\n                        it.workingOverlayIds.add(blockSpriteId(\"block/solid_infuser_emissive_on\"))\n                        it.tierOverlay(tier)\n                    }\n                }\n            }\n\n        val SAWMILL_REGISTRY = MachineRegistry(\"sawmill\")\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(), tier,\n                    when (tier) {\n                        Tier.MK1 -> IRConfig.machines.sawmillMk1\n                        Tier.MK2 -> IRConfig.machines.sawmillMk2\n                        Tier.MK3 -> IRConfig.machines.sawmillMk3\n                        else -> IRConfig.machines.sawmillMk4\n                    }, ::SawmillScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> SawmillBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider()\n\n        val RECYCLER_REGISTRY = MachineRegistry(\"recycler\", false, Tier.MK2)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this, SETTINGS(), tier, IRConfig.machines.recycler, ::RecyclerScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> RecyclerBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider()\n\n        val SMELTER_REGISTRY = MachineRegistry(\"smelter\", false, Tier.MK4)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this, SETTINGS(), tier, IRConfig.machines.smelter, ::SmelterScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> SmelterBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultFluidStorageProvider()\n            .defaultModelProvider(hasWorkingState = false)\n\n        val CONDENSER_REGISTRY = MachineRegistry(\"condenser\", false, Tier.MK4)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this, SETTINGS(), tier, IRConfig.machines.condenser, ::CondenserScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> CondenserBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultFluidStorageProvider()\n            .defaultModelProvider()\n\n        val ELECTRIC_FURNACE_FACTORY_REGISTRY = MachineRegistry(\"electric_furnace_factory\", false, Tier.MK4)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    IRConfig.machines.electricFurnaceFactory,\n                    ::ElectricFurnaceFactoryScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> ElectricFurnaceFactoryBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .modelProvider {\n                { id ->\n                    MachineBakedModel(id).also {\n                        it.baseSpriteId = blockSpriteId(\"block/electric_furnace\")\n                        it.factoryOverlay()\n                    }\n                }\n            }\n\n        val PULVERIZER_FACTORY_REGISTRY = MachineRegistry(\"pulverizer_factory\", false, Tier.MK4)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    IRConfig.machines.pulverizerFactory,\n                    ::PulverizerFactoryScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> PulverizerFactoryBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .modelProvider {\n                { id ->\n                    MachineBakedModel(id).also {\n                        it.baseSpriteId = blockSpriteId(\"block/pulverizer\")\n                        it.factoryOverlay()\n                    }\n                }\n            }\n\n        val COMPRESSOR_FACTORY_REGISTRY = MachineRegistry(\"compressor_factory\", false, Tier.MK4)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    IRConfig.machines.compressorFactory,\n                    ::CompressorFactoryScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> CompressorFactoryBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .modelProvider {\n                { id ->\n                    MachineBakedModel(id).also {\n                        it.baseSpriteId = blockSpriteId(\"block/compressor\")\n                        it.overlayIds.add(blockSpriteId(\"block/factory_overlay_compressor\"))\n                    }\n                }\n            }\n\n        val SOLID_INFUSER_FACTORY_REGISTRY = MachineRegistry(\"solid_infuser_factory\", false, Tier.MK4)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    IRConfig.machines.infuserFactory,\n                    ::SolidInfuserFactoryScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> SolidInfuserFactoryBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .modelProvider {\n                { id ->\n                    MachineBakedModel(id).also {\n                        it.baseSpriteId = blockSpriteId(\"block/solid_infuser\")\n                        it.factoryOverlay()\n                    }\n                }\n            }\n\n        val DRAIN_REGISTRY = MachineRegistry(\"drain\", false, Tier.MK1)\n            .blockProvider { tier ->\n                MachineBlock(\n                    this, SETTINGS(), tier, IRConfig.machines.drain, null\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> DrainBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultFluidStorageProvider()\n            .defaultModelProvider(hasWorkingState = false)\n\n        val PUMP_REGISTRY = MachineRegistry(\"pump\", false, Tier.MK1)\n            .blockProvider { PumpBlock(this, SETTINGS().nonOpaque()) }\n            .blockEntityProvider { tier -> { pos, state -> PumpBlockEntity(tier, pos, state) } }\n            .energyProvider { { be, dir -> if (dir == Direction.UP) (be as? MachineBlockEntity<*>)?.storage?.getSideStorage(dir) else null } }\n            .fluidStorageProvider { { be, dir -> if (be.cachedState[HorizontalFacingMachineBlock.HORIZONTAL_FACING] == dir) (be as PumpBlockEntity).fluidComponent else null } }\n            .noModelProvider()\n\n        val FLUID_INFUSER_REGISTRY = MachineRegistry(\"fluid_infuser\", true)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    when (tier) {\n                        Tier.MK1 -> IRConfig.machines.fluidInfuserMk1\n                        Tier.MK2 -> IRConfig.machines.fluidInfuserMk2\n                        Tier.MK3 -> IRConfig.machines.fluidInfuserMk3\n                        else -> IRConfig.machines.fluidInfuserMk4\n                    },\n                    ::FluidInfuserScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> FluidInfuserBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultFluidStorageProvider()\n            .defaultModelProvider()\n        \n        val ELECTROLYTIC_SEPARATOR_REGISTRY = MachineRegistry(\"electrolytic_separator\", true)\n            .blockProvider { tier ->\n                ElectrolyticSeparatorBlock(this, SETTINGS(), tier)\n            }\n            .blockEntityProvider { tier -> { pos, state -> ElectrolyticSeparatorBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultFluidStorageProvider()\n            .defaultModelProvider()\n\n        val CHOPPER_REGISTRY = MachineRegistry(\"chopper\", true)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    when (tier) {\n                        Tier.MK1 -> IRConfig.machines.chopperMk1\n                        Tier.MK2 -> IRConfig.machines.chopperMk2\n                        Tier.MK3 -> IRConfig.machines.chopperMk3\n                        else -> IRConfig.machines.chopperMk4\n                    }, ::ChopperScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> ChopperBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider()\n\n        val FARMER_REGISTRY = MachineRegistry(\"farmer\", true)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    when (tier) {\n                        Tier.MK1 -> IRConfig.machines.farmerMk1\n                        Tier.MK2 -> IRConfig.machines.farmerMk2\n                        Tier.MK3 -> IRConfig.machines.farmerMk3\n                        else -> IRConfig.machines.farmerMk4\n                    }, ::FarmerScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> FarmerBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider(hasWorkingState = false)\n\n        val SLAUGHTER_REGISTRY = MachineRegistry(\"slaughter\", true)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    when (tier) {\n                        Tier.MK1 -> IRConfig.machines.slaughterMk1\n                        Tier.MK2 -> IRConfig.machines.slaughterMk2\n                        Tier.MK3 -> IRConfig.machines.slaughterMk3\n                        else -> IRConfig.machines.slaughterMk4\n                    }, ::SlaughterScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> SlaughterBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider(hasWorkingState = false)\n\n        val RANCHER_REGISTRY = MachineRegistry(\"rancher\", true)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    when (tier) {\n                        Tier.MK1 -> IRConfig.machines.rancherMk1\n                        Tier.MK2 -> IRConfig.machines.rancherMk2\n                        Tier.MK3 -> IRConfig.machines.rancherMk3\n                        else -> IRConfig.machines.rancherMk4\n                    }, ::RancherScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> RancherBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider()\n\n        val MINING_RIG_REGISTRY = MachineRegistry(\"mining_rig\", false, Tier.MK4)\n            .blockProvider { tier -> MiningRigBlock(this, SETTINGS(), tier) }\n            .blockEntityProvider { tier -> { pos, state -> MiningRigBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .modelProvider {\n                { id -> MinerBakedModel(id) }\n            }\n\n        val DATA_CARD_WRITER_REGISTRY = MachineRegistry(\"data_card_writer\", false, Tier.MK4)\n            .blockProvider { tier -> HorizontalFacingMachineBlock(\n                this,\n                SETTINGS(),\n                tier,\n                IRConfig.machines.dataCardWriter,\n                ::DataCardWriterScreenHandler\n            ) }\n            .blockEntityProvider { tier -> { pos, state -> DataCardWriterBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .defaultModelProvider()\n\n        val FISHER_REGISTRY = MachineRegistry(\"fisher\", false, Tier.MK2, Tier.MK3, Tier.MK4)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS(),\n                    tier,\n                    when (tier) {\n                        Tier.MK2 -> IRConfig.machines.fishingMk2\n                        Tier.MK3 -> IRConfig.machines.fishingMk3\n                        else -> IRConfig.machines.fishingMk4\n                    }, ::FisherScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> FisherBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .noModelProvider()\n\n        val DIRT_OXYGENATOR_REGISTRY = MachineRegistry(\"dirt_oxygenator\", false, Tier.MK1)\n            .blockProvider { DirtOxygenatorBlock(this, SETTINGS()) }\n            .blockEntityProvider { { pos, state -> DirtOxygenatorBlockEntity(pos, state) } }\n            .defaultEnergyProvider()\n            .defaultFluidStorageProvider()\n            .defaultModelProvider(hasWorkingState = false)\n\n        val MODULAR_WORKBENCH_REGISTRY = MachineRegistry(\"modular_workbench\", false, Tier.MK4)\n            .blockProvider { tier ->\n                HorizontalFacingMachineBlock(\n                    this,\n                    SETTINGS().nonOpaque(),\n                    tier,\n                    IRConfig.machines.modularWorkbench,\n                    ::ModularWorkbenchScreenHandler\n                )\n            }\n            .blockEntityProvider { tier -> { pos, state -> ModularWorkbenchBlockEntity(tier, pos, state) } }\n            .defaultEnergyProvider()\n            .noModelProvider()\n\n        val CHARGE_PAD_REGISTRY = MachineRegistry(\"charge_pad\", false, Tier.MK4)\n            .blockProvider { tier -> ChargePadBlock(this, SETTINGS(), tier) }\n            .blockEntityProvider { tier -> { pos, state -> ChargePadBlockEntity(tier, pos, state) } }\n            .energyProvider { { be, dir -> if (dir == Direction.DOWN) (be as? ChargePadBlockEntity)?.energyIo else null } }\n            .noModelProvider()\n\n        val LASER_EMITTER_REGISTRY = MachineRegistry(\"laser_emitter\", false, Tier.MK4)\n            .blockProvider { LaserBlock(this, SETTINGS().nonOpaque()) }\n            .blockEntityProvider { { pos, state -> LaserBlockEntity(pos, state) } }\n            .energyProvider { { be, dir -> if (dir.opposite == be.cachedState[FacingMachineBlock.FACING]) (be as LaserBlockEntity).storage.getSideStorage(dir) else null } }\n            .noModelProvider()\n\n        val STEAM_TURBINE_REGISTRY = MachineRegistry(\"steam_turbine\", false, Tier.MK4)\n            .blockProvider { SteamTurbineBlock(this, SETTINGS().nonOpaque()) }\n            .blockEntityProvider { { pos, state -> SteamTurbineBlockEntity(pos, state) } }\n            .defaultFluidStorageProvider()\n            .defaultModelProvider(true)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/registry/MaterialHelper.kt",
    "content": "package me.steven.indrev.registry\n\nimport me.steven.indrev.utils.identifier\nimport me.steven.indrev.utils.itemSettings\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings\nimport net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap\nimport net.fabricmc.loader.api.FabricLoader\nimport net.minecraft.block.Block\nimport net.minecraft.block.Material\nimport net.minecraft.client.render.RenderLayer\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.item.*\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.registry.Registry\n\nclass MaterialHelper(private val id: String, private val block: MaterialHelper.() -> Unit) {\n\n    fun withItems(vararg variants: String): MaterialHelper {\n        variants.forEach { variant ->\n            val identifier = identifier(\"${id}_$variant\")\n            map[identifier] = { Registry.register(Registry.ITEM, identifier, Item(itemSettings())) }\n        }\n        return this\n    }\n\n    fun withItem(): MaterialHelper {\n        Registry.register(Registry.ITEM, identifier(id), Item(itemSettings()))\n        return this\n    }\n\n    fun withOre(rawOre: Boolean = true, supplier: (FabricBlockSettings) -> Block = { Block(it) }): MaterialHelper {\n        val ore = supplier(FabricBlockSettings.of(Material.STONE).requiresTool().strength(3f, 3f))\n        val identifier = identifier(\"${id}_ore\")\n        map[identifier] = {\n            Registry.register(Registry.BLOCK, identifier, ore)\n            Registry.register(Registry.ITEM, identifier, BlockItem(ore, itemSettings()))\n        }\n\n        val deepslateOre = supplier(FabricBlockSettings.of(Material.STONE).requiresTool().strength(3f, 3f))\n        val deepslateId = identifier(\"deepslate_${id}_ore\")\n        map[deepslateId] = {\n            Registry.register(Registry.BLOCK, deepslateId, deepslateOre)\n            Registry.register(Registry.ITEM, deepslateId, BlockItem(deepslateOre, itemSettings()))\n        }\n\n        if (rawOre) {\n            val rawOreBlock = supplier(FabricBlockSettings.of(Material.STONE).requiresTool().strength(3f, 3f))\n            val rawOreId = identifier(\"raw_${id}\")\n            val rawOreBlockId = identifier(\"raw_${id}_block\")\n            map[rawOreId] = {\n                Registry.register(Registry.BLOCK, rawOreBlockId, rawOreBlock)\n                Registry.register(Registry.ITEM, rawOreBlockId, BlockItem(rawOreBlock, itemSettings()))\n                Registry.register(Registry.ITEM, rawOreId, Item(itemSettings()))\n            }\n        }\n\n        return this\n    }\n\n    fun withTools(pickaxe: PickaxeItem, axe: AxeItem, shovel: ShovelItem, sword: SwordItem, hoe: HoeItem) {\n        map[identifier(\"${id}_pickaxe\")] = {\n            Registry.register(Registry.ITEM, identifier(\"${id}_pickaxe\"), pickaxe)\n        }\n        map[identifier(\"${id}_axe\")] = {\n            Registry.register(Registry.ITEM, identifier(\"${id}_axe\"), axe)\n        }\n        map[identifier(\"${id}_shovel\")] = {\n            Registry.register(Registry.ITEM, identifier(\"${id}_shovel\"), shovel)\n        }\n        map[identifier(\"${id}_sword\")] = {\n            Registry.register(Registry.ITEM, identifier(\"${id}_sword\"), sword)\n        }\n        map[identifier(\"${id}_hoe\")] = {\n            Registry.register(Registry.ITEM, identifier(\"${id}_hoe\"), hoe)\n        }\n    }\n\n    fun withArmor(material: ArmorMaterial) {\n        map[identifier(\"${id}_helmet\")] = {\n            Registry.register(Registry.ITEM, identifier(\"${id}_helmet\"), ArmorItem(material, EquipmentSlot.HEAD, itemSettings()))\n        }\n        map[identifier(\"${id}_chestplate\")] = {\n            Registry.register(Registry.ITEM, identifier(\"${id}_chestplate\"), ArmorItem(material, EquipmentSlot.CHEST, itemSettings()))\n        }\n        map[identifier(\"${id}_leggings\")] = {\n            Registry.register(Registry.ITEM, identifier(\"${id}_leggings\"), ArmorItem(material, EquipmentSlot.LEGS, itemSettings()))\n        }\n        map[identifier(\"${id}_boots\")] = {\n            Registry.register(Registry.ITEM, identifier(\"${id}_boots\"), ArmorItem(material, EquipmentSlot.FEET, itemSettings()))\n        }\n    }\n\n    fun withBlock(): MaterialHelper {\n        val block =\n            Block(FabricBlockSettings.of(Material.METAL).requiresTool().strength(5f, 6f))\n        val id = identifier(\"${id}_block\")\n        map[id] = {\n            Registry.register(Registry.BLOCK, id, block)\n            Registry.register(Registry.ITEM, id, BlockItem(block, itemSettings()))\n        }\n        if (FabricLoader.getInstance().environmentType == EnvType.CLIENT) {\n            BlockRenderLayerMap.INSTANCE.putBlock(block, RenderLayer.getCutout())\n        }\n        return this\n    }\n\n    fun register() = block()\n\n    companion object {\n        val map = HashMap<Identifier, () -> Unit>()\n\n        fun register() {\n            map.entries.sortedWith(\n                compareBy<MutableMap.MutableEntry<Identifier, () -> Unit>> { id -> id.key.path.contains(\"ore\") && !id.key.path.contains(\"purified\") }\n                    .then(compareBy { id -> id.key.path.contains(\"raw\") })\n                    .then(compareBy { id -> id.key.path.contains(\"block\") })\n                    .then(compareBy { id -> id.key.path.contains(\"ingot\") })\n                    .then(compareBy { id -> id.key.path.contains(\"chunk\") })\n                    .then(compareBy { id -> id.key.path.contains(\"dust\") })\n                    .then(compareBy { id -> id.key.path.contains(\"purified\") })\n                    .then(compareBy { id -> id.key.path.contains(\"plate\") && !id.key.path.contains(\"chestplate\") })\n                    .then(compareBy { id -> id.key.path.contains(\"nugget\") })\n                    .then(compareBy<MutableMap.MutableEntry<Identifier, () -> Unit>> { id -> id.key.path.substring(0, id.key.path.indexOf(\"_\")) }\n                        .then(compareBy { id -> id.key.path.contains(\"sword\") })\n                        .then(compareBy { id -> id.key.path.contains(\"pickaxe\") })\n                        .then(compareBy { id -> id.key.path.contains(\"axe\") && !id.key.path.contains(\"pickaxe\") })\n                        .then(compareBy { id -> id.key.path.contains(\"shovel\") })\n                        .then(compareBy { id -> id.key.path.contains(\"hoe\") })\n                        .then(compareBy { id -> id.key.path.contains(\"helmet\") })\n                        .then(compareBy { id -> id.key.path.contains(\"chestplate\") })\n                        .then(compareBy { id -> id.key.path.contains(\"leggings\") })\n                        .then(compareBy { id -> id.key.path.contains(\"boots\") })\n                    )\n            ).asReversed().forEach { it.value() }\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/registry/WorldGeneration.kt",
    "content": "package me.steven.indrev.registry\n\nimport com.google.common.collect.ImmutableList\nimport me.steven.indrev.config.IRConfig\nimport me.steven.indrev.utils.identifier\nimport me.steven.indrev.world.features.IRConfiguredFeature\nimport me.steven.indrev.world.features.SulfurCrystalFeature\nimport net.fabricmc.fabric.api.biome.v1.BiomeModifications\nimport net.minecraft.block.Blocks\nimport net.minecraft.util.math.intprovider.UniformIntProvider\nimport net.minecraft.util.registry.Registry\nimport net.minecraft.util.registry.RegistryEntry\nimport net.minecraft.world.biome.BiomeKeys\nimport net.minecraft.world.gen.GenerationStep\nimport net.minecraft.world.gen.YOffset\nimport net.minecraft.world.gen.feature.*\nimport net.minecraft.world.gen.placementmodifier.CountPlacementModifier\nimport net.minecraft.world.gen.placementmodifier.HeightRangePlacementModifier\nimport net.minecraft.world.gen.placementmodifier.SquarePlacementModifier\nimport net.minecraft.world.gen.stateprovider.BlockStateProvider\n\nobject WorldGeneration {\n    fun init() {\n        val config = IRConfig.oregen\n\n        if (config.tin) {\n            configuredFeatures.add(tinFeature)\n        }\n        if (config.nikolite) {\n            configuredFeatures.add(nikoliteFeature)\n        }\n        if (config.lead) {\n            configuredFeatures.add(leadFeature)\n        }\n        if (config.tungsten) {\n            configuredFeatures.add(tungstenFeature)\n        }\n        if (config.silver) {\n            configuredFeatures.add(silverFeature)\n        }\n        if (config.sulfurCrystals) {\n            configuredFeatures.add(sulfurFeatureOverworld)\n            configuredFeatures.add(sulfurFeatureNether)\n        }\n        if (config.sulfuricAcidLake) {\n            configuredFeatures.add(acidLakesFeature)\n        }\n    }\n\n    private val configuredFeatures = mutableListOf<IRConfiguredFeature>()\n\n    fun addFeatures() {\n        configuredFeatures.forEach { feature ->\n            BiomeModifications.addFeature(\n                { ctx -> feature.biomePredicate(ctx) },\n                feature.step,\n                feature.placedFeatureKey\n            )\n        }\n    }\n\n    private val tinTargets = ImmutableList.of(\n        OreFeatureConfig.createTarget(\n            OreConfiguredFeatures.STONE_ORE_REPLACEABLES,\n            IRBlockRegistry.TIN_ORE().defaultState\n        ),\n        OreFeatureConfig.createTarget(\n            OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES,\n            IRBlockRegistry.DEEPSLATE_TIN_ORE().defaultState\n        )\n    )\n\n    private val tinFeature =\n        IRConfiguredFeature(\n            identifier(\"tin_ore\"),\n            GenerationStep.Feature.UNDERGROUND_ORES,\n            ConfiguredFeature(Feature.ORE, OreFeatureConfig(tinTargets, 10)),\n            { feature ->\n                PlacedFeature(\n                    RegistryEntry.of(feature),\n                    listOf(\n                        CountPlacementModifier.of(14),\n                        SquarePlacementModifier.of(),\n                        HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-48), YOffset.fixed(48))\n                    )\n                )\n            },\n            IRConfiguredFeature.IS_OVERWORLD\n        )\n\n    private val leadTargets = ImmutableList.of(\n        OreFeatureConfig.createTarget(\n            OreConfiguredFeatures.STONE_ORE_REPLACEABLES,\n            IRBlockRegistry.LEAD_ORE().defaultState\n        ),\n        OreFeatureConfig.createTarget(\n            OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES,\n            IRBlockRegistry.DEEPSLATE_LEAD_ORE().defaultState\n        )\n    )\n\n    private val leadFeature =\n        IRConfiguredFeature(\n            identifier(\"lead_ore\"),\n            GenerationStep.Feature.UNDERGROUND_ORES,\n            ConfiguredFeature(Feature.ORE, OreFeatureConfig(leadTargets, 6)),\n            { feature ->\n                PlacedFeature(\n                    RegistryEntry.of(feature),\n                    listOf(\n                        CountPlacementModifier.of(11),\n                        SquarePlacementModifier.of(),\n                        HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-32), YOffset.fixed(32))\n                    )\n                )\n            },\n            IRConfiguredFeature.IS_OVERWORLD\n        )\n\n    private val silverTargets = ImmutableList.of(\n        OreFeatureConfig.createTarget(\n            OreConfiguredFeatures.STONE_ORE_REPLACEABLES,\n            IRBlockRegistry.SILVER_ORE().defaultState\n        ),\n        OreFeatureConfig.createTarget(\n            OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES,\n            IRBlockRegistry.DEEPSLATE_SILVER_ORE().defaultState\n        )\n    )\n\n    private val silverFeature =\n        IRConfiguredFeature(\n            identifier(\"silver_ore\"),\n            GenerationStep.Feature.UNDERGROUND_ORES,\n            ConfiguredFeature(Feature.ORE, OreFeatureConfig(silverTargets, 8)),\n            { feature ->\n                PlacedFeature(\n                    RegistryEntry.of(feature),\n                    listOf(\n                        CountPlacementModifier.of(9),\n                        SquarePlacementModifier.of(),\n                        HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-32), YOffset.fixed(32))\n                    )\n                )\n            },\n            IRConfiguredFeature.IS_OVERWORLD\n        )\n\n    private val tungstenTargets = ImmutableList.of(\n        OreFeatureConfig.createTarget(\n            OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES,\n            IRBlockRegistry.DEEPSLATE_TUNGSTEN_ORE().defaultState\n        )\n    )\n\n    private val tungstenFeature =\n        IRConfiguredFeature(\n            identifier(\"tungsten_ore\"),\n            GenerationStep.Feature.UNDERGROUND_ORES,\n            ConfiguredFeature(Feature.ORE, OreFeatureConfig(tungstenTargets, 5)),\n            { feature ->\n                PlacedFeature(\n                    RegistryEntry.of(feature),\n                    listOf(\n                        CountPlacementModifier.of(8),\n                        SquarePlacementModifier.of(),\n                        HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-16), YOffset.fixed(16))\n                    )\n                )\n            },\n            IRConfiguredFeature.IS_OVERWORLD\n        )\n\n    private val nikoliteTargets = ImmutableList.of(\n        OreFeatureConfig.createTarget(\n            OreConfiguredFeatures.STONE_ORE_REPLACEABLES,\n            IRBlockRegistry.NIKOLITE_ORE().defaultState\n        ),\n        OreFeatureConfig.createTarget(\n            OreConfiguredFeatures.DEEPSLATE_ORE_REPLACEABLES,\n            IRBlockRegistry.DEEPSLATE_NIKOLITE_ORE().defaultState\n        )\n    )\n\n    private val nikoliteFeature =\n        IRConfiguredFeature(\n            identifier(\"nikolite_ore\"),\n            GenerationStep.Feature.UNDERGROUND_ORES,\n            ConfiguredFeature(Feature.ORE, OreFeatureConfig(nikoliteTargets, 7)),\n            { feature ->\n                PlacedFeature(\n                    RegistryEntry.of(feature),\n                    listOf(\n                        CountPlacementModifier.of(8),\n                        SquarePlacementModifier.of(),\n                        HeightRangePlacementModifier.trapezoid(YOffset.aboveBottom(-16), YOffset.fixed(16))\n                    )\n                )\n            },\n            IRConfiguredFeature.IS_OVERWORLD\n        )\n\n    private val sulfurCrystalFeature: SulfurCrystalFeature = Registry.register(\n        Registry.FEATURE,\n        identifier(\"sulfur_crystal\"),\n        SulfurCrystalFeature(DefaultFeatureConfig.CODEC)\n    )\n\n    private val sulfurFeatureOverworld =\n        IRConfiguredFeature(\n            identifier(\"sulfur_crystal_overworld\"),\n            GenerationStep.Feature.UNDERGROUND_DECORATION,\n            ConfiguredFeature(sulfurCrystalFeature, DefaultFeatureConfig.INSTANCE),\n            { feature ->\n                PlacedFeature(\n                    RegistryEntry.of(feature),\n                    listOf(\n                        CountPlacementModifier.of(12),\n                        HeightRangePlacementModifier.uniform(YOffset.getBottom(), YOffset.fixed(16))\n                    )\n                )\n            },\n            IRConfiguredFeature.IS_OVERWORLD\n        )\n\n    private val sulfurFeatureNether =\n        IRConfiguredFeature(\n            identifier(\"sulfur_crystal_nether\"),\n            GenerationStep.Feature.UNDERGROUND_DECORATION,\n            ConfiguredFeature(sulfurCrystalFeature, DefaultFeatureConfig.INSTANCE),\n            { feature ->\n                PlacedFeature(\n                    RegistryEntry.of(feature),\n                    listOf(\n                        CountPlacementModifier.of(20),\n                        HeightRangePlacementModifier.uniform(YOffset.getBottom(), YOffset.getTop())\n                    )\n                )\n            },\n            IRConfiguredFeature.IS_NETHER\n        )\n\n    private val acidLakesFeature = IRConfiguredFeature(\n        identifier(\"sulfuric_acid_lake\"),\n        GenerationStep.Feature.LAKES,\n        ConfiguredFeature(\n            Feature.LAKE,\n            LakeFeature.Config(\n                BlockStateProvider.of(IRFluidRegistry.SULFURIC_ACID.defaultState),\n                BlockStateProvider.of(Blocks.COARSE_DIRT.defaultState)\n            )\n        ),\n        { feature ->\n            PlacedFeature(\n                RegistryEntry.of(feature),\n                listOf(\n                    CountPlacementModifier.of(UniformIntProvider.create(0, 60))\n                )\n            )\n        }\n    ) { ctx -> ctx.biomeKey == BiomeKeys.SWAMP }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/tools/IRToolMaterial.kt",
    "content": "package me.steven.indrev.tools\n\nimport me.steven.indrev.registry.IRItemRegistry\nimport net.minecraft.item.ToolMaterial\nimport net.minecraft.recipe.Ingredient\n\nenum class IRToolMaterial(\n    private val miningLevel: Int,\n    private val itemDurability: Int,\n    private val miningSpeed: Float,\n    private val attackDamage: Float,\n    private val enchantability: Int,\n    private val repairIngredient: () -> Ingredient?\n) : ToolMaterial {\n    TIN(1, 200, 4.0f, 1.0f, 14, { Ingredient.ofItems(IRItemRegistry.TIN_INGOT()) }),\n    COPPER(2, 300, 4.5f, 1.0f, 14, { Ingredient.ofItems(IRItemRegistry.COPPER_INGOT()) }),\n    STEEL(3, 600, 4.5f, 2.0f, 14, { Ingredient.ofItems(IRItemRegistry.STEEL_INGOT()) }),\n    LEAD(2, 900, 3.0f, 2.0f, 8, { Ingredient.ofItems(IRItemRegistry.LEAD_INGOT()) }),\n    BRONZE(2, 500, 3.5f, 2.5f, 12, { Ingredient.ofItems(IRItemRegistry.BRONZE_INGOT()) }),\n    SILVER(2, 500, 5.0f, 1.0f, 24, { Ingredient.ofItems(IRItemRegistry.SILVER_INGOT()) });\n\n    override fun getAttackDamage(): Float = attackDamage\n    override fun getDurability(): Int = itemDurability\n    override fun getEnchantability(): Int = enchantability\n    override fun getMiningLevel(): Int = miningLevel\n    override fun getMiningSpeedMultiplier(): Float = miningSpeed\n    override fun getRepairIngredient(): Ingredient? = repairIngredient()\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/tools/modular/ArmorModule.kt",
    "content": "package me.steven.indrev.tools.modular\n\nimport me.steven.indrev.registry.IRItemRegistry\nimport net.minecraft.client.gui.screen.Screen\nimport net.minecraft.entity.EquipmentSlot\nimport net.minecraft.item.ItemConvertible\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\n\nenum class ArmorModule(\n    override val key: String,\n    val slots: Array<EquipmentSlot>,\n    override val maxLevel: Int,\n    override val item: ItemConvertible,\n    val hasTexture: Boolean,\n    val hasOverlay: Boolean,\n) : Module {\n    NIGHT_VISION(\"night_vision\", arrayOf(EquipmentSlot.HEAD), 1, { IRItemRegistry.NIGHT_VISION_MODULE_ITEM }, true, true),\n    SPEED(\"speed\", arrayOf(EquipmentSlot.LEGS), 3, { IRItemRegistry.SPEED_MODULE_ITEM }, false, false),\n    JUMP_BOOST(\"jump_boost\", arrayOf(EquipmentSlot.FEET), 3, { IRItemRegistry.JUMP_BOOST_MODULE_ITEM },false, false),\n    BREATHING(\"breathing\", arrayOf(EquipmentSlot.HEAD), 1, { IRItemRegistry.BREATHING_MODULE_ITEM }, false, false),\n    FEATHER_FALLING(\"feather_falling\", arrayOf(EquipmentSlot.FEET), 1, { IRItemRegistry.FEATHER_FALLING_MODULE_ITEM }, false, false),\n    PROTECTION(\"protection\", arrayOf(EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET), 3, { IRItemRegistry.PROTECTION_MODULE_ITEM }, true, true),\n    AUTO_FEEDER(\"auto_feeder\", arrayOf(EquipmentSlot.HEAD), 1, { IRItemRegistry.AUTO_FEEDER_MODULE_ITEM }, false, false),\n    CHARGER(\"charger\", arrayOf(EquipmentSlot.CHEST), 1, { IRItemRegistry.CHARGER_MODULE_ITEM }, false, false),\n    SOLAR_PANEL(\"solar_panel\", arrayOf(EquipmentSlot.HEAD), 2, { IRItemRegistry.SOLAR_PANEL_MODULE_ITEM }, false, false),\n    FIRE_RESISTANCE(\"fire_resistance\", arrayOf(EquipmentSlot.CHEST),1, { IRItemRegistry.FIRE_RESISTANCE_MODULE_ITEM }, false, false),\n    PIGLIN_TRICKER(\"piglin_tricker\", arrayOf(EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET), 1, { IRItemRegistry.PIGLIN_TRICKER_MODULE_ITEM },false, false),\n    ELYTRA(\"elytra\", arrayOf(EquipmentSlot.CHEST), 1, { IRItemRegistry.ELYTRA_MODULE_ITEM }, false, false),\n    MAGNET(\"magnet\", arrayOf(EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET), 1, { IRItemRegistry.MAGNET_MODULE }, false, false),\n    JETPACK(\"jetpack\", arrayOf(EquipmentSlot.CHEST), 1, { IRItemRegistry.JETPACK_MODULE_ITEM }, false, false),\n    WATER_AFFINITY(\"water_affinity\", arrayOf(EquipmentSlot.CHEST, EquipmentSlot.LEGS), 1, { IRItemRegistry.WATER_AFFINITY_MODULE }, false, false) {\n        override fun getTooltip(stack: ItemStack, tooltip: MutableList<Text>?) {\n            val chestplate = TranslatableText(\"item.indrev.module_water_affinity.on\", TranslatableText(\"item.indrev.module_water_affinity.chestplate\").formatted(Formatting.GOLD))\n            tooltip?.add(chestplate.formatted(Formatting.BLUE, Formatting.ITALIC))\n            tooltip?.add(LiteralText(\"   \")\n                .append(TranslatableText(\"item.indrev.module_water_affinity.tooltip\")\n                    .formatted(Formatting.BLUE, Formatting.ITALIC)))\n            val legs = TranslatableText(\"item.indrev.module_water_affinity.on\", TranslatableText(\"item.indrev.module_water_affinity.leggings\").formatted(Formatting.GOLD))\n            tooltip?.add(legs.formatted(Formatting.BLUE, Formatting.ITALIC))\n            tooltip?.add(LiteralText(\"   \")\n                .append(TranslatableText(\"item.indrev.module_water_affinity.tooltip1\")\n                    .formatted(Formatting.BLUE, Formatting.ITALIC)))\n            tooltip?.add(LiteralText(\" \"))\n            if (Screen.hasShiftDown()) {\n                val maxLevelText = TranslatableText(\n                    \"item.indrev.module_max_level\",\n                    LiteralText(maxLevel.toString()).formatted(Formatting.GOLD)\n                )\n                tooltip?.add(maxLevelText.formatted(Formatting.BLUE))\n            }\n\n            tooltip?.add(TranslatableText(\"item.indrev.module_parts\").formatted(Formatting.BLUE))\n            slots.forEach {\n                tooltip?.add(TranslatableText(\"item.indrev.module_parts_${it.toString().lowercase()}\").formatted(Formatting.GOLD))\n            }\n        }\n                                                                                                                                                  },\n    COLOR(\"color\", arrayOf(EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET), -1, { null },false, false);\n\n    override fun getTooltip(stack: ItemStack, tooltip: MutableList<Text>?) {\n        super.getTooltip(stack, tooltip)\n        tooltip?.add(TranslatableText(\"item.indrev.module_parts\").formatted(Formatting.BLUE))\n        slots.forEach {\n            tooltip?.add(TranslatableText(\"item.indrev.module_parts_${it.toString().lowercase()}\").formatted(Formatting.GOLD))\n        }\n    }\n\n    companion object {\n        val COMPATIBLE: Array<ArmorModule> = values()\n        val COMPATIBLE_HELMET: Array<ArmorModule> = COMPATIBLE.filter { it.slots.contains(EquipmentSlot.HEAD) }.toTypedArray()\n        val COMPATIBLE_CHEST: Array<ArmorModule> = COMPATIBLE.filter { it.slots.contains(EquipmentSlot.CHEST) }.toTypedArray()\n        val COMPATIBLE_LEGS: Array<ArmorModule> = COMPATIBLE.filter { it.slots.contains(EquipmentSlot.LEGS) }.toTypedArray()\n        val COMPATIBLE_BOOTS: Array<ArmorModule> = COMPATIBLE.filter { it.slots.contains(EquipmentSlot.FEET) }.toTypedArray()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/tools/modular/DrillModule.kt",
    "content": "package me.steven.indrev.tools.modular\n\nimport me.steven.indrev.registry.IRItemRegistry\nimport net.minecraft.item.ItemConvertible\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.nbt.NbtHelper\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.math.BlockPos\n\nenum class DrillModule(\n    override val key: String,\n    override val maxLevel: Int,\n    override val item: ItemConvertible\n) : Module {\n    RANGE(\"range\", 5, { IRItemRegistry.RANGE_MODULE_ITEM }),\n    FORTUNE(\"fortune\", 3, { IRItemRegistry.FORTUNE_MODULE_ITEM }),\n    SILK_TOUCH(\"silk_touch\", 1, { IRItemRegistry.SILK_TOUCH_MODULE_ITEM }),\n    CONTROLLED_DESTRUCTION(\"controlled_destruction\", 1, { IRItemRegistry.CONTROLLED_DESTRUCTION_MODULE_ITEM }),\n    MATTER_PROJECTOR(\"matter_projector\", 1, { IRItemRegistry.MATTER_PROJECTOR_MODULE_ITEM });\n\n    override fun getTooltip(stack: ItemStack, tooltip: MutableList<Text>?) {\n        super.getTooltip(stack, tooltip)\n        tooltip?.add(TranslatableText(\"item.indrev.module_parts\").formatted(Formatting.BLUE))\n        tooltip?.add(TranslatableText(\"item.indrev.module_parts_drill\").formatted(Formatting.GOLD))\n    }\n\n    companion object {\n        val COMPATIBLE: Array<Module> = arrayOf(RANGE, FORTUNE, SILK_TOUCH, MiningToolModule.EFFICIENCY, CONTROLLED_DESTRUCTION, MATTER_PROJECTOR)\n\n        fun getBlacklistedPositions(stack: ItemStack): List<BlockPos> {\n            val nbt = stack.nbt ?: return emptyList()\n            if (\n                !nbt.contains(\"BlacklistedPositions\")\n                || (CONTROLLED_DESTRUCTION.getLevel(stack) <= 0 && MATTER_PROJECTOR.getLevel(stack) <= 0)\n                || RANGE.getLevel(stack) <= 0\n            ) return emptyList()\n            val list = nbt.getList(\"BlacklistedPositions\", 10)\n            return list.map { element -> NbtHelper.toBlockPos(element as NbtCompound) }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/tools/modular/GamerAxeModule.kt",
    "content": "package me.steven.indrev.tools.modular\n\nimport me.steven.indrev.registry.IRItemRegistry\nimport net.minecraft.item.ItemConvertible\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\n\nenum class GamerAxeModule(\n    override val key: String,\n    override val maxLevel: Int,\n    override val item: ItemConvertible\n) : Module {\n    LOOTING(\"looting\", 3, { IRItemRegistry.LOOTING_MODULE_ITEM }),\n    FIRE_ASPECT(\"fire_aspect\", 1, { IRItemRegistry.FIRE_ASPECT_MODULE_ITEM }),\n    SHARPNESS(\"sharpness\", 5, { IRItemRegistry.SHARPNESS_MODULE_ITEM }),\n    REACH(\"reach\", 4, { IRItemRegistry.SHARPNESS_MODULE_ITEM }); // NOT IMPLEMENTED\n\n    override fun getTooltip(stack: ItemStack, tooltip: MutableList<Text>?) {\n        super.getTooltip(stack, tooltip)\n        tooltip?.add(TranslatableText(\"item.indrev.module_parts\").formatted(Formatting.BLUE))\n        tooltip?.add(TranslatableText(\"item.indrev.module_parts_gamer_axe\").formatted(Formatting.GOLD))\n    }\n\n    companion object {\n        val COMPATIBLE: Array<Module> = arrayOf(LOOTING, FIRE_ASPECT, SHARPNESS, REACH, MiningToolModule.EFFICIENCY)\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/tools/modular/IRModularItem.kt",
    "content": "package me.steven.indrev.tools.modular\n\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\n\ninterface IRModularItem<T : Module> {\n    fun getCompatibleModules(itemStack: ItemStack): Array<T>\n\n    fun getInstalled(stack: ItemStack): List<T> {\n        val tag = stack.nbt ?: return emptyList()\n        return getCompatibleModules(stack).mapNotNull { module ->\n            if (tag.contains(module.key)) module\n            else null\n        }\n    }\n\n    fun getInstalledTooltip(upgrades: List<Module>, stack: ItemStack, tooltip: MutableList<Text>?) {\n        if (upgrades.isNotEmpty()) {\n            tooltip?.add(TranslatableText(\"item.indrev.modular.upgrade\").formatted(Formatting.GOLD))\n            upgrades.forEach { upgrade ->\n                val level = upgrade.getLevel(stack)\n                val text = TranslatableText(\"item.indrev.modular.upgrade.${upgrade.key}\", level)\n                if (upgrade.getMaxInstalledLevel(stack) != level)\n                    text.formatted(Formatting.ITALIC)\n                tooltip?.add(text.formatted(Formatting.BLUE))\n            }\n        }\n    }\n    fun getCount(stack: ItemStack): Int {\n        return getCompatibleModules(stack).map { module ->\n            val tag = stack.nbt ?: return@map 0\n            if (tag.contains(module.key)) tag.getInt(module.key)\n            else 0\n        }.sum()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/tools/modular/MiningToolModule.kt",
    "content": "package me.steven.indrev.tools.modular\n\nimport me.steven.indrev.registry.IRItemRegistry\nimport net.minecraft.item.ItemConvertible\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\n\nenum class MiningToolModule(\n    override val key: String,\n    override val maxLevel: Int,\n    override val item: ItemConvertible\n) : Module {\n    EFFICIENCY(\"efficiency\", 5, { IRItemRegistry.EFFICIENCY_MODULE_ITEM });\n\n    override fun getTooltip(stack: ItemStack, tooltip: MutableList<Text>?) {\n        super.getTooltip(stack, tooltip)\n        tooltip?.add(TranslatableText(\"item.indrev.module_parts\").formatted(Formatting.BLUE))\n        tooltip?.add(TranslatableText(\"item.indrev.module_parts_drill\").formatted(Formatting.GOLD))\n        tooltip?.add(TranslatableText(\"item.indrev.module_parts_gamer_axe\").formatted(Formatting.GOLD))\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/tools/modular/Module.kt",
    "content": "package me.steven.indrev.tools.modular\n\nimport net.minecraft.client.gui.screen.Screen\nimport net.minecraft.item.ItemConvertible\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\n\ninterface Module {\n    val key: String\n    val maxLevel: Int\n    val item: ItemConvertible\n\n    fun isInstalled(itemStack: ItemStack): Boolean {\n        return !itemStack.isEmpty && itemStack.orCreateNbt.contains(key)\n    }\n\n    fun getLevel(itemStack: ItemStack): Int {\n        if (itemStack.isEmpty) return 0\n        val tag = itemStack.getOrCreateSubNbt(\"selected\")\n        return if (tag.contains(key)) tag.getInt(key) else getMaxInstalledLevel(itemStack)\n    }\n\n    fun getMaxInstalledLevel(itemStack: ItemStack): Int {\n        if (itemStack.isEmpty) return 0\n        val tag = itemStack.orCreateNbt\n        return if (tag.contains(key)) tag.getInt(key) else 0\n    }\n\n    fun getTooltip(stack: ItemStack, tooltip: MutableList<Text>?) {\n        val titleText = TranslatableText(\"item.indrev.module_${key}.tooltip\")\n        tooltip?.add(titleText.formatted(Formatting.BLUE, Formatting.ITALIC))\n        tooltip?.add(LiteralText(\" \"))\n        if (Screen.hasShiftDown()) {\n            if (this != ArmorModule.COLOR) {\n                val maxLevelText = TranslatableText(\"item.indrev.module_max_level\", LiteralText(maxLevel.toString()).formatted(Formatting.GOLD))\n                tooltip?.add(maxLevelText.formatted(Formatting.BLUE))\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/EmptyModel.kt",
    "content": "package me.steven.indrev.utils\n\nimport com.mojang.datafixers.util.Pair\nimport net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel\nimport net.fabricmc.fabric.api.renderer.v1.render.RenderContext\nimport net.minecraft.block.BlockState\nimport net.minecraft.client.render.model.*\nimport net.minecraft.client.render.model.json.ModelOverrideList\nimport net.minecraft.client.render.model.json.ModelTransformation\nimport net.minecraft.client.texture.Sprite\nimport net.minecraft.client.util.SpriteIdentifier\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.BlockRenderView\nimport java.util.*\nimport java.util.function.Function\nimport java.util.function.Supplier\n\nobject EmptyModel : UnbakedModel, BakedModel, FabricBakedModel {\n    override fun getModelDependencies(): MutableCollection<Identifier> = mutableListOf()\n\n    override fun getTextureDependencies(\n        unbakedModelGetter: Function<Identifier, UnbakedModel>?,\n        unresolvedTextureReferences: MutableSet<Pair<String, String>>?\n    ): MutableCollection<SpriteIdentifier> = mutableListOf()\n\n    override fun bake(\n        loader: ModelLoader?,\n        textureGetter: Function<SpriteIdentifier, Sprite>?,\n        rotationContainer: ModelBakeSettings?,\n        modelId: Identifier?\n    ): BakedModel = this\n\n    override fun getQuads(state: BlockState?, face: Direction?, random: Random?): MutableList<BakedQuad> = mutableListOf()\n\n    override fun useAmbientOcclusion(): Boolean = false\n\n    override fun hasDepth(): Boolean = false\n\n    override fun isSideLit(): Boolean = false\n\n    override fun isBuiltin(): Boolean = false\n\n    override fun getParticleSprite(): Sprite? = null\n\n    override fun getTransformation(): ModelTransformation = ModelTransformation.NONE\n\n    override fun getOverrides(): ModelOverrideList = ModelOverrideList.EMPTY\n\n    override fun isVanillaAdapter(): Boolean = false\n\n    override fun emitBlockQuads(\n        blockView: BlockRenderView?,\n        state: BlockState?,\n        pos: BlockPos?,\n        randomSupplier: Supplier<Random>?,\n        context: RenderContext?\n    ) {\n    }\n\n    override fun emitItemQuads(stack: ItemStack?, randomSupplier: Supplier<Random>?, context: RenderContext?) {\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/EnergyDamageHandler.kt",
    "content": "package me.steven.indrev.utils\n\nimport net.fabricmc.fabric.api.item.v1.CustomDamageHandler\nimport net.minecraft.entity.LivingEntity\nimport net.minecraft.item.ItemStack\nimport java.util.function.Consumer\n\nobject EnergyDamageHandler : CustomDamageHandler {\n    override fun damage(stack: ItemStack, amount: Int, entity: LivingEntity?, breakCallback: Consumer<LivingEntity>?): Int {\n        /*val itemIo = energyOf(stack)\n        Transaction.openOuter().use {\n            itemIo?.extract(amount.toLong(), it)\n            it.commit()\n        }\n        return 0*/\n        return 0//TODO figure thsi out\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/IRFluidTank.kt",
    "content": "package me.steven.indrev.utils\n\nimport alexiil.mc.lib.attributes.fluid.amount.FluidAmount\nimport alexiil.mc.lib.attributes.fluid.render.FluidRenderFace\nimport alexiil.mc.lib.attributes.fluid.volume.FluidKeys\nimport me.steven.indrev.components.DefaultSyncableObject\nimport me.steven.indrev.components.FluidComponent\nimport me.steven.indrev.components.SyncableObject\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.api.Environment\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.base.ResourceAmount\nimport net.fabricmc.fabric.api.transfer.v1.storage.base.SingleSlotStorage\nimport net.fabricmc.fabric.api.transfer.v1.storage.base.SingleVariantStorage\nimport net.fabricmc.fabric.api.transfer.v1.transaction.Transaction\nimport net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext\nimport net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant\nimport net.minecraft.client.render.VertexConsumerProvider\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.network.PacketByteBuf\n\nclass IRFluidTank(val index: Int, val component: () -> FluidComponent) : SingleVariantStorage<FluidVariant>(), SyncableObject by DefaultSyncableObject() {\n\n    val isEmpty: Boolean get() = variant.isBlank || amount == 0L\n    val exposed = ExposedIRFluidTank()\n\n    override fun getCapacity(variant: FluidVariant): Long = component().getTankCapacity(index)\n\n    override fun getBlankVariant(): FluidVariant = FluidVariant.blank()\n\n    fun toTag(): NbtCompound {\n        val nbt = NbtCompound()\n        nbt.put(\"variant\", variant.toNbt())\n        nbt.putLong(\"amt\", amount)\n        return nbt\n    }\n\n    fun render(faces: List<FluidRenderFace?>?, vcp: VertexConsumerProvider?, matrices: MatrixStack?) {\n        if (!variant.isBlank)\n            FluidKeys.get(variant.fluid).withAmount(FluidAmount.BUCKET).render(faces, vcp, matrices)\n    }\n\n    fun renderGuiRect(x0: Double, y0: Double, x1: Double, y1: Double) {\n        if (!variant.isBlank)\n            FluidKeys.get(variant.fluid).withAmount(FluidAmount.BUCKET).renderGuiRect(x0, y0, x1, y1)\n    }\n\n    override fun onFinalCommit() {\n        super.onFinalCommit()\n        component().syncable().markForUpdate()\n        markDirty()\n    }\n\n    fun extract(amount: Long, act: Boolean = false): Long {\n        Transaction.openOuter().use {\n            val extracted = extract(variant, amount, it)\n            if (act) it.commit() else it.abort()\n            return extracted\n        }\n    }\n\n    fun tryExtract(amount: Long): Boolean {\n        Transaction.openOuter().use {\n            val extracted = extract(variant, amount, it)\n            it.abort()\n            return extracted == amount\n        }\n    }\n\n    fun insert(variant: FluidVariant, amount: Long, act: Boolean = false): Long {\n        if (!this.variant.isBlank && variant != this.variant) return 0\n        Transaction.openOuter().use {\n            val inserted = insert(variant, amount, it)\n            if (act) it.commit() else it.abort()\n            return inserted\n        }\n    }\n\n    fun tryInsert(variant: FluidVariant, amount: Long): Boolean {\n        if (!this.variant.isBlank && variant != this.variant) return false\n        Transaction.openOuter().use {\n            val inserted = insert(variant, amount, it)\n            it.abort()\n            return inserted == amount\n        }\n    }\n\n    override fun toPacket(buf: PacketByteBuf) {\n        resource.toPacket(buf)\n        buf.writeLong(amount)\n    }\n\n    @Environment(EnvType.CLIENT)\n    override fun fromPacket(buf: PacketByteBuf) {\n        this.variant = FluidVariant.fromPacket(buf)\n        this.amount = buf.readLong()\n    }\n\n    fun fromTag(nbt: NbtCompound) {\n        this.variant = FluidVariant.fromNbt(nbt.getCompound(\"variant\"))\n        this.amount = nbt.getLong(\"amt\")\n    }\n\n    inner class ExposedIRFluidTank : SnapshotParticipant<ResourceAmount<FluidVariant>>(), SingleSlotStorage<FluidVariant> by this {\n        override fun createSnapshot(): ResourceAmount<FluidVariant> = this@IRFluidTank.createSnapshot()\n\n        override fun readSnapshot(snapshot: ResourceAmount<FluidVariant>) = this@IRFluidTank.readSnapshot(snapshot)\n\n        override fun insert(resource: FluidVariant, maxAmount: Long, transaction: TransactionContext): Long {\n            return if (component().inputTanks.contains(index) && component().isFluidValidForTank(index, resource))\n                this@IRFluidTank.insert(resource, maxAmount, transaction)\n            else 0\n        }\n\n        override fun extract(resource: FluidVariant, maxAmount: Long, transaction: TransactionContext): Long {\n            return if (component().outputTanks.contains(index))\n                this@IRFluidTank.extract(resource, maxAmount, transaction)\n            else 0\n        }\n\n        override fun onFinalCommit() = this@IRFluidTank.onFinalCommit()\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/ReusableArrayDeque.kt",
    "content": "package me.steven.indrev.utils\n\n/**\n * Limited simple re-implementation of a reusable ArrayDeque\n * When polling, instead of reducing size and setting elements on the array to null, it will just increase the head\n * When finished, you can reset the head and start again\n * Used by IR's networks\n */\nclass ReusableArrayDeque<E : Comparable<E>>(elements: Collection<E>) : AbstractMutableList<E>() {\n    private var head: Int = 0\n    var elementData: Array<Any?>\n\n    override var size: Int = 0\n        private set\n\n    init {\n        elementData = elements.toTypedArray()\n        size = elementData.size\n        if (elementData.isEmpty()) elementData = emptyElementData\n    }\n\n    inline fun apply(transform: (Array<Any?>) -> Unit) {\n        @Suppress(\"UNCHECKED_CAST\")\n        transform(elementData)\n    }\n\n    override fun add(index: Int, element: E) = throw NotImplementedError()\n\n    override fun removeAt(index: Int): E = throw NotImplementedError()\n\n    override fun set(index: Int, element: E): E = throw NotImplementedError()\n\n    override fun get(index: Int): E {\n        checkElementIndex(index, size)\n\n        return internalGet(internalIndex(index))\n    }\n\n    fun removeFirst(): E {\n        if (isEmpty()) throw NoSuchElementException(\"ArrayDeque is empty.\")\n\n        val element = internalGet(head)\n        head = incremented(head)\n        return element\n    }\n\n    private fun incremented(index: Int): Int = index + 1\n\n    private fun internalGet(internalIndex: Int): E {\n        @Suppress(\"UNCHECKED_CAST\")\n        return elementData[internalIndex] as E\n    }\n\n    private fun internalIndex(index: Int): Int = positiveMod(head + index)\n\n    private fun positiveMod(index: Int): Int = if (index >= elementData.size) index - elementData.size else index\n\n    fun resetHead() {\n        head = 0\n    }\n\n    override fun isEmpty(): Boolean = head >= size\n\n    companion object {\n        private val emptyElementData = emptyArray<Any?>()\n\n        fun checkElementIndex(index: Int, size: Int) {\n            if (index < 0 || index >= size) {\n                throw IndexOutOfBoundsException(\"index: $index, size: $size\")\n            }\n        }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/accessorextensions.kt",
    "content": "package me.steven.indrev.utils\n\nimport it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap\nimport me.steven.indrev.api.ServerWorldExtension\nimport me.steven.indrev.networks.energy.EnergyNetworkState\nimport me.steven.indrev.networks.fluid.FluidNetworkState\nimport me.steven.indrev.networks.item.ItemNetworkState\nimport net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.Direction\nimport team.reborn.energy.api.EnergyStorage\n\nval ServerWorld.energyIoCache: Long2ObjectOpenHashMap<BlockApiCache<EnergyStorage, Direction>>\n    get() = (this as ServerWorldExtension).indrev_getEnergyCache()\n\nval ServerWorld.energyNetworkState: EnergyNetworkState\n    get() = (this as ServerWorldExtension).indrev_getEnergyNetworkState()\n\nval ServerWorld.fluidNetworkState: FluidNetworkState\n    get() = (this as ServerWorldExtension).indrev_getFluidNetworkState()\n\nval ServerWorld.itemNetworkState: ItemNetworkState\n    get() = (this as ServerWorldExtension).indrev_getItemNetworkState()"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/clientutils.kt",
    "content": "package me.steven.indrev.utils\n\nimport com.mojang.blaze3d.systems.RenderSystem\nimport io.github.cottonmc.cotton.gui.client.BackgroundPainter\nimport io.github.cottonmc.cotton.gui.client.ScreenDrawing\nimport me.steven.indrev.config.BasicMachineConfig\nimport me.steven.indrev.config.GeneratorConfig\nimport me.steven.indrev.config.HeatMachineConfig\nimport me.steven.indrev.config.LFCConfig\nimport net.fabricmc.api.EnvType\nimport net.fabricmc.loader.api.FabricLoader\nimport net.minecraft.client.gui.screen.Screen\nimport net.minecraft.client.option.KeyBinding\nimport net.minecraft.client.render.*\nimport net.minecraft.client.util.math.MatrixStack\nimport net.minecraft.item.ItemStack\nimport net.minecraft.text.LiteralText\nimport net.minecraft.text.Text\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.Formatting\nimport net.minecraft.util.Identifier\nimport kotlin.math.atan2\n\nval isClient = FabricLoader.getInstance().environmentType == EnvType.CLIENT\n\nval UPGRADE_SLOT_PANEL_PAINTER: BackgroundPainter = BackgroundPainter.createLightDarkVariants(\n    BackgroundPainter.createNinePatch(Identifier(\"libgui\", \"textures/widget/panel_light.png\")).setPadding(4),\n    BackgroundPainter.createNinePatch(Identifier(\"libgui\", \"textures/widget/panel_dark.png\")).setPadding(4)\n)\n\nfun draw2Colors(matrices: MatrixStack, x1: Int, y1: Int, x2: Int, y2: Int, color1: Long, color2: Long) {\n    val matrix = matrices.peek().positionMatrix\n\n    var j: Int\n    var xx1 = x1.toFloat()\n    var xx2 = x2.toFloat()\n    var yy1 = x1.toFloat()\n    var yy2 = x2.toFloat()\n\n    if (x1 < x2) {\n        j = x1\n        xx1 = x2.toFloat()\n        xx2 = j.toFloat()\n    }\n\n    if (y1 < y2) {\n        j = y1\n        yy1 = y2.toFloat()\n        yy2 = j.toFloat()\n    }\n\n    val f1 = (color1 shr 24 and 255) / 255.0f\n    val g1 = (color1 shr 16 and 255) / 255.0f\n    val h1 = (color1 shr 8 and 255) / 255.0f\n    val k1 = (color1 and 255) / 255.0f\n\n    val f2 = (color2 shr 24 and 255) / 255.0f\n    val g2 = (color2 shr 16 and 255) / 255.0f\n    val h2 = (color2 shr 8 and 255) / 255.0f\n    val k2 = (color2 and 255) / 255.0f\n\n    RenderSystem.setShader { GameRenderer.getPositionColorShader() }\n    RenderSystem.enableBlend()\n    RenderSystem.disableTexture()\n    RenderSystem.defaultBlendFunc()\n    Tessellator.getInstance().buffer.run {\n        begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR)\n        vertex(matrix, xx1, yy1, 0.0f).color(g1, h1, k1, f1).next()\n        vertex(matrix, xx1, yy2, 0.0f).color(g1, h1, k1, f1).next()\n        vertex(matrix, xx2, yy2, 0.0f).color(g1, h1, k1, f1).next()\n        vertex(matrix, xx1, yy1, 0.0f).color(g1, h1, k1, f1).next()\n        end()\n        BufferRenderer.draw(this)\n        begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR)\n        vertex(matrix, xx1, yy1, 0.0f).color(g2, h2, k2, f2).next()\n        vertex(matrix, xx2, yy2, 0.0f).color(g2, h2, k2, f2).next()\n        vertex(matrix, xx2, yy1, 0.0f).color(g2, h2, k2, f2).next()\n        vertex(matrix, xx1, yy1, 0.0f).color(g2, h2, k2, f2).next()\n        end()\n        BufferRenderer.draw(this)\n    }\n    RenderSystem.enableTexture()\n    RenderSystem.disableBlend()\n}\n\nfun drawCircle(matrices: MatrixStack, value: Int, max: Int, x: Int, y: Int, width: Int, colorProvider: (Int, Int) -> Int) {\n    val maxRadius = width / 2 - 1\n    val minRadius = maxRadius - 3\n\n    val maxAngle = value * 360 / max.toDouble()\n\n    for (xOffset in -maxRadius until maxRadius) {\n        for (yOffset in -maxRadius until maxRadius) {\n            val squaredDist = xOffset * xOffset + yOffset * yOffset\n            val angle = Math.toDegrees(atan2(-xOffset.toDouble(), yOffset.toDouble())) + 180\n\n            if (squaredDist >= minRadius * minRadius && squaredDist < maxRadius * maxRadius && angle < maxAngle) {\n                val color = colorProvider(x + xOffset, y + yOffset)\n                ScreenDrawing.coloredRect(matrices, x + xOffset + width / 2, y + yOffset + width / 2, 1, 1, color)\n            }\n\n        }\n    }\n}\n\nfun buildEnergyTooltip(stack: ItemStack?, tooltip: MutableList<Text>?) {\n    val handler = energyOf(stack) ?: return\n    if (handler.amount > 0) {\n        val percentage = handler.amount * 100 / handler.capacity\n        tooltip?.add(LiteralText(\"${getEnergyString(handler.amount)} LF (${percentage.toInt()}%)\").formatted(Formatting.GRAY))\n    }\n}\n\nfun buildMachineTooltip(config: Any, tooltip: MutableList<Text>?) {\n    if (Screen.hasShiftDown()) {\n        when (config) {\n            is HeatMachineConfig -> {\n                tooltip?.add(configText(\"maxInput\", \"lftick\", config.maxInput))\n                tooltip?.add(configText(\"maxEnergyStored\", \"lf\", getEnergyString(config.maxEnergyStored)))\n                tooltip?.add(configText(\"energyCost\", \"lftick\", config.energyCost))\n                val speed = config.processSpeed * 100\n                if (speed >= 1000)\n                    tooltip?.add(configText(\"processSpeed\", \"seconds\", config.processSpeed / 20))\n                else\n                    tooltip?.add(configText(\"processSpeed\", \"${speed.toInt()}%\"))\n                tooltip?.add(configText(\"temperatureBoost\", \"${config.processTemperatureBoost * config.processSpeed * 100}%\"))\n            }\n            is BasicMachineConfig -> {\n                tooltip?.add(configText(\"maxInput\", \"lftick\", config.maxInput))\n                tooltip?.add(configText(\"maxEnergyStored\", \"lf\", getEnergyString(config.maxEnergyStored)))\n                tooltip?.add(configText(\"energyCost\", \"lftick\", config.energyCost))\n                val speed = config.processSpeed * 100\n                if (speed >= 1000)\n                    tooltip?.add(configText(\"processSpeed\", \"seconds\", config.processSpeed / 20))\n                else\n                    tooltip?.add(configText(\"processSpeed\", \"${speed.toInt()}%\"))\n            }\n            is GeneratorConfig -> {\n                tooltip?.add(configText(\"maxOutput\", \"lftick\", config.maxOutput))\n                tooltip?.add(configText(\"maxEnergyStored\", \"lf\", getEnergyString(config.maxEnergyStored)))\n                tooltip?.add(configText(\"ratio\", \"lftick\", config.ratio))\n                if (config.temperatureBoost > 0)\n                    tooltip?.add(configText(\"temperatureBoost\", \"lftick\", config.temperatureBoost * config.ratio))\n            }\n            is LFCConfig -> {\n                tooltip?.add(configText(\"maxInput\", \"lftick\", config.maxInput))\n                tooltip?.add(configText(\"maxOutput\", \"lftick\", config.maxOutput))\n                tooltip?.add(configText(\"maxEnergyStored\", \"lf\", getEnergyString(config.maxEnergyStored)))\n            }\n        }\n    } else {\n        tooltip?.add(\n            TranslatableText(\"gui.indrev.tooltip.press_shift\", LiteralText(\"\").append(KeyBinding.getLocalizedName(\"key.keyboard.left.shift\").get()).formatted(Formatting.AQUA)).formatted(Formatting.GRAY)\n        )\n    }\n}\n\nprivate fun configText(key: String, value: String): Text {\n    return TranslatableText(\"gui.indrev.tooltip.$key\").formatted(Formatting.AQUA)\n        .append(LiteralText(value).formatted(Formatting.GRAY))\n}\n\nprivate fun configText(key: String, unit: String, value: Any): Text {\n    return TranslatableText(\"gui.indrev.tooltip.$key\").formatted(Formatting.AQUA)\n        .append(TranslatableText(\"gui.indrev.tooltip.$unit\", value).formatted(Formatting.GRAY))\n}\n\nfun getEnergyString(energy: Long): String =\n    when {\n        energy >= 1000000000000 -> \"${\"%.1f\".format(energy / 1000000000000f)}T\"\n        energy >= 1000000000 -> \"${\"%.1f\".format(energy / 1000000000f)}B\"\n        energy >= 1000000 -> \"${\"%.1f\".format(energy / 1000000f)}M\"\n        energy >= 1000 -> \"${\"%.1f\".format(energy / 1000f)}k\"\n        else -> \"%.1f\".format(energy.toFloat())\n    }"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/energyutils.kt",
    "content": "package me.steven.indrev.utils\n\nimport net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache\nimport net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext\nimport net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage\nimport net.fabricmc.fabric.api.transfer.v1.transaction.Transaction\nimport net.minecraft.inventory.Inventory\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport team.reborn.energy.api.EnergyStorage\nimport java.util.function.LongFunction\n\nfun energyOf(world: ServerWorld, blockPos: BlockPos, direction: Direction): EnergyStorage? {\n    return world.energyIoCache.computeIfAbsent(\n        blockPos.asLong(),\n        LongFunction { BlockApiCache.create(EnergyStorage.SIDED, world, blockPos) }).find(direction)\n}\n\nfun energyOf(itemStack: ItemStack?): EnergyStorage? {\n    return if (itemStack == null || itemStack.isEmpty) null\n    else EnergyStorage.ITEM.find(itemStack, ContainerItemContext.withInitial(itemStack))\n}\n\nfun energyOf(inv: Inventory?, slot: Int): EnergyStorage? {\n    val itemStack = inv?.getStack(slot)\n    return if (itemStack == null || itemStack.isEmpty) null\n    else EnergyStorage.ITEM.find(itemStack, ContainerItemContext.ofSingleSlot(InventoryStorage.of(inv, null).getSlot(slot)))\n}\n\nfun extract(inv: Inventory?, slot: Int, amount: Long): Boolean {\n    return energyOf(inv, slot)?.use(amount) == true\n}\n\nfun insert(inv: Inventory?, slot: Int, amount: Long): Boolean {\n    val e = energyOf(inv, slot) ?: return false\n    Transaction.openOuter().use { t ->\n        val inserted = e.insert(amount, t)\n        if (inserted == amount) {\n            t.commit()\n            return true\n        }\n        return false\n    }\n}\n\nfun EnergyStorage.use(amount: Long): Boolean {\n    Transaction.openOuter().use { t ->\n        val extracted = this.extract(amount, t)\n        if (extracted == amount) {\n            t.commit()\n            return true\n        }\n        return false\n    }\n}\n\nfun EnergyStorage.insert(amount: Long, act: Boolean): Long {\n    Transaction.openOuter().use { t ->\n        val inserted = this.insert(amount, t)\n        if (act) {\n            t.commit()\n        }\n        return inserted\n    }\n}\n\nfun EnergyStorage.extract(amount: Long, act: Boolean): Long {\n    Transaction.openOuter().use { t ->\n        val extracted = this.extract(amount, t)\n        if (extracted == amount && act) {\n            t.commit()\n        }\n        return extracted\n    }\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/fluidutils.kt",
    "content": "package me.steven.indrev.utils\n\nimport alexiil.mc.lib.attributes.fluid.amount.FluidAmount\nimport alexiil.mc.lib.attributes.fluid.volume.FluidKeys\nimport it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap\nimport net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry\nimport net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.Storage\nimport net.fabricmc.fabric.api.transfer.v1.storage.base.ResourceAmount\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.Blocks\nimport net.minecraft.block.FluidBlock\nimport net.minecraft.client.gui.screen.Screen\nimport net.minecraft.fluid.Fluid\nimport net.minecraft.fluid.Fluids\nimport net.minecraft.item.ItemStack\nimport net.minecraft.network.PacketByteBuf\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.text.OrderedText\nimport net.minecraft.text.Style\nimport net.minecraft.text.TranslatableText\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.registry.Registry\nimport net.minecraft.world.World\nimport java.util.*\nimport java.util.function.LongFunction\n\n\n//these are just for reference, they're not used much\n\nval bucket = 81000L\n\nval bottle = bucket / 3 // 27000 droplets\n\nval half_bucket = bucket / 2 // 40500 droplets\n\nval block = bucket\nval ingot = block / 9 // 9000 droplets\nval nugget = ingot / 9 // 1000 droplets\nval scrap = ingot / 4 // 250 droplets\n\nval mb = bucket / 1000\n\nfun FluidBlock.drainFluid(world: World, pos: BlockPos, state: BlockState): Fluid {\n    return if (state.get(FluidBlock.LEVEL) as Int == 0) {\n        world.setBlockState(pos, Blocks.AIR.defaultState, 11)\n        fluid\n    } else {\n        Fluids.EMPTY\n    }\n}\n\n//TODO fuck weakhashmaps\nval fluidApiCache = WeakHashMap<World, Long2ObjectOpenHashMap<BlockApiCache<Storage<FluidVariant>, Direction>>>()\n\nfun fluidStorageOf(world: ServerWorld, blockPos: BlockPos, direction: Direction): Storage<FluidVariant>? {\n    return fluidApiCache\n        .computeIfAbsent(world) { Long2ObjectOpenHashMap() }\n        .computeIfAbsent(blockPos.asLong(), LongFunction { BlockApiCache.create(FluidStorage.SIDED, world, blockPos) })\n        .find(direction)\n}\n\n\nfun fluidStorageOf(world: World, blockPos: BlockPos, direction: Direction): Storage<FluidVariant>? {\n    if (world is ServerWorld)\n        return fluidStorageOf(world, blockPos, direction)\n    else return FluidStorage.SIDED.find(world, blockPos, direction)\n}\n\nfun fluidStorageOf(itemStack: ItemStack?): Storage<FluidVariant>? {\n    return if (itemStack == null) null\n    else FluidStorage.ITEM.find(itemStack, null)\n}\n\ntypealias IRFluidAmount = ResourceAmount<FluidVariant>\n\nfun IRFluidAmount.toPacket(buf: PacketByteBuf) {\n    resource.toPacket(buf)\n    buf.writeLong(amount)\n}\n\nfun IRFluidAmount.renderGuiRect(x0: Double, y0: Double, x1: Double, y1: Double) {\n    FluidKeys.get(resource.fluid).withAmount(FluidAmount.BUCKET).renderGuiRect(x0, y0, x1, y1)\n}\n\nfun fromPacket(buf: PacketByteBuf): IRFluidAmount {\n    val res = FluidVariant.fromPacket(buf)\n    val amt = buf.readLong()\n    return amt of res\n}\n\ninfix fun Long.of(variant: FluidVariant) = IRFluidAmount(variant, this)\n\nfun getTooltip(variant: FluidVariant, amount: Long, capacity: Long): List<OrderedText> {\n    val tooltips = mutableListOf<OrderedText>()\n    val id = Registry.BLOCK.getId(variant.fluid.defaultState.blockState.block)\n    val color = FluidRenderHandlerRegistry.INSTANCE.get(variant.fluid)?.getFluidColor(null, null, variant.fluid.defaultState) ?: -1\n\n    tooltips.add(TranslatableText(\"block.${id.namespace}.${id.path}\").setStyle(Style.EMPTY.withColor(color)).asOrderedText())\n\n    val asMb = amount / 81\n    val accurate = amount / 81.0\n    val prefix = when {\n        accurate > asMb -> \">\"\n        accurate < asMb -> \"<\"\n        else -> \"\"\n    }\n    if (capacity > 0)\n        tooltips.add(TranslatableText(\"$prefix$asMb / ${capacity / 81} mB\").asOrderedText())\n    else\n        tooltips.add(TranslatableText(\"$prefix$asMb mB\").asOrderedText())\n\n    if (Screen.hasShiftDown()) {\n        if (capacity > 0)\n            tooltips.add(TranslatableText(\"$amount / $capacity droplets\").asOrderedText())\n        else\n            tooltips.add(TranslatableText(\"$amount droplets\").asOrderedText())\n    }\n    \n    return tooltips\n\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/helperextensions.kt",
    "content": "package me.steven.indrev.utils\n\nimport me.steven.indrev.api.IREntityExtension\nimport me.steven.indrev.inventories.IRInventory\nimport net.minecraft.entity.Entity\nimport net.minecraft.entity.effect.StatusEffectCategory\nimport net.minecraft.item.FoodComponent\nimport net.minecraft.item.Item\nimport net.minecraft.item.ItemStack\nimport net.minecraft.util.collection.WeightedList\nimport net.minecraft.util.math.*\nimport net.minecraft.util.thread.ThreadExecutor\nimport net.minecraft.world.World\nimport java.util.concurrent.CompletableFuture\n\nfun World.isLoaded(pos: BlockPos): Boolean {\n    return chunkManager.isChunkLoaded(pos.x shr 4, pos.z shr 4)\n}\n\nfun <E> WeightedList<E>.pickRandom(): E {\n    return this.shuffle().entries.first().element\n}\n\nfun FoodComponent.hasNegativeEffects(): Boolean {\n    return statusEffects.any { it.first.effectType.category == StatusEffectCategory.HARMFUL }\n}\n\ninline fun Entity.redirectDrops(inv: IRInventory, run: () -> Unit) {\n    this as IREntityExtension\n    this.machineInv = inv\n    run()\n    this.machineInv = null\n}\n\nfun BlockPos.toVec3d() = Vec3d(x.toDouble(), y.toDouble(), z.toDouble())\n\nfun <V> ThreadExecutor<*>.submitAndGet(task: () -> V): V {\n    return (if (!this.isOnThread)\n        CompletableFuture.supplyAsync(task, this)\n    else\n        CompletableFuture.completedFuture(task())).get()\n}\n\ninline fun Box.any(f: (Int, Int, Int) -> Boolean): Boolean {\n    for (x in minX.toInt()..maxX.toInt())\n        for (y in minY.toInt()..maxY.toInt())\n            for (z in minZ.toInt()..maxZ.toInt())\n                if (f(x, y, z)) return true\n    return false\n}\n\ninline fun Box.forEach(f: (Int, Int, Int) -> Unit) {\n    for (x in minX.toInt() until maxX.toInt())\n        for (y in minY.toInt() until maxY.toInt())\n            for (z in minZ.toInt() until maxZ.toInt())\n                f(x, y, z)\n}\n\ninline fun <T> Box.map(f: (Int, Int, Int) -> T): MutableList<T> {\n    val list = ArrayList<T>((xLength * yLength * zLength).toInt())\n    for (x in minX.toInt() until maxX.toInt())\n        for (y in minY.toInt() until maxY.toInt())\n            for (z in minZ.toInt() until maxZ.toInt())\n                list.add(f(x, y, z))\n    return list\n}\n\noperator fun Box.contains(pos: BlockPos): Boolean {\n    return contains(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble())\n}\n\ninline fun Box.firstOrNull(f: (Int, Int, Int) -> Boolean): BlockPos? {\n    for (x in minX.toInt()..maxX.toInt())\n        for (y in minY.toInt()..maxY.toInt())\n            for (z in minZ.toInt()..maxZ.toInt())\n                if (f(x, y, z)) return BlockPos(x, y, z)\n    return null\n}\n\noperator fun Vec3i.component1() = x\noperator fun Vec3i.component2() = y\noperator fun Vec3i.component3() = z\n\noperator fun Vec3d.component1() = this.x\noperator fun Vec3d.component2() = this.y\noperator fun Vec3d.component3() = this.z\n\noperator fun Vec3f.component1() = this.x\noperator fun Vec3f.component2() = this.y\noperator fun Vec3f.component3() = this.z\n\noperator fun ItemStack.component1(): ItemStack = this\noperator fun ItemStack.component2(): Item = item\n\nfun <T> Collection<T>.asMutableList(): MutableList<T> {\n    return this as? MutableList<T> ?: ArrayList(this)\n}\n"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/hiddenitems.kt",
    "content": "package me.steven.indrev.utils\n\nimport net.minecraft.util.Identifier\n\nval hiddenIds = arrayOf(\n    \"heliostat\",\n    \"resistant_glass\",\n    \"solar_receiver\",\n    \"fluid_valve\",\n    \"molten_salt\",\n    \"steam_turbine_mk4\",\n    \"steam_turbine_steam_input_valve\",\n    \"steam_turbine_energy_output\",\n    \"steam_turbine_casing\",\n    \"steam_turbine_rotor\",\n    \"steam_turbine_pressure_valve\",\n    \"solar_power_plant_tower\",\n    \"solar_power_plant_smelter\",\n    \"boiler\",\n    \"steam\",\n    \"carbon_fiber_plate\",\n    \"carbon_fiber_rod\",\n    \"salt\",\n    \"carbon_fiber_helmet_frame\",\n    \"carbon_fiber_chest_frame\",\n    \"carbon_fiber_legs_frame\",\n    \"carbon_fiber_boots_frame\",\n    \"module_controlled_destruction\",\n    \"module_matter_projector\",\n    \"biomass_composter\",\n    \"hydrogen_bucket\",\n    \"oxygen_bucket\",\n    \"methane_bucket\",\n    \"steam_bucket\",\n    \"molten_salt_bucket\",\n    \"jetpack_mk1\",\n    \"jetpack_mk2\",\n    \"jetpack_mk3\",\n    \"jetpack_mk4\",\n    \"distiller_mk4\",\n    \"module_magnet\",\n    \"gas_generator_mk4\",\n    \"dirt_oxygenator_mk1\",\n    \"soot\",\n    \"electrolytic_separator_mk1\",\n    \"electrolytic_separator_mk2\",\n    \"electrolytic_separator_mk3\",\n    \"electrolytic_separator_mk4\",\n    \"electrolytic_separator_creative\",\n    \"module_water_affinity\",\n    \"module_jetpack\",\n    \"reinforced_elytra\",\n    \"module_reinforced_elytra\"\n)\n\nfun hide(id: Identifier) = id.namespace == \"indrev\" && hiddenIds.contains(id.path)"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/interactions.kt",
    "content": "package me.steven.indrev.utils\n\nimport it.unimi.dsi.fastutil.longs.LongOpenHashSet\nimport me.steven.indrev.api.sideconfigs.ConfigurationType\nimport me.steven.indrev.api.sideconfigs.SideConfiguration\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport me.steven.indrev.blocks.HeliostatBlock\nimport me.steven.indrev.blocks.machine.MachineBlock\nimport me.steven.indrev.gui.ScrewdriverScreenHandlerFactory\nimport me.steven.indrev.gui.screenhandlers.wrench.ScrewdriverScreenHandler\nimport me.steven.indrev.registry.IRBlockRegistry\nimport net.minecraft.block.BlockState\nimport net.minecraft.block.entity.BlockEntity\nimport net.minecraft.entity.player.PlayerEntity\nimport net.minecraft.item.ItemStack\nimport net.minecraft.nbt.NbtLong\nimport net.minecraft.text.LiteralText\nimport net.minecraft.util.ActionResult\nimport net.minecraft.util.BlockRotation\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.world.World\nimport java.util.*\n\nfun wrench(\n    world: World,\n    pos: BlockPos,\n    blockState: BlockState,\n    blockEntity: BlockEntity?,\n    player: PlayerEntity?,\n    stack: ItemStack\n): ActionResult {\n    val block = blockState.block\n    if (player?.isSneaking == true && block is MachineBlock) {\n        block.writeNbtComponents(world, player, pos, blockState, blockEntity, stack)\n        world.breakBlock(pos, false, player)\n    } else {\n        val rotated = blockState.rotate(BlockRotation.CLOCKWISE_90)\n        if (rotated == blockState) return ActionResult.PASS\n        world.setBlockState(pos, rotated)\n    }\n    return ActionResult.success(world.isClient)\n}\n\nfun screwdriver(\n    world: World,\n    pos: BlockPos,\n    blockState: BlockState,\n    blockEntity: BlockEntity?,\n    player: PlayerEntity?,\n    stack: ItemStack\n): ActionResult {\n    if (blockState.isOf(IRBlockRegistry.HELIOSTAT_BLOCK)) {\n        val positions = LongOpenHashSet()\n        positions.add(pos.asLong())\n        HeliostatBlock.findConnectingHeliostats(pos, world, LongOpenHashSet(), positions)\n        val tagList = stack.orCreateNbt.getList(\"SelectedHeliostats\", 4)\n        positions.forEach { long -> tagList.add(NbtLong.of(long)) }\n        stack.orCreateNbt.put(\"SelectedHeliostats\", tagList)\n        player?.sendMessage(LiteralText(\"Click on Solar Power Plant Tower to link the Heliostats.\"), true)\n    } else if (blockEntity is MachineBlockEntity<*>) {\n        if (ConfigurationType.getTypes(blockEntity).isNotEmpty()) {\n            val map = EnumMap<ConfigurationType, SideConfiguration>(ConfigurationType::class.java)\n            map[ConfigurationType.ITEM] = if (blockEntity.isConfigurable(ConfigurationType.ITEM)) blockEntity.getCurrentConfiguration(ConfigurationType.ITEM) else SideConfiguration.EMPTY_ITEM\n            map[ConfigurationType.FLUID] = if (blockEntity.isConfigurable(ConfigurationType.FLUID)) blockEntity.getCurrentConfiguration(ConfigurationType.FLUID) else SideConfiguration.EMPTY_FLUID\n            map[ConfigurationType.ENERGY] = if (blockEntity.isConfigurable(ConfigurationType.ENERGY)) blockEntity.getCurrentConfiguration(ConfigurationType.ENERGY) else SideConfiguration.EMPTY_ENERGY\n            player?.openHandledScreen(ScrewdriverScreenHandlerFactory({ syncId, inv, ctx -> ScrewdriverScreenHandler(syncId, inv, ctx, map) }, pos, blockEntity))\n            return ActionResult.success(world.isClient)\n        }\n    }\n    return ActionResult.PASS\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/itemutils.kt",
    "content": "package me.steven.indrev.utils\n\nimport it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap\nimport net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache\nimport net.fabricmc.fabric.api.transfer.v1.item.ItemStorage\nimport net.fabricmc.fabric.api.transfer.v1.item.ItemVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.Storage\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.World\nimport java.util.*\nimport java.util.function.LongFunction\n\ntypealias ItemFilter = (ItemVariant) -> Boolean\n\nval itemApiCache = WeakHashMap<World, Long2ObjectOpenHashMap<BlockApiCache<Storage<ItemVariant>, Direction>>>()\n\nfun itemStorageOf(world: ServerWorld, blockPos: BlockPos, direction: Direction): Storage<ItemVariant>? {\n    return itemApiCache\n        .computeIfAbsent(world) { Long2ObjectOpenHashMap() }\n        .computeIfAbsent(blockPos.asLong(), LongFunction { BlockApiCache.create(ItemStorage.SIDED, world, blockPos) })\n        .find(direction)\n}\n\nfun itemStorageOf(world: World, blockPos: BlockPos, direction: Direction): Storage<ItemVariant>? {\n    return if (world is ServerWorld) itemStorageOf(world, blockPos, direction) else ItemStorage.SIDED.find(world, blockPos, direction)\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/machineinteractions.kt",
    "content": "package me.steven.indrev.utils\n\nimport me.steven.indrev.api.machines.TransferMode\nimport me.steven.indrev.blockentities.MachineBlockEntity\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.storage.Storage\nimport net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil\nimport net.minecraft.block.ChestBlock\nimport net.minecraft.block.InventoryProvider\nimport net.minecraft.block.entity.ChestBlockEntity\nimport net.minecraft.inventory.Inventory\nimport net.minecraft.inventory.SidedInventory\nimport net.minecraft.item.ItemStack\nimport net.minecraft.server.world.ServerWorld\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.World\nimport team.reborn.energy.api.EnergyStorageUtil\n\nfun MachineBlockEntity<*>.transferItems() {\n    itemTransferCooldown--\n    if (itemTransferCooldown <= 0) {\n        itemTransferCooldown = 0\n        inventoryComponent?.itemConfig?.forEach { (direction, mode) ->\n            val pos = pos.offset(direction)\n            val inventory = inventoryComponent?.inventory ?: return@forEach\n            if (mode.output && inventoryComponent!!.itemConfig.autoPush) {\n                val neighborInv = getInvAt(world!!, pos)\n                if (neighborInv != null) {\n                    inventory.outputSlots.forEach { slot ->\n                        transferItems(inventory, neighborInv, slot, direction)\n                    }\n                    return@forEach\n                }\n                val insertable = itemStorageOf(world!!, pos, direction.opposite)\n                val extractable = itemStorageOf(world!!, this.pos, direction)\n                StorageUtil.move(extractable, insertable, { true },  64, null)\n            }\n            if (mode.input && inventoryComponent!!.itemConfig.autoPull) {\n                val neighborInv = getInvAt(world!!, pos)\n                if (neighborInv != null) {\n                    getAvailableSlots(neighborInv, direction.opposite).forEach { slot ->\n                        transferItems(neighborInv, inventory, slot, direction.opposite)\n                    }\n                    return@forEach\n                }\n                val extractable = itemStorageOf(world!!, pos, direction.opposite)\n                val insertable = itemStorageOf(world!!, this.pos, direction)\n                StorageUtil.move(extractable, insertable, { true },  64, null)\n            }\n        }\n    }\n}\n\nfun MachineBlockEntity<*>.transferItems(from: Inventory, to: Inventory, slot: Int, direction: Direction) {\n    val toTransfer = from.getStack(slot)\n    while (!toTransfer.isEmpty) {\n        val firstSlot = (0 until to.size()).firstOrNull { firstSlot ->\n            val firstStack = to.getStack(firstSlot)\n            (canMergeItems(firstStack, toTransfer) || firstStack.isEmpty)\n                    && (to !is SidedInventory || to.canInsert(firstSlot, toTransfer, direction.opposite))\n        } ?: break\n        val targetStack = to.getStack(firstSlot)\n        if (from is SidedInventory && !from.canExtract(slot, toTransfer, direction))\n            break\n        val availableSize = (toTransfer.maxCount - targetStack.count).coerceAtMost(toTransfer.count)\n        if (!targetStack.isEmpty) {\n            toTransfer.count -= availableSize\n            targetStack.count += availableSize\n        } else {\n            from.setStack(slot, ItemStack.EMPTY)\n            to.setStack(firstSlot, toTransfer)\n            break\n        }\n        itemTransferCooldown = 12\n    }\n}\n\nprivate fun getAvailableSlots(inventory: Inventory, side: Direction): IntArray =\n    if (inventory is SidedInventory) inventory.getAvailableSlots(side) ?: EMPTY_INT_ARRAY\n    else (0 until inventory.size()).map { it }.toIntArray()\n\nprivate fun canMergeItems(first: ItemStack, second: ItemStack): Boolean =\n    first.item == second.item\n            && first.damage == second.damage\n            && first.count < first.maxCount\n            && ItemStack.areNbtEqual(first, second)\n\nprivate fun getInvAt(world: World, pos: BlockPos): Inventory? {\n    val blockState = world.getBlockState(pos)\n    val block = blockState?.block\n    return when {\n        block is InventoryProvider -> block.getInventory(blockState, world, pos)\n        blockState?.hasBlockEntity() == true -> {\n            val blockEntity = world.getBlockEntity(pos) as? Inventory ?: return null\n            if (blockEntity is ChestBlockEntity && block is ChestBlock)\n                ChestBlock.getInventory(block, blockState, world, pos, true)\n            else blockEntity\n        }\n        else -> null\n    }\n}\n\nfun MachineBlockEntity<*>.transferFluids() {\n    fluidComponent?.transferConfig?.forEach innerForEach@{ (direction, mode) ->\n        if (mode == TransferMode.NONE) return@innerForEach\n        var extractable: Storage<FluidVariant>? = null\n        var insertable: Storage<FluidVariant>? = null\n        if (mode.output) {\n            insertable = fluidStorageOf(world as ServerWorld, pos.offset(direction), direction.opposite)\n            extractable = fluidStorageOf(world as ServerWorld, pos, direction)\n        }\n        if (mode.input) {\n            extractable = fluidStorageOf(world as ServerWorld, pos.offset(direction), direction.opposite)\n            insertable =  fluidStorageOf(world as ServerWorld, pos, direction)\n\n        }\n        if (extractable != null && insertable != null)\n            StorageUtil.move(extractable, insertable, { true }, getFluidTransferRate(), null)\n    }\n}\n\nfun MachineBlockEntity<*>.transferEnergy() {\n    val world = world as ServerWorld\n    Direction.values()\n        .forEach { direction ->\n            if (validConnections.contains(direction)) {\n                val sourceIo = energyOf(world, pos, direction)\n                val targetIo = energyOf(world, pos.offset(direction), direction.opposite)\n                if (sourceIo == null || targetIo == null)\n                    validConnections.remove(direction)\n                else if (sourceIo.supportsExtraction() && targetIo.supportsInsertion())\n                    EnergyStorageUtil.move(sourceIo, targetIo, Long.MAX_VALUE, null)\n            }\n        }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/utils/utils.kt",
    "content": "package me.steven.indrev.utils\n\nimport com.google.gson.JsonElement\nimport me.shedaniel.math.Point\nimport me.shedaniel.rei.api.client.gui.widgets.Widget\nimport me.shedaniel.rei.api.client.gui.widgets.Widgets\nimport me.steven.indrev.IndustrialRevolution\nimport me.steven.indrev.gui.widgets.machines.TANK_BOTTOM\nimport net.fabricmc.fabric.api.item.v1.FabricItemSettings\nimport net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType\nimport net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry\nimport net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant\nimport net.fabricmc.fabric.api.transfer.v1.transaction.Transaction\nimport net.minecraft.block.Block\nimport net.minecraft.block.entity.BlockEntityType\nimport net.minecraft.client.MinecraftClient\nimport net.minecraft.client.util.SpriteIdentifier\nimport net.minecraft.entity.player.PlayerInventory\nimport net.minecraft.fluid.Fluid\nimport net.minecraft.inventory.Inventory\nimport net.minecraft.item.Item\nimport net.minecraft.nbt.NbtCompound\nimport net.minecraft.recipe.Recipe\nimport net.minecraft.recipe.RecipeManager\nimport net.minecraft.recipe.RecipeType\nimport net.minecraft.screen.PlayerScreenHandler\nimport net.minecraft.screen.ScreenHandler\nimport net.minecraft.screen.ScreenHandlerContext\nimport net.minecraft.text.OrderedText\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.math.ChunkPos\nimport net.minecraft.util.math.Direction\nimport net.minecraft.util.registry.Registry\n\n\nval EMPTY_INT_ARRAY = intArrayOf()\n\nfun identifier(id: String) = Identifier(IndustrialRevolution.MOD_ID, id)\n\nfun blockSpriteId(id: String) = SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, identifier(id))\n\nfun Identifier.block(block: Block): Identifier {\n    Registry.register(Registry.BLOCK, this, block)\n    return this\n}\n\nfun Identifier.fluid(fluid: Fluid): Identifier {\n    Registry.register(Registry.FLUID, this, fluid)\n    return this\n}\n\nfun Identifier.item(item: Item): Identifier {\n    Registry.register(Registry.ITEM, this, item)\n    return this\n}\n\nfun Identifier.blockEntityType(entityType: BlockEntityType<*>): Identifier {\n    Registry.register(Registry.BLOCK_ENTITY_TYPE, this, entityType)\n    return this\n}\n\nfun itemSettings(): FabricItemSettings = FabricItemSettings().group(IndustrialRevolution.MOD_GROUP)\n\nfun <T : ScreenHandler> Identifier.registerScreenHandler(\n    f: (Int, PlayerInventory, ScreenHandlerContext) -> T\n): ExtendedScreenHandlerType<T> =\n    ScreenHandlerRegistry.registerExtended(this) { syncId, inv, buf ->\n        f(syncId, inv, ScreenHandlerContext.create(inv.player.world, buf.readBlockPos()))\n    } as ExtendedScreenHandlerType<T>\n\n\nfun ChunkPos.toNbt() = NbtCompound().also {\n    it.putInt(\"x\", x)\n    it.putInt(\"z\", z)\n}\n\nfun getChunkPos(nbt: NbtCompound) = ChunkPos(nbt.getInt(\"x\"), nbt.getInt(\"z\"))\n\nfun getFluidFromJson(json: JsonElement): Array<IRFluidAmount> {\n    if (json.isJsonArray) {\n        return json.asJsonArray.map { getFluidFromJson(it.asJsonObject).toList() }.flatten().toTypedArray()\n    } else {\n        val json = json.asJsonObject\n        val fluidId = json.get(\"fluid\").asString\n        val fluidKey = FluidVariant.of(Registry.FLUID.get(Identifier(fluidId)))\n        val fluidAmount = json.get(\"amount\").asLong\n        return arrayOf(fluidAmount of fluidKey)\n    }\n}\n\nfun createREIFluidWidget(widgets: MutableList<Widget>, startPoint: Point, fluid: IRFluidAmount) {\n    widgets.add(Widgets.createTexturedWidget(TANK_BOTTOM.image, startPoint.x, startPoint.y, 0f, 0f, 16, 52, 16, 52))\n    widgets.add(Widgets.createDrawableWidget { _, matrices, mouseX, mouseY, _ ->\n        fluid.renderGuiRect(startPoint.x + 2.0, startPoint.y.toDouble() + 1.5, startPoint.x.toDouble() + 14, startPoint.y.toDouble() + 50)\n        if (mouseX > startPoint.x && mouseX < startPoint.x + 16 && mouseY > startPoint.y && mouseY < startPoint.y + 52) {\n            val information = mutableListOf<OrderedText>()\n            information.addAll(getTooltip(fluid.resource, fluid.amount, -1))\n            MinecraftClient.getInstance().currentScreen?.renderOrderedTooltip(matrices, information, mouseX, mouseY)\n        }\n    })\n}\n\nfun pack(dirs: Collection<Direction>): Byte {\n    var i = 0\n    dirs.forEach { dir -> i = i or (1 shl dir.id) }\n    return i.toByte()\n}\n\nfun unpack(byte: Byte): List<Direction> {\n    val i = byte.toInt()\n    return DIRECTIONS.filter { dir -> i and (1 shl dir.id) != 0 }\n}\n\nprivate val DIRECTIONS = Direction.values()\n\nval Fluid?.rawId: Int\n    get() = Registry.FLUID.getRawId(this)\n\ninline fun <T> transaction(block: (Transaction) -> T) = Transaction.openOuter().use(block)\n\n@Suppress(\"UNCHECKED_CAST\")\nfun <T : Recipe<Inventory>> RecipeManager.getRecipes(type: RecipeType<T>) = getAllOfType(type) as Map<Identifier, T>"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/world/features/IRConfiguredFeature.kt",
    "content": "package me.steven.indrev.world.features\n\nimport net.fabricmc.fabric.api.biome.v1.BiomeModification\nimport net.fabricmc.fabric.api.biome.v1.BiomeSelectionContext\nimport net.minecraft.tag.BiomeTags\nimport net.minecraft.util.Identifier\nimport net.minecraft.util.registry.BuiltinRegistries\nimport net.minecraft.util.registry.Registry\nimport net.minecraft.util.registry.RegistryEntry\nimport net.minecraft.util.registry.RegistryKey\nimport net.minecraft.world.World\nimport net.minecraft.world.biome.Biome\nimport net.minecraft.world.dimension.DimensionOptions\nimport net.minecraft.world.gen.GenerationStep\nimport net.minecraft.world.gen.feature.ConfiguredFeature\nimport net.minecraft.world.gen.feature.PlacedFeature\n\n\nclass IRConfiguredFeature(\n    val identifier: Identifier,\n    val step: GenerationStep.Feature,\n    val configuredFeature: ConfiguredFeature<*, *>,\n    val placedFeature: (ConfiguredFeature<*, *>) -> PlacedFeature,\n    val biomePredicate: (BiomeSelectionContext) -> Boolean\n) {\n    val configuredFeatureKey = RegistryKey.of(Registry.CONFIGURED_FEATURE_KEY, identifier)\n    val placedFeatureKey = RegistryKey.of(Registry.PLACED_FEATURE_KEY, identifier)\n    init {\n        BuiltinRegistries.add(BuiltinRegistries.CONFIGURED_FEATURE, configuredFeatureKey.value, configuredFeature)\n        BuiltinRegistries.add(BuiltinRegistries.PLACED_FEATURE, placedFeatureKey.value, placedFeature(configuredFeature))\n    }\n\n    companion object {\n        //TODO\n        val IS_OVERWORLD: (BiomeSelectionContext) -> Boolean = { ctx -> ctx.canGenerateIn(DimensionOptions.OVERWORLD) }\n        val IS_NETHER: (BiomeSelectionContext) -> Boolean = { ctx -> ctx.canGenerateIn(DimensionOptions.NETHER) }\n    }\n}"
  },
  {
    "path": "src/main/kotlin/me/steven/indrev/world/features/SulfurCrystalFeature.kt",
    "content": "package me.steven.indrev.world.features\n\nimport com.mojang.serialization.Codec\nimport me.steven.indrev.blocks.misc.SulfurCrystalBlock\nimport me.steven.indrev.registry.IRBlockRegistry\nimport me.steven.indrev.utils.any\nimport me.steven.indrev.utils.forEach\nimport net.minecraft.block.Blocks\nimport net.minecraft.block.Material\nimport net.minecraft.util.math.BlockPos\nimport net.minecraft.util.math.Box\nimport net.minecraft.util.math.Direction\nimport net.minecraft.world.gen.feature.DefaultFeatureConfig\nimport net.minecraft.world.gen.feature.Feature\nimport net.minecraft.world.gen.feature.util.FeatureContext\n\nclass SulfurCrystalFeature(codec: Codec<DefaultFeatureConfig>) : Feature<DefaultFeatureConfig>(codec) {\n\n    override fun generate(\n        context: FeatureContext<DefaultFeatureConfig>\n    ): Boolean {\n        val blockPos = context.origin\n        val world = context.world\n        val random = context.random\n        val mutablePos = BlockPos.Mutable()\n        val coveredArea = Box(blockPos).expand(8.0, 8.0, 8.0)\n        val isNearLava = coveredArea.any { x, y, z ->\n            if (context.world.isOutOfHeightLimit(y)) return@any false\n            mutablePos.set(x, y, z)\n            world?.getBlockState(mutablePos)?.isOf(Blocks.LAVA) == true\n        }\n        if (!isNearLava) return false\n        coveredArea.forEach { x, y, z ->\n            if (context.world.isOutOfHeightLimit(y)) return@forEach\n            mutablePos.set(x, y, z)\n            DIRECTIONS_LIST.shuffled(random).forEach { dir ->\n                val blockState = world?.getBlockState(mutablePos)\n                val pos = mutablePos.offset(dir)\n                val airState = world?.getBlockState(pos)\n                val state = IRBlockRegistry.SULFUR_CRYSTAL_CLUSTER.defaultState.with(SulfurCrystalBlock.FACING, dir)\n                if (blockState?.material == Material.STONE && airState?.isAir == true && state.canPlaceAt(world, pos)) {\n                    world.setBlockState(pos, state, 2)\n                    return true\n                }\n            }\n        }\n\n        return false\n    }\n\n    companion object {\n        private val DIRECTIONS_LIST = Direction.values().toMutableList()\n    }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/biomass_composter.json",
    "content": "{\n  \"multipart\": [\n    {\n      \"apply\": {\n        \"model\": \"block/composter\"\n      }\n    },\n    {\n      \"when\": {\n        \"closed\": true\n      },\n      \"apply\": {\n        \"model\": \"indrev:block/plank_block\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/block_base.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/block_base\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/block_highlight.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/block_highlight\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/block_shadow.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/block_shadow\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/bronze_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/bronze_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/cabinet.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"indrev:block/cabinet\",\n      \"y\": 90\n    },\n    \"facing=south\": {\n      \"model\": \"indrev:block/cabinet\",\n      \"y\": 270\n    },\n    \"facing=west\": {\n      \"model\": \"indrev:block/cabinet\",\n      \"y\": 180\n    },\n    \"facing=east\": {\n      \"model\": \"indrev:block/cabinet\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/capsule.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/capsule\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/charge_pad_mk4.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"indrev:block/charge_pad_mk4\"\n    },\n    \"facing=south\": {\n      \"model\": \"indrev:block/charge_pad_mk4\",\n      \"y\": 180\n    },\n    \"facing=west\": {\n      \"model\": \"indrev:block/charge_pad_mk4\",\n      \"y\": 270\n    },\n    \"facing=east\": {\n      \"model\": \"indrev:block/charge_pad_mk4\",\n      \"y\": 90\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/composting.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"block/composter_contents1\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/controller.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"indrev:block/controller\"\n    },\n    \"facing=south\": {\n      \"model\": \"indrev:block/controller\",\n      \"y\": 180\n    },\n    \"facing=west\": {\n      \"model\": \"indrev:block/controller\",\n      \"y\": 270\n    },\n    \"facing=east\": {\n      \"model\": \"indrev:block/controller\",\n      \"y\": 90\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/coolant.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"minecraft:block/water\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/deepslate_lead_ore.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/deepslate_lead_ore\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/deepslate_nikolite_ore.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/deepslate_nikolite_ore\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/deepslate_silver_ore.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/deepslate_silver_ore\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/deepslate_tin_ore.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/deepslate_tin_ore\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/deepslate_tungsten_ore.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/deepslate_tungsten_ore\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/drain_mk1.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/drain_mk1\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/drill_bottom.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/drill_bottom\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/drill_middle.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/drill_middle\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/drill_top.json",
    "content": "{\n  \"variants\": {\n    \"working=false\": {\n      \"model\": \"indrev:block/drill_top\"\n    },\n    \"working=true\": {\n      \"model\": \"indrev:block/drill_top_on\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/duct.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"indrev:block/duct\"\n    },\n    \"facing=south\": {\n      \"model\": \"indrev:block/duct\",\n      \"y\": 180\n    },\n    \"facing=west\": {\n      \"model\": \"indrev:block/duct\",\n      \"y\": 270\n    },\n    \"facing=east\": {\n      \"model\": \"indrev:block/duct\",\n      \"y\": 90\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/electrum_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/electrum_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/fisher_mk2.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"indrev:block/fishing_farm_mk2\"\n    },\n    \"facing=south\": {\n      \"model\": \"indrev:block/fishing_farm_mk2\",\n      \"y\": 180\n    },\n    \"facing=west\": {\n      \"model\": \"indrev:block/fishing_farm_mk2\",\n      \"y\": 270\n    },\n    \"facing=east\": {\n      \"model\": \"indrev:block/fishing_farm_mk2\",\n      \"y\": 90\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/fisher_mk3.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"indrev:block/fishing_farm_mk3\"\n    },\n    \"facing=south\": {\n      \"model\": \"indrev:block/fishing_farm_mk3\",\n      \"y\": 180\n    },\n    \"facing=west\": {\n      \"model\": \"indrev:block/fishing_farm_mk3\",\n      \"y\": 270\n    },\n    \"facing=east\": {\n      \"model\": \"indrev:block/fishing_farm_mk3\",\n      \"y\": 90\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/fisher_mk4.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"indrev:block/fishing_farm_mk4\"\n    },\n    \"facing=south\": {\n      \"model\": \"indrev:block/fishing_farm_mk4\",\n      \"y\": 180\n    },\n    \"facing=west\": {\n      \"model\": \"indrev:block/fishing_farm_mk4\",\n      \"y\": 270\n    },\n    \"facing=east\": {\n      \"model\": \"indrev:block/fishing_farm_mk4\",\n      \"y\": 90\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/frame.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/frame\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/heat_generator_mk4.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"indrev:block/heat_generator_mk4\"\n    },\n    \"facing=south\": {\n      \"model\": \"indrev:block/heat_generator_mk4\",\n      \"y\": 180\n    },\n    \"facing=west\": {\n      \"model\": \"indrev:block/heat_generator_mk4\",\n      \"y\": 270\n    },\n    \"facing=east\": {\n      \"model\": \"indrev:block/heat_generator_mk4\",\n      \"y\": 90\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/hydrogen.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"minecraft:block/water\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/intake.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"indrev:block/intake\"\n    },\n    \"facing=south\": {\n      \"model\": \"indrev:block/intake\",\n      \"y\": 180\n    },\n    \"facing=west\": {\n      \"model\": \"indrev:block/intake\",\n      \"y\": 270\n    },\n    \"facing=east\": {\n      \"model\": \"indrev:block/intake\",\n      \"y\": 90\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/laser_emitter_mk4.json",
    "content": "{\n  \"variants\": {\n    \"facing=north,powered=false\": {\n      \"model\": \"indrev:block/laser\"\n    },\n    \"facing=south,powered=false\": {\n      \"model\": \"indrev:block/laser\",\n      \"y\": 180\n    },\n    \"facing=west,powered=false\": {\n      \"model\": \"indrev:block/laser\",\n      \"y\": 270\n    },\n    \"facing=east,powered=false\": {\n      \"model\": \"indrev:block/laser\",\n      \"y\": 90\n    },\n    \"facing=down,powered=false\": {\n      \"model\": \"indrev:block/laser\",\n      \"x\": 90\n    },\n    \"facing=up,powered=false\": {\n      \"model\": \"indrev:block/laser\",\n      \"x\": 270\n    },\n    \"facing=north,powered=true\": {\n      \"model\": \"indrev:block/laser_on\"\n    },\n    \"facing=south,powered=true\": {\n      \"model\": \"indrev:block/laser_on\",\n      \"y\": 180\n    },\n    \"facing=west,powered=true\": {\n      \"model\": \"indrev:block/laser_on\",\n      \"y\": 270\n    },\n    \"facing=east,powered=true\": {\n      \"model\": \"indrev:block/laser_on\",\n      \"y\": 90\n    },\n    \"facing=down,powered=true\": {\n      \"model\": \"indrev:block/laser_on\",\n      \"x\": 90\n    },\n    \"facing=up,powered=true\": {\n      \"model\": \"indrev:block/laser_on\",\n      \"x\": 270\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/lead_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/lead_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/lead_ore.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/lead_ore\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/machine_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/machine_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/methane.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"minecraft:block/water\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/modular_workbench_mk4.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/modular_workbench_mk4\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/molten_copper.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/gray_lava\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/molten_gold.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/gray_lava\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/molten_iron.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/gray_lava\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/molten_lead.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/gray_lava\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/molten_netherite.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/gray_lava\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/molten_silver.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/gray_lava\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/molten_tin.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/gray_lava\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/nikolite_ore.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/nikolite_ore\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/ore_base.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/ore_base\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/ore_highlight.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/ore_highlight\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/oxygen.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"minecraft:block/water\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/plank_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/plank_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/planks.json",
    "content": "{\n  \"variants\": {\n    \"layers=1\": {\n      \"model\": \"indrev:block/planks_height2\"\n    },\n    \"layers=2\": {\n      \"model\": \"indrev:block/planks_height4\"\n    },\n    \"layers=3\": {\n      \"model\": \"indrev:block/planks_height6\"\n    },\n    \"layers=4\": {\n      \"model\": \"indrev:block/planks_height8\"\n    },\n    \"layers=5\": {\n      \"model\": \"indrev:block/planks_height10\"\n    },\n    \"layers=6\": {\n      \"model\": \"indrev:block/planks_height12\"\n    },\n    \"layers=7\": {\n      \"model\": \"indrev:block/planks_height14\"\n    },\n    \"layers=8\": {\n      \"model\": \"indrev:block/plank_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/pump_mk1.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"indrev:block/pump_mk1\",\n      \"y\": 270\n    },\n    \"facing=south\": {\n      \"model\": \"indrev:block/pump_mk1\",\n      \"y\": 90\n    },\n    \"facing=west\": {\n      \"model\": \"indrev:block/pump_mk1\",\n      \"y\": 180\n    },\n    \"facing=east\": {\n      \"model\": \"indrev:block/pump_mk1\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/raw_lead_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/raw_lead_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/raw_silver_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/raw_silver_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/raw_tin_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/raw_tin_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/raw_tungsten_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/raw_tungsten_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/silo.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/silo\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/silver_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/silver_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/silver_ore.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/silver_ore\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/steel_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/steel_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/sulfur_crystal.json",
    "content": "{\n  \"variants\": {\n    \"facing=north\": {\n      \"model\": \"indrev:block/sulfur_crystal\",\n      \"x\": 90\n    },\n    \"facing=south\": {\n      \"model\": \"indrev:block/sulfur_crystal\",\n      \"x\": 90,\n      \"y\": 180\n    },\n    \"facing=west\": {\n      \"model\": \"indrev:block/sulfur_crystal\",\n      \"x\": -90,\n      \"y\": 90\n    },\n    \"facing=east\": {\n      \"model\": \"indrev:block/sulfur_crystal\",\n      \"x\": 270,\n      \"y\": 270\n    },\n    \"facing=up\": {\n      \"model\": \"indrev:block/sulfur_crystal\"\n    },\n    \"facing=down\": {\n      \"model\": \"indrev:block/sulfur_crystal\",\n      \"x\": 180,\n      \"y\": 180\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/sulfuric_acid.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"minecraft:block/water\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/tank.json",
    "content": "{\n  \"variants\": {\n    \"up=false,down=false\": {\n      \"model\": \"indrev:block/tank\"\n    },\n    \"up=true,down=false\": {\n      \"model\": \"indrev:block/tank_up\"\n    },\n    \"up=false,down=true\": {\n      \"model\": \"indrev:block/tank_down\"\n    },\n    \"up=true,down=true\": {\n      \"model\": \"indrev:block/tank_both\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/tin_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/tin_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/tin_ore.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/tin_ore\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/toxic_mud.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"minecraft:block/water\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/tungsten_block.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/tungsten_block\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/tungsten_ore.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/tungsten_ore\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/warning_strobe.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/warning_strobe\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/blockstates/wither_proof_obsidian.json",
    "content": "{\n  \"variants\": {\n    \"\": {\n      \"model\": \"indrev:block/wither_proof_obsidian\"\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/lang/en_us.json",
    "content": "{\n  \"item.patchouli.industrial_revolution_book.name\": \"Revolutionary Guide\",\n  \"item.indrev.guide_book\": \"Revolutionary Guide\",\n  \"item.patchouli.industrial_revolution_book.landing\": \"This book will help you go through some of the important aspects of the mod.$(br2)If you find any issues, questions or suggestions, open an issue on $(l:https://github.com/GabrielOlvH/Industrial-Revolution/issues)GitHub$() or join our $(l:https://discord.com/invite/G4PjhEf)Discord!$()$(br2)Thank you for playing!\",\n  \"itemGroup.indrev.indrev_group\": \"Industrial Revolution\",\n  \"block.machines.tooltip.io\": \"I/O: %s\",\n  \"block.indrev.lazuli_flux_container_1\": \"Lazuli Flux\",\n  \"block.indrev.lazuli_flux_container_2\": \"Container\",\n  \"block.indrev.coal_generator\": \"Coal Generator\",\n  \"block.indrev.coal_generator_mk1\": \"Coal Generator\",\n  \"block.indrev.heat_generator\": \"Heat Generator\",\n  \"block.indrev.heat_generator_mk4\": \"Heat Generator\",\n  \"block.indrev.electric_furnace\": \"Electric Furnace\",\n  \"block.indrev.electric_furnace_mk1\": \"Electric Furnace MK1\",\n  \"block.indrev.electric_furnace_mk2\": \"Electric Furnace MK2\",\n  \"block.indrev.electric_furnace_mk3\": \"Electric Furnace MK3\",\n  \"block.indrev.electric_furnace_mk4\": \"Electric Furnace MK4\",\n  \"block.indrev.electric_furnace_creative\": \"Electric Furnace Creative\",\n  \"block.indrev.electric_furnace_factory\": \"Electric Furnace Factory\",\n  \"block.indrev.electric_furnace_factory_mk4\": \"Electric Furnace Factory MK4\",\n  \"block.indrev.smelter\": \"Industrial Smelter\",\n  \"block.indrev.smelter_mk4\": \"Industrial Smelter\",\n  \"block.indrev.condenser\": \"Condenser\",\n  \"block.indrev.condenser_mk4\": \"Condenser\",\n  \"block.indrev.pulverizer\": \"Pulverizer\",\n  \"block.indrev.pulverizer_mk1\": \"Pulverizer MK1\",\n  \"block.indrev.pulverizer_mk2\": \"Pulverizer MK2\",\n  \"block.indrev.pulverizer_mk3\": \"Pulverizer MK3\",\n  \"block.indrev.pulverizer_mk4\": \"Pulverizer MK4\",\n  \"block.indrev.pulverizer_creative\": \"Pulverizer Creative\",\n  \"block.indrev.pulverizer_factory\": \"Pulverizer Factory\",\n  \"block.indrev.pulverizer_factory_mk4\": \"Pulverizer Factory MK4\",\n  \"block.indrev.sawmill\": \"Sawmill\",\n  \"block.indrev.sawmill_mk1\": \"Sawmill MK1\",\n  \"block.indrev.sawmill_mk2\": \"Sawmill MK2\",\n  \"block.indrev.sawmill_mk3\": \"Sawmill MK3\",\n  \"block.indrev.sawmill_mk4\": \"Sawmill MK4\",\n  \"block.indrev.sawmill_creative\": \"Sawmill Creative\",\n  \"block.indrev.compressor\": \"Compressor\",\n  \"block.indrev.compressor_mk1\": \"Compressor MK1\",\n  \"block.indrev.compressor_mk2\": \"Compressor MK2\",\n  \"block.indrev.compressor_mk3\": \"Compressor MK3\",\n  \"block.indrev.compressor_mk4\": \"Compressor MK4\",\n  \"block.indrev.compressor_creative\": \"Compressor Creative\",\n  \"block.indrev.compressor_factory\": \"Compressor Factory\",\n  \"block.indrev.compressor_factory_mk4\": \"Compressor Factory MK4\",\n  \"block.indrev.solid_infuser_factory\": \"Solid Infuser Factory\",\n  \"block.indrev.solid_infuser\": \"Solid Infuser\",\n  \"block.indrev.solid_infuser_mk1\": \"Solid Infuser MK1\",\n  \"block.indrev.solid_infuser_mk2\": \"Solid Infuser MK2\",\n  \"block.indrev.solid_infuser_mk3\": \"Solid Infuser MK3\",\n  \"block.indrev.solid_infuser_mk4\": \"Solid Infuser MK4\",\n  \"block.indrev.solid_infuser_creative\": \"Solid Infuser Creative\",\n  \"block.indrev.solid_infuser_factory_mk4\": \"Solid Infuser Factory MK4\",\n  \"block.indrev.lazuli_flux_container_mk1\": \"Lazuli Flux Container MK1\",\n  \"block.indrev.lazuli_flux_container_mk2\": \"Lazuli Flux Container MK2\",\n  \"block.indrev.lazuli_flux_container_mk3\": \"Lazuli Flux Container MK3\",\n  \"block.indrev.lazuli_flux_container_mk4\": \"Lazuli Flux Container MK4\",\n  \"block.indrev.lazuli_flux_container_creative\": \"Lazuli Flux Container Creative\",\n  \"block.indrev.cable_mk1\": \"Cable MK1\",\n  \"block.indrev.cable_mk2\": \"Cable MK2\",\n  \"block.indrev.cable_mk3\": \"Cable MK3\",\n  \"block.indrev.cable_mk4\": \"Cable MK4\",\n  \"block.indrev.fluid_pipe_mk1\": \"Fluid Pipe MK1\",\n  \"block.indrev.fluid_pipe_mk2\": \"Fluid Pipe MK2\",\n  \"block.indrev.fluid_pipe_mk3\": \"Fluid Pipe MK3\",\n  \"block.indrev.fluid_pipe_mk4\": \"Fluid Pipe MK4\",\n  \"block.indrev.item_pipe_mk1\": \"Item Pipe MK1\",\n  \"block.indrev.item_pipe_mk2\": \"Item Pipe MK2\",\n  \"block.indrev.item_pipe_mk3\": \"Item Pipe MK3\",\n  \"block.indrev.item_pipe_mk4\": \"Item Pipe MK4\",\n  \"block.indrev.fisher\": \"Fisher\",\n  \"block.indrev.fisher_mk2\": \"Basic Fisher\",\n  \"block.indrev.fisher_mk3\": \"Improved Fisher\",\n  \"block.indrev.fisher_mk4\": \"Advanced Fisher\",\n  \"block.indrev.data_card_writer\": \"Data Card Encoder\",\n  \"block.indrev.data_card_writer_mk4\": \"Data Card Encoder\",\n  \"block.indrev.mining_rig\": \"Mining Rig Computer\",\n  \"block.indrev.mining_rig_mk4\": \"Mining Rig Computer\",\n  \"block.indrev.mining_rig.tooltip\": \"You must use a Chunk Scanner in this chunk before using the miner\",\n  \"block.indrev.mining_rig.mined\": \"cycles remaining\",\n  \"block.indrev.drill\": \"Mining Rig Drill\",\n  \"block.indrev.drill_bottom\": \"Mining Rig Drill\",\n  \"block.indrev.drill_middle\": \"Mining Rig Drill\",\n  \"block.indrev.drill_top\": \"Mining Rig Drill\",\n  \"block.indrev.drill.faster\": \"%sx faster\",\n  \"block.indrev.drill.no_drills\": \"Empty.\",\n  \"block.indrev.drill.wrong_location\": \"Incorrect report for current location!\",\n  \"block.indrev.drill.active\": \"Active drills\",\n  \"block.indrev.drill.activating\": \"Not ready\",\n  \"block.indrev.drill.not_enough_power\": \"Insufficient power\",\n  \"block.indrev.drill.power_required\": \"Requires at least %sLF to operate\",\n  \"block.indrev.recycler\": \"Recycler\",\n  \"block.indrev.recycler_mk2\": \"Recycler\",\n  \"block.indrev.cable\": \"Cable\",\n  \"block.indrev.farmer_mk1\": \"Farmer MK1\",\n  \"block.indrev.farmer_mk2\": \"Farmer MK2\",\n  \"block.indrev.farmer_mk3\": \"Farmer MK3\",\n  \"block.indrev.farmer_mk4\": \"Farmer MK4\",\n  \"block.indrev.farmer_creative\": \"Creative Farmer\",\n  \"block.indrev.farmer\": \"Farmer\",\n  \"block.indrev.slaughter_mk1\": \"Slaughter MK1\",\n  \"block.indrev.slaughter_mk2\": \"Slaughter MK2\",\n  \"block.indrev.slaughter_mk3\": \"Slaughter MK3\",\n  \"block.indrev.slaughter_mk4\": \"Slaughter MK4\",\n  \"block.indrev.slaughter_creative\": \"Creative Slaughter\",\n  \"block.indrev.slaughter\": \"Slaughter\",\n  \"block.indrev.chopper\": \"Chopper\",\n  \"block.indrev.chopper_mk1\": \"Chopper MK1\",\n  \"block.indrev.chopper_mk2\": \"Chopper MK2\",\n  \"block.indrev.chopper_mk3\": \"Chopper MK3\",\n  \"block.indrev.chopper_mk4\": \"Chopper MK4\",\n  \"block.indrev.chopper_creative\": \"Creative Chopper\",\n  \"block.indrev.rancher\": \"Rancher\",\n  \"block.indrev.rancher_mk1\": \"Rancher MK1\",\n  \"block.indrev.rancher_mk2\": \"Rancher MK2\",\n  \"block.indrev.rancher_mk3\": \"Rancher MK3\",\n  \"block.indrev.rancher_mk4\": \"Rancher MK4\",\n  \"block.indrev.rancher_creative\": \"Creative Rancher\",\n  \"block.indrev.fluid_infuser\": \"Fluid Infuser\",\n  \"block.indrev.fluid_infuser_mk1\": \"Fluid Infuser MK1\",\n  \"block.indrev.fluid_infuser_mk2\": \"Fluid Infuser MK2\",\n  \"block.indrev.fluid_infuser_mk3\": \"Fluid Infuser MK3\",\n  \"block.indrev.fluid_infuser_mk4\": \"Fluid Infuser MK4\",\n  \"block.indrev.fluid_infuser_creative\": \"Fluid Infuser Creative\",\n  \"block.indrev.electrolytic_separator\": \"Electrolytic Separator\",\n  \"block.indrev.electrolytic_separator_mk1\": \"Electrolytic Separator MK1\",\n  \"block.indrev.electrolytic_separator_mk2\": \"Electrolytic Separator MK2\",\n  \"block.indrev.electrolytic_separator_mk3\": \"Electrolytic Separator MK3\",\n  \"block.indrev.electrolytic_separator_mk4\": \"Electrolytic Separator MK4\",\n  \"block.indrev.electrolytic_separator_creative\": \"Electrolytic Separator Creative\",\n  \"block.indrev.drain_mk1\": \"Drain\",\n  \"block.indrev.tank\": \"Tank\",\n  \"block.indrev.factory\": \"Factory\",\n  \"block.indrev.biomass_generator\": \"Biomass Generator\",\n  \"block.indrev.biomass_generator_mk3\": \"Biomass Generator\",\n  \"block.indrev.solar_generator\": \"Solar Generator\",\n  \"block.indrev.solar_generator_mk1\": \"Solar Generator\",\n  \"block.indrev.solar_generator_mk3\": \"Solar Generator\",\n  \"block.indrev.charge_pad_mk4\": \"Charge Pad\",\n  \"block.indrev.charge_pad_mk4.tooltip\": \"Charges equipped items or anything you put in it!\",\n  \"block.indrev.aoe.range\": \"Range: %s\",\n  \"block.indrev.aoe.toggle.false\": \"Show range\",\n  \"block.indrev.aoe.toggle.true\": \"Hide range\",\n  \"block.indrev.planks\": \"Plank\",\n  \"block.indrev.plank_block\": \"Plank Block\",\n  \"block.indrev.wither_proof_obsidian\": \"Wither Proof Obsidian\",\n  \"block.indrev.controller\": \"Controller\",\n  \"block.indrev.frame\": \"Frame\",\n  \"block.indrev.duct\": \"Duct\",\n  \"block.indrev.warning_strobe\": \"Warning Strobe\",\n  \"block.indrev.silo\": \"Silo\",\n  \"block.indrev.intake\": \"Intake\",\n  \"block.indrev.cabinet\": \"Cabinet\",\n  \"block.indrev.coolant\": \"Coolant\",\n  \"block.indrev.molten_iron\": \"Molten Iron\",\n  \"block.indrev.molten_gold\": \"Molten Gold\",\n  \"block.indrev.molten_copper\": \"Molten Copper\",\n  \"block.indrev.molten_tin\": \"Molten Tin\",\n  \"block.indrev.molten_netherite\": \"Molten Netherite\",\n  \"item.indrev.ore_data_card\": \"Ore Data Card\",\n  \"item.indrev.empty_ore_data_card\": \"Empty Ore Data Card\",\n  \"item.indrev.ore_data_card.modifier.speed\": \"speed\",\n  \"item.indrev.ore_data_card.modifier.size\": \"size\",\n  \"item.indrev.ore_data_card.modifier.richness\": \"richness\",\n  \"item.indrev.ore_data_card.modifier.rng\": \"RNG\",\n  \"block.indrev.hydrogen\": \"Hydrogen\",\n  \"item.indrev.hydrogen_bucket\": \"Hydrogen Bucket\",\n  \"block.indrev.oxygen\": \"Oxygen\",\n  \"item.indrev.oxygen_bucket\": \"Oxygen Bucket\",\n  \"block.indrev.methane\": \"Methane\",\n  \"item.indrev.methane_bucket\": \"Methane Bucket\",\n  \"block.indrev.sulfuric_acid\": \"Sulfuric Acid\",\n  \"item.indrev.sulfuric_acid_bucket\": \"Sulfuric Acid Bucket\",\n  \"block.indrev.toxic_mud\": \"Toxic Mud\",\n  \"item.indrev.toxic_mud_bucket\": \"Toxic Mud Bucket\",\n  \"block.indrev.machine_block\": \"Machine Block\",\n  \"item.indrev.modular_core\": \"Core of Modularity (Uncharged)\",\n  \"item.indrev.modular_core_activated\": \"Core of Modularity\",\n  \"item.indrev.sawdust\": \"Sawdust\",\n  \"block.indrev.laser_emitter_mk4\": \"Laser Emitter\",\n  \"block.indrev.capsule\": \"Capsule\",\n  \"block.indrev.tin_ore\": \"Tin Ore\",\n  \"block.indrev.tin_block\": \"Block of Tin\",\n  \"item.indrev.tin_ingot\": \"Tin Ingot\",\n  \"item.indrev.tin_chunk\": \"Tin Chunk\",\n  \"item.indrev.tin_dust\": \"Tin Dust\",\n  \"item.indrev.tin_plate\": \"Tin Plate\",\n  \"item.indrev.tin_gear\": \"Tin Gear\",\n  \"item.indrev.tin_nugget\": \"Tin Nugget\",\n  \"item.indrev.molten_tin_bucket\": \"Molten Tin Bucket\",\n  \"item.indrev.tin_purified_ore\": \"Purified Tin Ore\",\n  \"item.indrev.copper_chunk\": \"Copper Chunk\",\n  \"item.indrev.copper_dust\": \"Copper Dust\",\n  \"item.indrev.copper_plate\": \"Copper Plate\",\n  \"item.indrev.copper_gear\": \"Copper Gear\",\n  \"item.indrev.copper_nugget\": \"Copper Nugget\",\n  \"item.indrev.copper_purified_ore\": \"Purified Copper Ore\",\n  \"item.indrev.molten_copper_bucket\": \"Molten Copper Bucket\",\n  \"block.indrev.molten_lead\": \"Molten Lead\",\n  \"item.indrev.molten_lead_bucket\": \"Molten Lead Bucket\",\n  \"block.indrev.lead_ore\": \"Lead Ore\",\n  \"block.indrev.lead_block\": \"Block of Lead\",\n  \"item.indrev.lead_ingot\": \"Lead Ingot\",\n  \"item.indrev.lead_chunk\": \"Lead Chunk\",\n  \"item.indrev.lead_dust\": \"Lead Dust\",\n  \"item.indrev.lead_plate\": \"Lead Plate\",\n  \"item.indrev.lead_gear\": \"Lead Gear\",\n  \"item.indrev.lead_nugget\": \"Lead Nugget\",\n  \"item.indrev.lead_purified_ore\": \"Purified Lead Ore\",\n  \"item.indrev.raw_lead\": \"Raw Lead\",\n  \"item.indrev.raw_silver\": \"Raw Silver\",\n  \"item.indrev.raw_tungsten\": \"Raw Tungsten\",\n  \"item.indrev.raw_tin\": \"Raw Tin\",\n  \"block.indrev.raw_lead_block\": \"Raw Lead Block\",\n  \"block.indrev.raw_silver_block\": \"Raw Silver Block\",\n  \"block.indrev.raw_tungsten_block\": \"Raw Tungsten Block\",\n  \"block.indrev.raw_tin_block\": \"Raw Tin Block\",\n  \"block.indrev.molten_silver\": \"Molten Silver\",\n  \"item.indrev.molten_silver_bucket\": \"Molten Silver Bucket\",\n  \"block.indrev.silver_ore\": \"Silver Ore\",\n  \"block.indrev.silver_block\": \"Block of Silver\",\n  \"item.indrev.silver_ingot\": \"Silver Ingot\",\n  \"item.indrev.silver_chunk\": \"Silver Chunk\",\n  \"item.indrev.silver_dust\": \"Silver Dust\",\n  \"item.indrev.silver_plate\": \"Silver Plate\",\n  \"item.indrev.silver_gear\": \"Silver Gear\",\n  \"item.indrev.silver_nugget\": \"Silver Nugget\",\n  \"item.indrev.silver_purified_ore\": \"Purified Silver Ore\",\n  \"block.indrev.molten_tungsten\": \"Molten Tungsten\",\n  \"item.indrev.molten_tungsten_bucket\": \"Molten Tungsten Bucket\",\n  \"block.indrev.tungsten_ore\": \"Tungsten Ore\",\n  \"block.indrev.tungsten_block\": \"Block of Tungsten\",\n  \"item.indrev.tungsten_ingot\": \"Tungsten Ingot\",\n  \"item.indrev.tungsten_chunk\": \"Tungsten Chunk\",\n  \"item.indrev.tungsten_dust\": \"Tungsten Dust\",\n  \"item.indrev.tungsten_plate\": \"Tungsten Plate\",\n  \"item.indrev.tungsten_gear\": \"Tungsten Gear\",\n  \"item.indrev.tungsten_nugget\": \"Tungsten Nugget\",\n  \"item.indrev.tungsten_purified_ore\": \"Purified Tungsten Ore\",\n  \"block.indrev.deepslate_tungsten_ore\": \"Deepslate Tungsten Ore\",\n  \"block.indrev.deepslate_tin_ore\": \"Deepslate Tin Ore\",\n  \"block.indrev.deepslate_silver_ore\": \"Deepslate Silver Ore\",\n  \"block.indrev.deepslate_nikolite_ore\": \"Deepslate Nikolite Ore\",\n  \"block.indrev.deepslate_lead_ore\": \"Deepslate Lead Ore\",\n  \"block.indrev.electrum_block\": \"Block of Electrum\",\n  \"item.indrev.electrum_ingot\": \"Electrum Ingot\",\n  \"item.indrev.electrum_dust\": \"Electrum Dust\",\n  \"item.indrev.electrum_plate\": \"Electrum Plate\",\n  \"item.indrev.electrum_gear\": \"Electrum Gear\",\n  \"item.indrev.electrum_nugget\": \"Electrum Nugget\",\n  \"block.indrev.bronze_block\": \"Block of Bronze\",\n  \"item.indrev.bronze_ingot\": \"Bronze Ingot\",\n  \"item.indrev.bronze_chunk\": \"Bronze Chunk\",\n  \"item.indrev.bronze_dust\": \"Bronze Dust\",\n  \"item.indrev.bronze_plate\": \"Bronze Plate\",\n  \"item.indrev.bronze_gear\": \"Bronze Gear\",\n  \"item.indrev.bronze_nugget\": \"Bronze Nugget\",\n  \"block.indrev.sulfur_crystal\": \"Sulfur Crystal\",\n  \"item.indrev.sulfur_crystal\": \"Sulfur Crystal\",\n  \"item.indrev.sulfur_dust\": \"Sulfur Dust\",\n  \"block.indrev.steel_block\": \"Block of Steel\",\n  \"item.indrev.steel_dust\": \"Steel Dust\",\n  \"item.indrev.steel_ingot\": \"Steel Ingot\",\n  \"item.indrev.steel_plate\": \"Steel Plate\",\n  \"item.indrev.steel_gear\": \"Steel Gear\",\n  \"item.indrev.steel_nugget\": \"Steel Nugget\",\n  \"item.indrev.gold_dust\": \"Gold Dust\",\n  \"item.indrev.gold_plate\": \"Gold Plate\",\n  \"item.indrev.gold_chunk\": \"Gold Chunk\",\n  \"item.indrev.gold_purified_ore\": \"Purified Gold Ore\",\n  \"item.indrev.molten_gold_bucket\": \"Molten Gold Bucket\",\n  \"item.indrev.iron_dust\": \"Iron Dust\",\n  \"item.indrev.iron_plate\": \"Iron Plate\",\n  \"item.indrev.iron_chunk\": \"Iron Chunk\",\n  \"item.indrev.molten_iron_bucket\": \"Molten Iron Bucket\",\n  \"item.indrev.iron_purified_ore\": \"Purified Iron Ore\",\n  \"item.indrev.diamond_dust\": \"Diamond Dust\",\n  \"item.indrev.coal_dust\": \"Coal Dust\",\n  \"block.indrev.pump_mk1\": \"Pump\",\n  \"item.indrev.empty_enhancer\": \"Empty Enhancer\",\n  \"item.indrev.buffer_enhancer\": \"Buffer Enhancer\",\n  \"item.indrev.buffer_enhancer.tooltip\": \"Increases energy capacity\",\n  \"item.indrev.energy_enhancer\": \"Energy Efficiency Enhancer\",\n  \"item.indrev.energy_enhancer.tooltip\": \"Decreases energy cost\",\n  \"item.indrev.damage_enhancer\": \"Damage Enhancer\",\n  \"item.indrev.damage_enhancer.tooltip\": \"Increases the damage done by a Slaughter\",\n  \"item.indrev.speed_enhancer\": \"Speed Enhancer\",\n  \"item.indrev.speed_enhancer.tooltip\": \"Increases processing speed and power consumption\",\n  \"item.indrev.blast_furnace_enhancer\": \"Blasting Enhancer\",\n  \"item.indrev.blast_furnace_enhancer.tooltip\": \"Allows the Electric Furnace to accept Blast Furnace recipes\",\n  \"item.indrev.smoker_enhancer\": \"Smoking Enhancer\",\n  \"item.indrev.smoker_enhancer.tooltip\": \"Allows Electric Furnace to accept Smoker recipes\",\n  \"item.indrev.enhancers.incompatible\": \"This machine does not support this enhancer\",\n  \"item.indrev.enhancers.count\": \"Limited by %s per slot by the machine\",\n  \"item.indrev.rechargeable.tooltip\": \"Rechargeable\",\n  \"item.indrev.chunk_scanner\": \"Chunk Scanner\",\n  \"item.indrev.chunk_scanner.scanning\": \"Scanning...\",\n  \"item.indrev.chunk_scanner.scanned1\": \"Finished Scanning! Check your inventory.\",\n  \"item.indrev.chunk_scanner.scanned2\": \"Vein type: %s\",\n  \"item.indrev.chunk_scanner.already_scanned\": \"This chunk is already scanned and it has a %s vein\",\n  \"item.indrev.chunk_scanner.tooltip1\": \"Hold right click to start scanning this chunk\",\n  \"item.indrev.chunk_scanner.tooltip2\": \"Vein type: %s\",\n  \"item.indrev.chunk_scanner.tooltip3\": \"From %s to %s\",\n  \"item.indrev.chunk_scanner.tooltip4\": \"Dimension: %s\",\n  \"item.indrev.chunk_scanner.tooltip5\": \"Right Click to read more\",\n  \"item.indrev.fan\": \"Fan\",\n  \"item.indrev.cooler_cell\": \"Cooler Cell\",\n  \"item.indrev.heatsink\": \"Heatsink\",\n  \"item.indrev.heat_coil\": \"Heat Coil\",\n  \"item.indrev.heat_coil.tooltip\": \"Converts Lazuli Flux into heat\",\n  \"item.indrev.stone_drill_head\": \"Stone Drill Head\",\n  \"item.indrev.iron_drill_head\": \"Iron Drill Head\",\n  \"item.indrev.diamond_drill_head\": \"Diamond Drill Head\",\n  \"item.indrev.netherite_drill_head\": \"Netherite Drill Head\",\n  \"item.indrev.mining_drill_mk1\": \"Mining Drill MK1\",\n  \"item.indrev.mining_drill_mk2\": \"Mining Drill MK2\",\n  \"item.indrev.mining_drill_mk3\": \"Mining Drill MK3\",\n  \"item.indrev.mining_drill_mk4\": \"Modular Mining Drill\",\n  \"item.indrev.energy_reader\": \"Energy Reader\",\n  \"item.indrev.energy_reader.use\": \"Energy stored:\",\n  \"item.indrev.energy_reader.use1\": \"%s LF/tick\",\n  \"item.indrev.enriched_nikolite_dust\": \"Enriched Nikolite Dust\",\n  \"item.indrev.enriched_nikolite_ingot\": \"Enriched Nikolite Ingot\",\n  \"item.indrev.battery\": \"Battery\",\n  \"item.indrev.circuit_mk1\": \"MK1 Circuit\",\n  \"item.indrev.circuit_mk2\": \"MK2 Circuit\",\n  \"item.indrev.circuit_mk3\": \"MK3 Circuit\",\n  \"item.indrev.circuit_mk4\": \"MK4 Circuit\",\n  \"item.indrev.tier_upgrade_mk2\": \"MK2 Machine Upgrade\",\n  \"item.indrev.tier_upgrade_mk2.tooltip\": \"Upgrades MK1 machines to MK2\",\n  \"item.indrev.tier_upgrade_mk3\": \"MK3 Machine Upgrade\",\n  \"item.indrev.tier_upgrade_mk3.tooltip\": \"Upgrades MK2 machines to MK3\",\n  \"item.indrev.tier_upgrade_mk4\": \"MK4 Machine Upgrade\",\n  \"item.indrev.tier_upgrade_mk4.tooltip\": \"Upgrades MK3 machines to MK4\",\n  \"item.indrev.servo_output\": \"Output Servo\",\n  \"item.indrev.servo_output.tooltip\": \"Pushes into connected inventories\",\n  \"item.indrev.servo_retriever\": \"Retriever Servo\",\n  \"item.indrev.servo_retriever.tooltip\": \"Pulls from connected inventories\",\n  \"item.indrev.servo.mode\": \"Priority: \",\n  \"item.indrev.servo.mode.round_robin\": \"Round Robin\",\n  \"item.indrev.servo.mode.round_robin.tooltip\": \"Prioritizes containers with least amount of the item/fluid\",\n  \"item.indrev.servo.mode.nearest_first\": \"Nearest First\",\n  \"item.indrev.servo.mode.nearest_first.tooltip\": \"Prioritizes closer containers\",\n  \"item.indrev.servo.mode.furthest_first\": \"Furthest First\",\n  \"item.indrev.servo.mode.furthest_first.tooltip\": \"Prioritizes farther containers\",\n  \"item.indrev.servo.mode.random\": \"Random\",\n  \"item.indrev.servo.mode.random.tooltip\": \"No prioritization\",\n  \"item.indrev.biomass\": \"Biomass\",\n  \"item.indrev.untanned_leather\": \"Untanned Leather\",\n  \"item.indrev.coolant_bucket\": \"Coolant Bucket\",\n  \"item.indrev.molten_netherite_bucket\": \"Molten Netherite Bucket\",\n  \"item.indrev.netherite_scrap_purified_ore\": \"Purified Ancient Debris\",\n  \"item.indrev.netherite_scrap_chunk\": \"Ancient Debris Chunk\",\n  \"item.indrev.netherite_scrap_dust\": \"Netherite Scrap Dust\",\n  \"item.indrev.hammer\": \"Hammer\",\n  \"item.indrev.screwdriver\": \"Screwdriver\",\n  \"item.indrev.wrench\": \"Wrench\",\n  \"item.indrev.wrench.switch_mode\": \"Wrench Mode: %s\",\n  \"item.indrev.wrench.title\": \"Configure Machine\",\n  \"item.indrev.wrench.autopush\": \"Auto Push\",\n  \"item.indrev.wrench.autopull\": \"Auto Pull\",\n  \"item.indrev.wrench.item\": \"Item\",\n  \"item.indrev.wrench.fluid\": \"Fluid\",\n  \"item.indrev.wrench.energy\": \"Energy\",\n  \"item.indrev.wrench.output\": \"Output\",\n  \"item.indrev.wrench.input\": \"Input\",\n  \"item.indrev.wrench.input_first\": \"Input (First Slot)\",\n  \"item.indrev.wrench.input_second\": \"Input (Second Slot)\",\n  \"item.indrev.wrench.input_output\": \"Input and Output\",\n  \"item.indrev.wrench.none\": \"None\",\n  \"item.indrev.wrench.tooltip\": \"Shift + Right Click to change mode\",\n  \"item.indrev.wrench.tooltip1\": \"Mode: %s\",\n  \"item.indrev.wrench.tooltip1.rotate\": \"Rotate\",\n  \"item.indrev.wrench.tooltip1.configure\": \"Configure\",\n  \"item.indrev.wrench.mode\": \"Mode: %s\",\n  \"item.indrev.wrench.side.north\": \"North\",\n  \"item.indrev.wrench.side.south\": \"South\",\n  \"item.indrev.wrench.side.west\": \"West\",\n  \"item.indrev.wrench.side.east\": \"East\",\n  \"item.indrev.wrench.side.up\": \"Up\",\n  \"item.indrev.wrench.side.down\": \"Down\",\n  \"item.indrev.wrench.side.top\": \"Top\",\n  \"item.indrev.wrench.side.bottom\": \"Bottom\",\n  \"item.indrev.wrench.side.left\": \"Left\",\n  \"item.indrev.wrench.side.right\": \"Right\",\n  \"item.indrev.wrench.side.front\": \"Front\",\n  \"item.indrev.wrench.side.back\": \"Back\",\n  \"item.indrev.wrench.connected\": \"Connected to %s\",\n  \"item.indrev.tech_soup\": \"Tech Soup\",\n  \"item.indrev.scan_output\": \"Resource Report\",\n  \"item.indrev.portable_charger\": \"Portable Charger\",\n  \"item.indrev.gamer_axe\": \"Gamer Axe\",\n  \"item.indrev.gamer_axe.true\": \"On\",\n  \"item.indrev.gamer_axe.false\": \"Off\",\n  \"item.indrev.gamer_axe.tooltip.true\": \"Press %s to toggle off\",\n  \"item.indrev.gamer_axe.tooltip.false\": \"Press %s to toggle on\",\n  \"item.indrev.modular_item.tooltip\": \"Press %s to toggle modules\",\n  \"gui.indrev.button.auto_split\": \"Auto-Split Stacks\",\n  \"gui.indrev.resourcereport.size\": \"Vein size: %s\",\n  \"gui.indrev.resourcereport.size.tiny\": \"Tiny\",\n  \"gui.indrev.resourcereport.size.small\": \"Small\",\n  \"gui.indrev.resourcereport.size.average\": \"Average\",\n  \"gui.indrev.resourcereport.size.big\": \"Big\",\n  \"gui.indrev.resourcereport.size.very_big\": \"Very big\",\n  \"gui.indrev.resourcereport.size.enormous\": \"Enormous\",\n  \"gui.indrev.resourcereport.size.gigantic\": \"Gigantic\",\n  \"gui.furnace.mode\": \"Mode\",\n  \"gui.furnace.mode.furnace\": \"Furnace\",\n  \"gui.furnace.mode.blast_furnace\": \"Blast Furnace\",\n  \"gui.furnace.mode.smoker\": \"Smoker\",\n  \"gui.widget.process\": \"Progress: %s\",\n  \"gui.widget.energy\": \"Energy\",\n  \"gui.widget.temperature\": \"Temperature\",\n  \"gui.widget.temperature_info.high\": \"Too hot\",\n  \"gui.widget.temperature_info.medium\": \"Full potential\",\n  \"gui.widget.temperature_info.low\": \"Not heated up\",\n  \"gui.indrev.solar.on\": \"Generating\",\n  \"gui.indrev.heatgen.idle\": \"Idle\",\n  \"gui.indrev.heatgen.title\": \"Consuming %s mB\",\n  \"gui.indrev.heatgen.generating\": \"Generating %s LF\",\n  \"gui.indrev.heatgen.pertick\": \"per tick\",\n  \"gui.indrev.heatgen.extra\": \"Careful, it's hot!\",\n  \"gui.indrev.guide_book_shortcut.contains\": \"Opens about page on the Revolutionary Guide\",\n  \"gui.indrev.guide_book_shortcut.missing\": \"Missing the Revolutionary Guide!\",\n  \"gui.indrev.tooltip.maxTransferRate\": \"Transfer capacity: \",\n  \"gui.indrev.tooltip.maxInput\": \"Input capacity: \",\n  \"gui.indrev.tooltip.maxOutput\": \"Output capacity: \",\n  \"gui.indrev.tooltip.maxEnergyStored\": \"Energy capacity: \",\n  \"gui.indrev.tooltip.seconds\": \"%s seconds\",\n  \"gui.indrev.tooltip.energyCost\": \"Energy cost: \",\n  \"gui.indrev.tooltip.processSpeed\": \"Speed: \",\n  \"gui.indrev.tooltip.ratio\": \"Generation: \",\n  \"gui.indrev.tooltip.press_shift\": \"Hold %s for more information\",\n  \"gui.indrev.tooltip.lftick\": \"%s LF/tick\",\n  \"gui.indrev.tooltip.lf\": \"%s LF\",\n  \"gui.indrev.tooltip.itemsec\": \"%s items/s\",\n  \"gui.indrev.tooltip.fluidsec\": \"%s buckets/s\",\n  \"gui.indrev.tooltip.temperatureBoost\": \"Heated up: \",\n  \"gui.indrev.locked\": \"Locked\",\n  \"gui.indrev.modular_armor_slot_type\": \"Insert item here\",\n  \"gui.indrev.module_slot_type\": \"Insert desired module here\",\n  \"gui.indrev.scan_output_slot_type\": \"Insert data card\",\n  \"gui.indrev.output_slot_type\": \"Collected items\",\n  \"gui.indrev.chopper_input_axe\": \"Insert axe\",\n  \"gui.indrev.chopper_input_bone_meal\": \"Insert bone meal\",\n  \"gui.indrev.chopper_input_sapling\": \"Insert saplings\",\n  \"gui.indrev.farmer_input_slot_type\": \"Seeds and bone meals\",\n  \"gui.indrev.slaughter_input_sword\": \"Insert sword\",\n  \"gui.indrev.cooler_slot_type\": \"Cooler\",\n  \"gui.indrev.battery_slot_type\": \"Battery\",\n  \"gui.indrev.upgrade_slot_type\": \"Upgrades\",\n  \"gui.indrev.fishingrod\": \"Fishing rod\",\n  \"indrev.category.rei.upgrading\": \"You must craft a %s and use it on a %s to upgrade it to %s\",\n  \"indrev.category.rei.pulverizing\": \"Pulverizing\",\n  \"indrev.category.rei.infusing\": \"Infusing\",\n  \"indrev.category.rei.compressing\": \"Compressing\",\n  \"indrev.category.rei.recycling\": \"Recycling\",\n  \"indrev.category.rei.fluid_infusing\": \"Fluid Infusing\",\n  \"indrev.category.rei.condensing\": \"Condensing\",\n  \"indrev.category.rei.smelting\": \"Smelting\",\n  \"indrev.category.rei.sawmill\": \"Sawmill\",\n  \"indrev.category.rei.module\": \"Module Crafting\",\n  \"item.indrev.nikolite_dust\": \"Nikolite Dust\",\n  \"item.indrev.nikolite_ingot\": \"Nikolite Ingot\",\n  \"block.indrev.nikolite_ore\": \"Nikolite Ore\",\n  \"item.indrev.modular.upgrade\": \"Modules installed:\",\n  \"item.indrev.modular.upgrade.night_vision\": \"Night Vision %s\",\n  \"item.indrev.modular.upgrade.speed\": \"Speed %s\",\n  \"item.indrev.modular.upgrade.jump_boost\": \"Jump Boost %s\",\n  \"item.indrev.modular.upgrade.breathing\": \"Breathing %s\",\n  \"item.indrev.modular.upgrade.protection\": \"Protection %s\",\n  \"item.indrev.modular.upgrade.feather_falling\": \"Feather Falling %s\",\n  \"item.indrev.modular.upgrade.auto_feeder\": \"Auto Feeder %s\",\n  \"item.indrev.modular.upgrade.charger\": \"Charger %s\",\n  \"item.indrev.modular.upgrade.solar_panel\": \"Solar Panel %s\",\n  \"item.indrev.modular.upgrade.piglin_tricker\": \"Piglin Tricker %s\",\n  \"item.indrev.modular.upgrade.elytra\": \"Builtin Elytra %s\",\n  \"item.indrev.modular.upgrade.jetpack\": \"Builtin Jetpack %s\",\n  \"item.indrev.modular.upgrade.magnet\": \"Magnet %s\",\n  \"item.indrev.modular.upgrade.water_affinity\": \"Water Affinity %s\",\n  \"item.indrev.modular.upgrade.fire_resistance\": \"Fire Resistance %s\",\n  \"item.indrev.modular.upgrade.efficiency\": \"Efficiency %s\",\n  \"item.indrev.modular.upgrade.range\": \"Range %s\",\n  \"item.indrev.modular.upgrade.fortune\": \"Fortune %s\",\n  \"item.indrev.modular.upgrade.silk_touch\": \"Silk Touch %s\",\n  \"item.indrev.modular.upgrade.sharpness\": \"Sharpness %s\",\n  \"item.indrev.modular.upgrade.looting\": \"Looting %s\",\n  \"item.indrev.modular.upgrade.fire_aspect\": \"Fire Aspect %s\",\n  \"item.indrev.modular.upgrade.reach\": \"Reach %s\",\n  \"item.indrev.steel_helmet\": \"Steel Helmet\",\n  \"item.indrev.steel_chestplate\": \"Steel Chestplate\",\n  \"item.indrev.steel_leggings\": \"Steel Leggings\",\n  \"item.indrev.steel_boots\": \"Steel Boots\",\n  \"item.indrev.steel_sword\": \"Steel Sword\",\n  \"item.indrev.steel_pickaxe\": \"Steel Pickaxe\",\n  \"item.indrev.steel_axe\": \"Steel Axe\",\n  \"item.indrev.steel_shovel\": \"Steel Shovel\",\n  \"item.indrev.steel_hoe\": \"Steel Hoe\",\n  \"item.indrev.bronze_helmet\": \"Bronze Helmet\",\n  \"item.indrev.bronze_chestplate\": \"Bronze Chestplate\",\n  \"item.indrev.bronze_leggings\": \"Bronze Leggings\",\n  \"item.indrev.bronze_boots\": \"Bronze Boots\",\n  \"item.indrev.bronze_sword\": \"Bronze Sword\",\n  \"item.indrev.bronze_pickaxe\": \"Bronze Pickaxe\",\n  \"item.indrev.bronze_axe\": \"Bronze Axe\",\n  \"item.indrev.bronze_shovel\": \"Bronze Shovel\",\n  \"item.indrev.bronze_hoe\": \"Bronze Hoe\",\n  \"item.indrev.lead_helmet\": \"Lead Helmet\",\n  \"item.indrev.lead_chestplate\": \"Lead Chestplate\",\n  \"item.indrev.lead_leggings\": \"Lead Leggings\",\n  \"item.indrev.lead_boots\": \"Lead Boots\",\n  \"item.indrev.lead_sword\": \"Lead Sword\",\n  \"item.indrev.lead_pickaxe\": \"Lead Pickaxe\",\n  \"item.indrev.lead_axe\": \"Lead Axe\",\n  \"item.indrev.lead_shovel\": \"Lead Shovel\",\n  \"item.indrev.lead_hoe\": \"Lead Hoe\",\n  \"item.indrev.silver_helmet\": \"Silver Helmet\",\n  \"item.indrev.silver_chestplate\": \"Silver Chestplate\",\n  \"item.indrev.silver_leggings\": \"Silver Leggings\",\n  \"item.indrev.silver_boots\": \"Silver Boots\",\n  \"item.indrev.silver_sword\": \"Silver Sword\",\n  \"item.indrev.silver_pickaxe\": \"Silver Pickaxe\",\n  \"item.indrev.silver_axe\": \"Silver Axe\",\n  \"item.indrev.silver_shovel\": \"Silver Shovel\",\n  \"item.indrev.silver_hoe\": \"Silver Hoe\",\n  \"block.indrev.modular_workbench\": \"Modular Workbench\",\n  \"block.indrev.modular_workbench_mk4\": \"Modular Workbench\",\n  \"item.indrev.modular_armor_helmet\": \"Modular Helmet\",\n  \"item.indrev.modular_armor_chest\": \"Modular Chestplate\",\n  \"item.indrev.modular_armor_legs\": \"Modular Leggings\",\n  \"item.indrev.modular_armor_boots\": \"Modular Boots\",\n  \"item.indrev.module_parts\": \"Can be installed on\",\n  \"item.indrev.module_parts_head\": \"Helmet\",\n  \"item.indrev.module_parts_chest\": \"Chestplate\",\n  \"item.indrev.module_parts_legs\": \"Legs\",\n  \"item.indrev.module_parts_feet\": \"Boots\",\n  \"item.indrev.module_parts_drill\": \"Mining Drill\",\n  \"item.indrev.module_parts_gamer_axe\": \"Gamer Axe\",\n  \"item.indrev.module_max_level\": \"Maximum level: %s\",\n  \"item.indrev.module_protection\": \"Protection Module\",\n  \"item.indrev.module_protection.tooltip\": \"Increases the Protection provided by the armor.\",\n  \"item.indrev.module_feather_falling\": \"Feather Falling Module\",\n  \"item.indrev.module_feather_falling.tooltip\": \"Fall damage is negated at cost of shield charge.\",\n  \"item.indrev.module_speed\": \"Speed Module\",\n  \"item.indrev.module_speed.tooltip\": \"Gives you Speed effect at cost of energy.\",\n  \"item.indrev.module_jump_boost\": \"Jump Boost Module\",\n  \"item.indrev.module_jump_boost.tooltip\": \"Gives you Jump Boost effect at cost of energy.\",\n  \"item.indrev.module_night_vision\": \"Night Vision Module\",\n  \"item.indrev.module_night_vision.tooltip\": \"Gives you Night Vision effect at cost of energy.\",\n  \"item.indrev.module_breathing\": \"Breathing Module\",\n  \"item.indrev.module_breathing.tooltip\": \"Gives you Breathing effect when underwater at cost of energy.\",\n  \"item.indrev.module_auto_feeder\": \"Auto Feeder Module\",\n  \"item.indrev.module_auto_feeder.tooltip\": \"Eats food from your inventory automatically at cost of energy.\",\n  \"item.indrev.module_charger\": \"Charger Module\",\n  \"item.indrev.module_charger.tooltip\": \"Recharges any items in your inventory.\",\n  \"item.indrev.module_solar_panel\": \"Solar Panel Module\",\n  \"item.indrev.module_solar_panel.tooltip\": \"Recharges your armor and held item when under sunlight.\",\n  \"item.indrev.module_piglin_tricker\": \"Piglin Tricker Module\",\n  \"item.indrev.module_piglin_tricker.tooltip\": \"Tricks Piglins into thinking you are wearing gold.\",\n  \"item.indrev.module_elytra\": \"Builtin Elytra Module\",\n  \"item.indrev.module_elytra.tooltip\": \"Installs a Reinforced Elytra on your chestplate.\",\n  \"item.indrev.module_jetpack\": \"Builtin Jetpack Module\",\n  \"item.indrev.module_jetpack.tooltip\": \"Installs a Jetpack on your chestplate.\",\n  \"item.indrev.module_magnet\": \"Magnet Module\",\n  \"item.indrev.module_magnet.tooltip\": \"Pulls items and experience towards you.\",\n  \"item.indrev.module_water_affinity\": \"Water Affinity Module\",\n  \"item.indrev.module_water_affinity.on\": \"When on %s\",\n  \"item.indrev.module_water_affinity.chestplate\": \"Chestplate\",\n  \"item.indrev.module_water_affinity.leggings\": \"Leggings\",\n  \"item.indrev.module_water_affinity.tooltip\": \"mine faster underwater.\",\n  \"item.indrev.module_water_affinity.tooltip1\": \"swim and walk faster underwater.\",\n  \"item.indrev.module_fire_resistance\": \"Fire Resistance Module\",\n  \"item.indrev.module_fire_resistance.tooltip\": \"Gives you Fire Resistance effect at cost of shield charge.\",\n  \"item.indrev.module_efficiency\": \"Efficiency Module\",\n  \"item.indrev.module_efficiency.tooltip\": \"Increases the speed of your mining tool.\",\n  \"item.indrev.module_range\": \"Range Module\",\n  \"item.indrev.module_range.tooltip\": \"Increases the Range of blocks your Drill can break at once.\",\n  \"item.indrev.module_fortune\": \"Fortune Module\",\n  \"item.indrev.module_fortune.tooltip\": \"Behaves like a Fortune enchantment.\",\n  \"item.indrev.module_silk_touch\": \"Silk Touch Module\",\n  \"item.indrev.module_silk_touch.tooltip\": \"Behaves like a Silk Touch enchantment.\",\n  \"item.indrev.module_reach\": \"Reach Module\",\n  \"item.indrev.module_reach.tooltip\": \"Damage multiple mobs at once.\",\n  \"item.indrev.module_looting\": \"Looting Module\",\n  \"item.indrev.module_looting.tooltip\": \"Behaves like a Looting enchantment.\",\n  \"item.indrev.module_fire_aspect\": \"Fire Aspect Module\",\n  \"item.indrev.module_fire_aspect.tooltip\": \"Behaves like a Fire Aspect enchantment.\",\n  \"item.indrev.module_sharpness\": \"Sharpness Module\",\n  \"item.indrev.module_sharpness.tooltip\": \"Increases the damage dealt.\",\n  \"item.indrev.module_color.tooltip\": \"§cC§eo§al§9o§6§dr§1s\",\n  \"item.indrev.module_color_pink\": \"Pink Color Module\",\n  \"item.indrev.module_color_red\": \"Red Color Module\",\n  \"item.indrev.module_color_purple\": \"Purple Color Module\",\n  \"item.indrev.module_color_blue\": \"Blue Color Module\",\n  \"item.indrev.module_color_cyan\": \"Cyan Color Module\",\n  \"item.indrev.module_color_green\": \"Green Color Module\",\n  \"item.indrev.module_color_yellow\": \"Yellow Color Module\",\n  \"item.indrev.module_color_orange\": \"Orange Color Module\",\n  \"item.indrev.module_color_black\": \"Black Color Module\",\n  \"item.indrev.module_color_brown\": \"Brown Color Module\",\n  \"item.indrev.copper_sword\": \"Copper Sword\",\n  \"item.indrev.copper_pickaxe\": \"Copper Pickaxe\",\n  \"item.indrev.copper_axe\": \"Copper Axe\",\n  \"item.indrev.copper_shovel\": \"Copper Shovel\",\n  \"item.indrev.copper_hoe\": \"Copper Hoe\",\n  \"item.indrev.copper_helmet\": \"Copper Helmet\",\n  \"item.indrev.copper_chestplate\": \"Copper Chestplate\",\n  \"item.indrev.copper_leggings\": \"Copper Leggings\",\n  \"item.indrev.copper_boots\": \"Copper Boots\",\n  \"item.indrev.tin_sword\": \"Tin Sword\",\n  \"item.indrev.tin_pickaxe\": \"Tin Pickaxe\",\n  \"item.indrev.tin_axe\": \"Tin Axe\",\n  \"item.indrev.tin_shovel\": \"Tin Shovel\",\n  \"item.indrev.tin_hoe\": \"Tin Hoe\",\n  \"item.indrev.tin_helmet\": \"Tin Helmet\",\n  \"item.indrev.tin_chestplate\": \"Tin Chestplate\",\n  \"item.indrev.tin_leggings\": \"Tin Leggings\",\n  \"item.indrev.tin_boots\": \"Tin Boots\",\n  \"category.indrev\": \"Industrial Revolution\",\n  \"key.indrev.modular\": \"Modular item configuration\",\n  \"key.indrev.gamer_axe_toggle\": \"Toggle Gamer Axe\",\n  \"text.multiblock.not_built\": \"Structure not built!\",\n  \"advancements.indrev.nikolite\": \"The Beginning\",\n  \"advancements.indrev.nikolite.description\": \"Find Nikolite\",\n  \"advancements.indrev.machine_block\": \"Foundations\",\n  \"advancements.indrev.machine_block.description\": \"Get a Machine Block\",\n  \"advancements.indrev.coal_generator\": \"First Steps\",\n  \"advancements.indrev.coal_generator.description\": \"Get a Coal Generator\",\n  \"advancements.indrev.basic_solar_generator\": \"Passive Energy\",\n  \"advancements.indrev.basic_solar_generator.description\": \"Get a Basic Solar Generator\",\n  \"advancements.indrev.advanced_solar_generator\": \"More Passive Energy\",\n  \"advancements.indrev.advanced_solar_generator.description\": \"Get an Advanced Solar Generator\",\n  \"advancements.indrev.heat_generator\": \"It's Getting Hotter\",\n  \"advancements.indrev.heat_generator.description\": \"Get a Heat Generator\",\n  \"advancements.indrev.biomass_generator\": \"Not so Eco-friendly\",\n  \"advancements.indrev.biomass_generator.description\": \"Get a Biomass Generator\",\n  \"advancements.indrev.pulverizer\": \"It's Getting Dusty\",\n  \"advancements.indrev.pulverizer.description\": \"Get a Pulverizer\",\n  \"advancements.indrev.electric_furnace\": \"Better Furnace\",\n  \"advancements.indrev.electric_furnace.description\": \"Get an Electric Furnace\",\n  \"advancements.indrev.compressor\": \"Watch Your Fingers\",\n  \"advancements.indrev.compressor.description\": \"Get a Compressor\",\n  \"advancements.indrev.recycler\": \"Eco-friendly\",\n  \"advancements.indrev.recycler.description\": \"Get a Recycler\",\n  \"advancements.indrev.solid_infuser\": \"Infusing\",\n  \"advancements.indrev.solid_infuser.description\": \"Get a Solid Infuser\",\n  \"advancements.indrev.mk2_upgrade\": \"Double Efficiency\",\n  \"advancements.indrev.mk2_upgrade.description\": \"Upgrade a machine to MK2\",\n  \"advancements.indrev.mk3_upgrade\": \"Triple Efficiency\",\n  \"advancements.indrev.mk3_upgrade.description\": \"Upgrade a machine to MK3\",\n  \"advancements.indrev.mk4_upgrade\": \"Factory. Must. Grow\",\n  \"advancements.indrev.mk4_upgrade.description\": \"Upgrade a machine to MK4\",\n  \"advancements.indrev.biomass\": \"Looks Gross...\",\n  \"advancements.indrev.biomass.description\": \"Get Biomass\",\n  \"advancements.indrev.nikolite_ingot\": \"New Horizons\",\n  \"advancements.indrev.nikolite_ingot.description\": \"Get Nikolite Ingot\",\n  \"advancements.indrev.enriched_nikolite_dust\": \"Powerful Electronics\",\n  \"advancements.indrev.enriched_nikolite_dust.description\": \"Get Enriched Nikolite Dust\",\n  \"advancements.indrev.enriched_nikolite_ingot\": \"More. Power.\",\n  \"advancements.indrev.enriched_nikolite_ingot.description\": \"Get Enriched Nikolite Ingot\",\n  \"advancements.indrev.lazuli_flux_container_mk1\": \"Basic Energy Storage\",\n  \"advancements.indrev.lazuli_flux_container_mk1.description\": \"Get a Lazuli Flux Container MK1\",\n  \"advancements.indrev.lazuli_flux_container_mk2\": \"Mediocre Energy Storage\",\n  \"advancements.indrev.lazuli_flux_container_mk2.description\": \"Get a Lazuli Flux Container MK2\",\n  \"advancements.indrev.lazuli_flux_container_mk3\": \"Not so Mediocre Energy Storage\",\n  \"advancements.indrev.lazuli_flux_container_mk3.description\": \"Get a Lazuli Flux Container MK3\",\n  \"advancements.indrev.lazuli_flux_container_mk4\": \"Ultimate Energy Storage\",\n  \"advancements.indrev.lazuli_flux_container_mk4.description\": \"Get a Lazuli Flux Container MK4\",\n  \"advancements.indrev.empty_enhancer\": \"A Pretty Plate\",\n  \"advancements.indrev.empty_enhancer.description\": \"Get an Empty Enhancer\",\n  \"advancements.indrev.speed_enhancer\": \"Machine go Brrrr\",\n  \"advancements.indrev.speed_enhancer.description\": \"Get a Speed Upgrade\",\n  \"advancements.indrev.buffer_enhancer\": \"Beyond the Limit\",\n  \"advancements.indrev.buffer_enhancer.description\": \"Get a Buffer Upgrade\",\n  \"advancements.indrev.energy_enhancer\": \"Always Efficient\",\n  \"advancements.indrev.energy_enhancer.description\": \"Get an Energy Upgrade\",\n  \"advancements.indrev.cable_mk1\": \"Yellow Cables!\",\n  \"advancements.indrev.cable_mk1.description\": \"Get a Cable MK1\",\n  \"advancements.indrev.cable_mk2\": \"Blue Cables!\",\n  \"advancements.indrev.cable_mk2.description\": \"Get a Cable MK2\",\n  \"advancements.indrev.cable_mk3\": \"Purple Cables!\",\n  \"advancements.indrev.cable_mk3.description\": \"Get a Cable MK3\",\n  \"advancements.indrev.cable_mk4\": \"Red Cables!\",\n  \"advancements.indrev.cable_mk4.description\": \"Get a Cable MK4\",\n  \"advancements.indrev.modular_workbench\": \"It Does not Look Like a Workbench\",\n  \"advancements.indrev.modular_workbench.description\": \"Get a Modular Workbench\",\n  \"advancements.indrev.modular_armor\": \"Modularity\",\n  \"advancements.indrev.modular_armor.description\": \"Get a Modular Armor full set\",\n  \"advancements.indrev.gamer_axe\": \"Not Enough RGB\",\n  \"advancements.indrev.gamer_axe.description\": \"Get a Gamer Axe\",\n  \"advancements.indrev.chopper_mk4\": \"Auto Wood Farm\",\n  \"advancements.indrev.chopper_mk4.description\": \"Get a Chopper\",\n  \"advancements.indrev.rancher_mk1\": \"I, Robot Farmer\",\n  \"advancements.indrev.rancher_mk1.description\": \"Get a Farmer\",\n  \"advancements.indrev.rancher_mk4\": \"Mechanized Production\",\n  \"advancements.indrev.rancher_mk4.description\": \"Get a Rancher\",\n  \"advancements.indrev.industrial_smelter\": \"Enhanced Ore Processing\",\n  \"advancements.indrev.industrial_smelter.description\": \"Get an Industrial Smelter\",\n  \"advancements.indrev.condenser\": \"I Ran Out of Ideas\",\n  \"advancements.indrev.condenser.description\": \"Get a Condenser\",\n  \"advancements.indrev.blast_furnace_enhancer\": \"Blast Electric Furnace\",\n  \"advancements.indrev.blast_furnace_enhancer.description\": \"Get the Smoking Upgrade\",\n  \"advancements.indrev.smoker_enhancer\": \"A Chef's Dream\",\n  \"advancements.indrev.smoker_enhancer.description\": \"Get the Smoking Upgrade\",\n  \"advancements.indrev.circuit_mk1\": \"First Machines\",\n  \"advancements.indrev.circuit_mk1.description\": \"Get a MK1 Circuit\",\n  \"advancements.indrev.circuit_mk2\": \"New Technology\",\n  \"advancements.indrev.circuit_mk2.description\": \"Get a MK2 Circuit\",\n  \"advancements.indrev.circuit_mk3\": \"Advanced Technology\",\n  \"advancements.indrev.circuit_mk3.description\": \"Get a MK3 Circuit\",\n  \"advancements.indrev.circuit_mk4\": \"Ultimate Technology\",\n  \"advancements.indrev.circuit_mk4.description\": \"Get a MK4 Circuit\",\n  \"advancements.indrev.farmer_mk1\": \"Happy Little Farm\",\n  \"advancements.indrev.farmer_mk1.description\": \"Get a MK1 Farmer\",\n  \"advancements.indrev.slaughter_mk1\": \"Ultimate Mob Killer\",\n  \"advancements.indrev.slaughter_mk1.description\": \"Get a MK1 Slaughter\",\n  \"gui.indrev.tip\": \"TIP \",\n  \"gui.indrev.tip_0\": \"Make sure to configure fluid and item input and output\",\n  \"gui.indrev.tip_1\": \"Do not use cables above the input limit\",\n  \"gui.indrev.tip_2\": \"Use coolers like a cool guy\",\n  \"gui.indrev.tip_3\": \"Use coolers to never loose efficiency\",\n  \"gui.indrev.tip_4\": \"Factory. Must. Grow.\",\n  \"gui.indrev.tip_5\": \"You should check out the Modular Armor\",\n  \"gui.indrev.tip_6\": \"Real gamers use the Gamer Axe\",\n  \"gui.indrev.tip_7\": \"If you have issues or suggestions join our Discord!\",\n  \"gui.indrev.tip_8\": \"Always backup your worlds before updating\",\n  \"gui.indrev.tip_9\": \"Cover your toes at night\",\n  \"gui.indrev.modules_installed\": \"Modules installed: \",\n  \"gui.indrev.shield\": \"Shield: \",\n  \"gui.indrev.installing\": \"Installing\",\n  \"gui.indrev.incompatible\": \"Cannot be installed\",\n  \"gui.indrev.max_level\": \"Maximum level hit\",\n  \"gui.indrev.progress\": \"Progress: \",\n  \"gui.indrev.whitelist.true\": \"Whitelist\",\n  \"gui.indrev.whitelist.false\": \"Blacklist\",\n  \"gui.indrev.matchDurability.true\": \"Match item damage\",\n  \"gui.indrev.matchDurability.false\": \"Ignore item damage\",\n  \"gui.indrev.matchTag.true\": \"Match item NBT\",\n  \"gui.indrev.matchTag.false\": \"Ignore item NBT\",\n  \"death.attack.acid\": \"%s was dissolved in sulfuric acid\",\n  \"death.attack.acid.player\": \"%s was dissolved in sulfuric acid while trying to escape %s\",\n  \"death.attack.laser\": \"%s ran into the laser and got fried.\",\n  \"death.attack.laser.player\": \"%s ran into the laser and got fried while trying to escape %s\",\n  \"vein.indrev.bauxite\": \"Bauxite\",\n  \"vein.indrev.certus_quartz\": \"Certus Quartz\",\n  \"vein.indrev.peat\": \"Peat\",\n  \"vein.indrev.lignite\": \"Lignite\",\n  \"vein.indrev.bituminous\": \"Bituminous\",\n  \"vein.indrev.anthracite\": \"Anthracite\",\n  \"vein.indrev.cuprite\": \"Cuprite\",\n  \"vein.indrev.calaverite\": \"Calaverite\",\n  \"vein.indrev.calaverite_nether\": \"Calaverite (Nether)\",\n  \"vein.indrev.iridium\": \"Iridium\",\n  \"vein.indrev.siderite\": \"Siderite\",\n  \"vein.indrev.limonite\": \"Limonite\",\n  \"vein.indrev.hematite\": \"Hematite\",\n  \"vein.indrev.magnetite\": \"Magnetite\",\n  \"vein.indrev.chalcopryte\": \"Chalcopryte\",\n  \"vein.indrev.nikolite\": \"Nikolite\",\n  \"vein.indrev.quartz\": \"Quartz\",\n  \"vein.indrev.argentite\": \"Argentite\",\n  \"vein.indrev.chlorargyrite\": \"Chlorargyrite\",\n  \"vein.indrev.cassiterite\": \"Cassiterite\",\n  \"vein.indrev.stannite\": \"Stannite\",\n  \"vein.indrev.scheelite\": \"Scheelite\",\n  \"vein.indrev.wolframite\": \"Wolframite\",\n  \"vein.indrev.ferberite\": \"Ferberite\",\n  \"vein.indrev.galena\": \"Galena\",\n  \"vein.indrev.glowstonedeposit\": \"Glowstone Deposit\",\n  \"vein.indrev.soul_nether\": \"Nether Soul Deposit\",\n  \"vein.indrev.sulfur_nether\": \"Sulfur\",\n  \"vein.indrev.denseice\": \"Dense Ice Deposit\",\n  \"attribute.indrev.shield\": \"Shield\",\n  \"subtitles.indrev.laser\": \"Laser beam\",\n  \"item.indrev.reinforced_elytra\": \"Reinforced Elytra\",\n  \"item.indrev.jetpack_mk1\": \"Jetpack MK1\",\n  \"item.indrev.jetpack_mk2\": \"Jetpack MK2\",\n  \"item.indrev.jetpack_mk3\": \"Jetpack MK3\",\n  \"item.indrev.jetpack_mk4\": \"Jetpack MK4\",\n  \"block.indrev.gas_generator\": \"Gas Burning Generator\",\n  \"block.indrev.gas_generator_mk4\": \"Gas Burning Generator\",\n  \"item.indrev.soot\": \"Soot\",\n  \"item.indrev.carbon_fiber_plate\": \"Carbon Fiber Plate\",\n  \"item.indrev.carbon_fiber_rod\": \"Carbon Fiber Rod\",\n  \"item.indrev.carbon_fiber_helmet_frame\": \"Carbon Fiber Chestplate Frame\",\n  \"item.indrev.carbon_fiber_chest_frame\": \"Carbon Fiber Helmet Frame\",\n  \"item.indrev.carbon_fiber_legs_frame\": \"Carbon Fiber Leggings Frame\",\n  \"item.indrev.carbon_fiber_boots_frame\": \"Carbon Fiber Boots Frame\",\n  \"subtitles.indrev.laser\": \"Laser beam\",\n  \"block.indrev.boiler\": \"Boiler\",\n  \"block.indrev.steam_turbine_mk4\": \"Steam Turbine\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/lang/pt_br.json",
    "content": "{\n  \"item.patchouli.industrial_revolution_book.name\": \"Guia do revolucionário\",\n  \"item.patchouli.industrial_revolution_book.landing\": \"Este livro irá lhe ajudar em aspectos importantes do mod.$(br2) Se houver algum problema, dúvidas ou sugestões, abra uma issue em $(l:https://github.com/GabrielOlvH/Industrial-Revolution/issues)GitHub$() ou entre no nosso $(l:https://discord.com/invite/G4PjhEf)Discord!$()$(br2)Obrigado por jogar!\",\n  \"itemGroup.indrev.indrev_group\": \"Revolução industrial\",\n  \"block.machines.tooltip.io\": \"I/O: %s\",\n  \"block.indrev.coal_generator\": \"Gerador a carvão\",\n  \"block.indrev.coal_generator_mk1\": \"Gerador a carvão\",\n  \"block.indrev.heat_generator\": \"Gerador a calor\",\n  \"block.indrev.heat_generator_mk4\": \"Gerador a calor\",\n  \"block.indrev.electric_furnace\": \"Fornalha elétrica\",\n  \"block.indrev.electric_furnace_mk1\": \"Fornalha elétrica MK1\",\n  \"block.indrev.electric_furnace_mk2\": \"Fornalha elétrica MK2\",\n  \"block.indrev.electric_furnace_mk3\": \"Fornalha elétrica MK3\",\n  \"block.indrev.electric_furnace_mk4\": \"Fornalha elétrica MK4\",\n  \"block.indrev.pulverizer\": \"Pulverizador\",\n  \"block.indrev.pulverizer_mk1\": \"Pulverizador MK1\",\n  \"block.indrev.pulverizer_mk2\": \"Pulverizador MK2\",\n  \"block.indrev.pulverizer_mk3\": \"Pulverizador MK3\",\n  \"block.indrev.pulverizer_mk4\": \"Pulverizador MK4\",\n  \"block.indrev.compressor\": \"Compressor\",\n  \"block.indrev.compressor_mk1\": \"Compressor MK1\",\n  \"block.indrev.compressor_mk2\": \"Compressor MK2\",\n  \"block.indrev.compressor_mk3\": \"Compressor MK3\",\n  \"block.indrev.compressor_mk4\": \"Compressor MK4\",\n  \"block.indrev.infuser\": \"Infusor\",\n  \"block.indrev.infuser_mk1\": \"Infusor MK1\",\n  \"block.indrev.infuser_mk2\": \"Infusor MK2\",\n  \"block.indrev.infuser_mk3\": \"Infusor MK3\",\n  \"block.indrev.infuser_mk4\": \"Infusor MK4\",\n  \"block.indrev.lazuli_flux_container_mk1\": \"Reservatório de Lazuli Flux MK1\",\n  \"block.indrev.lazuli_flux_container_mk2\": \"Reservatório de Lazuli Flux MK2\",\n  \"block.indrev.lazuli_flux_container_mk3\": \"Reservatório de Lazuli Flux MK3\",\n  \"block.indrev.lazuli_flux_container_mk4\": \"Reservatório de Lazuli Flux MK4\",\n  \"block.indrev.cable_mk1\": \"Cabo MK1\",\n  \"block.indrev.cable_mk2\": \"Cabo MK2\",\n  \"block.indrev.cable_mk3\": \"Cabo MK3\",\n  \"block.indrev.cable_mk4\": \"Cabo MK4\",\n  \"block.indrev.fishing_farm\": \"Pescadora\",\n  \"block.indrev.fishing_farm_mk2\": \"Pescadora simples\",\n  \"block.indrev.fishing_farm_mk3\": \"Pescadora aprimorada\",\n  \"block.indrev.fishing_farm_mk4\": \"Pescadora avançada\",\n  \"block.indrev.miner\": \"Mineradora\",\n  \"block.indrev.miner_mk4\": \"Mineradora\",\n  \"block.indrev.ender_miner_mk4\": \"Mineradora de ender\",\n  \"block.indrev.miner.tooltip\": \"Você deve escanear esse chunk com um Analisador de terreno antes de usar a mineradora.\",\n  \"block.indrev.miner.mined\": \"Minerado: %d\",\n  \"block.indrev.recycler\": \"Recicladora\",\n  \"block.indrev.recycler_mk2\": \"Recicladora\",\n  \"block.indrev.cable\": \"Cabo\",\n  \"block.indrev.chopper\": \"Lenhadora\",\n  \"block.indrev.chopper_mk4\": \"Lenhadora\",\n  \"block.indrev.rancher\": \"Rancheiro\",\n  \"block.indrev.rancher_mk4\": \"Rancheiro\",\n  \"block.indrev.biomass_generator\": \"Gerador a biomassa\",\n  \"block.indrev.biomass_generator_mk3\": \"Gerador a biomassa\",\n  \"block.indrev.solar_generator\": \"Gerador solar\",\n  \"block.indrev.solar_generator_mk1\": \"Gerador solar\",\n  \"block.indrev.solar_generator_mk3\": \"Gerador solar\",\n  \"block.indrev.charge_pad_mk4\": \"Pedestal de carregamento\",\n  \"block.indrev.charge_pad_mk4.tooltip\": \"Recarrega itens equipados ou que forem colocados nele.\",\n  \"block.indrev.aoe.toggle.btn\": \"R\",\n  \"block.indrev.aoe.toggle.false\": \"Mostrar alcance\",\n  \"block.indrev.aoe.toggle.true\": \"Esconder alcance\",\n  \"block.indrev.coolant\": \"Refrigerante\",\n  \"block.indrev.molten_netherite\": \"Netherita derretida\",\n  \"block.indrev.machine_block\": \"Bloco de máquina\",\n  \"block.indrev.tin_ore\": \"Minério de estanho\",\n  \"block.indrev.tin_block\": \"Bloco de estanho\",\n  \"item.indrev.tin_ingot\": \"Barra de estanho\",\n  \"item.indrev.tin_dust\": \"Pó de estanho\",\n  \"item.indrev.tin_plate\": \"Chapa de estanho\",\n  \"item.indrev.tin_gear\": \"Engrenagem de estanho\",\n  \"block.indrev.copper_ore\": \"Minério de cobre\",\n  \"block.indrev.copper_block\": \"bloco de cobre\",\n  \"item.indrev.copper_ingot\": \"Barra de cobre\",\n  \"item.indrev.copper_dust\": \"Pó de cobre\",\n  \"item.indrev.copper_plate\": \"Chapa de cobre\",\n  \"item.indrev.copper_gear\": \"Engrenagem de cobre\",\n  \"block.indrev.steel_block\": \"Bloco de aço\",\n  \"item.indrev.steel_dust\": \"Pó de aço\",\n  \"item.indrev.steel_ingot\": \"Barra de aço\",\n  \"item.indrev.steel_plate\": \"Chapa de aço\",\n  \"item.indrev.steel_gear\": \"Engrenagem de aço\",\n  \"item.indrev.gold_dust\": \"Pó de ouro\",\n  \"item.indrev.gold_plate\": \"Chapa de ouro\",\n  \"item.indrev.iron_dust\": \"Pó de ferro\",\n  \"item.indrev.iron_plate\": \"Chapa de ferro\",\n  \"item.indrev.iron_gear\": \"Engrenagem de ferro\",\n  \"item.indrev.diamond_dust\": \"Pó de diamante\",\n  \"item.indrev.coal_dust\": \"Pó de carvão\",\n  \"item.indrev.empty_upgrade\": \"Módulo de melhoria\",\n  \"item.indrev.buffer_upgrade\": \"Melhoria de armazenamento\",\n  \"item.indrev.buffer_upgrade.tooltip\": \"Aumenta o armazenamento de energia da máquina\",\n  \"item.indrev.energy_upgrade\": \"Melhoria de eficiência de energia\",\n  \"item.indrev.energy_upgrade.tooltip\": \"Faz com que a máquina gaste menos energia\",\n  \"item.indrev.speed_upgrade\": \"Melhoria de velocidade\",\n  \"item.indrev.speed_upgrade.tooltip\": \"Diminui o tempo de processamento da máquina\",\n  \"item.indrev.rechargeable.tooltip\": \"Recarregável\",\n  \"item.indrev.chunk_scanner\": \"Escaneador de terreno\",\n  \"item.indrev.chunk_scanner.scanning\": \"Escaneando...\",\n  \"item.indrev.chunk_scanner.scanned1\": \"Escaneamento concluído!\",\n  \"item.indrev.chunk_scanner.scanned2\": \"Depósito de %s\",\n  \"item.indrev.chunk_scanner.already_scanned\": \"Este chunk já foi escaneado. Ele contém %s\",\n  \"item.indrev.chunk_scanner.tooltip1\": \"Segure clique direito para escanear este chunk\",\n  \"item.indrev.chunk_scanner.tooltip2\": \"Depósito de %s\",\n  \"item.indrev.chunk_scanner.tooltip3\": \"De %s até %s\",\n  \"item.indrev.chunk_scanner.tooltip4\": \"Dimensão: %s\",\n  \"item.indrev.fan\": \"Ventilador\",\n  \"item.indrev.cooler_cell\": \"Célula de refrigeração\",\n  \"item.indrev.heatsink\": \"Dissipador\",\n  \"item.indrev.mining_drill_mk1\": \"Broca de mineração MK1\",\n  \"item.indrev.mining_drill_mk2\": \"Broca de mineração MK2\",\n  \"item.indrev.mining_drill_mk3\": \"Broca de mineração MK3\",\n  \"item.indrev.mining_drill_mk4\": \"Broca de mineração MK4\",\n  \"item.indrev.energy_reader\": \"Leitor de energia\",\n  \"item.indrev.energy_reader.use\": \"Energia armazenada:\",\n  \"item.indrev.enriched_nikolite_dust\": \"Pó de Nikolita enriquecida\",\n  \"item.indrev.enriched_nikolite_ingot\": \"Barra Nikolita enriquecida\",\n  \"item.indrev.battery\": \"Bateria\",\n  \"item.indrev.circuit_mk1\": \"Circuito MK1\",\n  \"item.indrev.circuit_mk2\": \"Circuito MK2\",\n  \"item.indrev.circuit_mk3\": \"Circuito MK3\",\n  \"item.indrev.circuit_mk4\": \"Circuito MK4\",\n  \"item.indrev.tier_upgrade_mk2\": \"Melhoria de máquina MK2\",\n  \"item.indrev.tier_upgrade_mk2.tooltip\": \"Atualiza uma máquina MK1 para MK2\",\n  \"item.indrev.tier_upgrade_mk3\": \"Melhoria de máquina MK3\",\n  \"item.indrev.tier_upgrade_mk3.tooltip\": \"Atualiza uma máquina MK2 para MK3\",\n  \"item.indrev.tier_upgrade_mk4\": \"Melhoria de máquina MK4\",\n  \"item.indrev.tier_upgrade_mk4.tooltip\": \"Atualiza uma máquina MK3 para MK4\",\n  \"item.indrev.biomass\": \"Biomassa\",\n  \"item.indrev.coolant_bucket\": \"Balde de líquido refrigerante\",\n  \"item.indrev.molten_netherite_bucket\": \"Balde de Netherita derretida\",\n  \"item.indrev.hammer\": \"Martelo\",\n  \"item.indrev.wrench\": \"Chave inglesa\",\n  \"item.indrev.wrench.switch_mode\": \"Modo da chave: %s\",\n  \"item.indrev.wrench.title\": \"Configurar máquina\",\n  \"item.indrev.wrench.output\": \"Saída\",\n  \"item.indrev.wrench.input\": \"Entrada\",\n  \"item.indrev.wrench.input_output\": \"Entrada e saída\",\n  \"item.indrev.wrench.none\": \"Vazio\",\n  \"item.indrev.wrench.tooltip\": \"Shift + clique direito para trocar de modo\",\n  \"item.indrev.wrench.mode\": \"Modo: %s\",\n  \"item.indrev.tech_soup\": \"Sopa eletrônica\",\n  \"item.indrev.scan_output\": \"Escanear saída\",\n  \"item.indrev.portable_charger\": \"Carregador portátil\",\n  \"item.indrev.gamer_axe\": \"Machado gamer\",\n  \"gui.widget.energy\": \"Energia\",\n  \"gui.widget.temperature\": \"Temperatura\",\n  \"gui.widget.temperature_info.high\": \"Muito quente!\",\n  \"gui.widget.temperature_info.medium\": \"Potencial máximo\",\n  \"gui.widget.temperature_info.low\": \"Baixa temperatura\",\n  \"gui.indrev.guide_book_shortcut.contains\": \"Abre a página referente no livro de ajuda\",\n  \"gui.indrev.guide_book_shortcut.missing\": \"É necessário ter o Guia do revolucionário!\",\n  \"gui.indrev.tooltip.maxInput\": \"Limite de entrada: \",\n  \"gui.indrev.tooltip.maxOutput\": \"Limite de saída: \",\n  \"gui.indrev.tooltip.maxEnergyStored\": \"Capacidade de armazenamento: \",\n  \"gui.indrev.tooltip.energyCost\": \"Energia necessária: \",\n  \"gui.indrev.tooltip.processSpeed\": \"Velocidade: \",\n  \"gui.indrev.tooltip.ratio\": \"Geração: \",\n  \"gui.indrev.tooltip.press_shift\": \"Segure Shift para mais informações\",\n  \"gui.indrev.tooltip.lftick\": \"%s LF/tick\",\n  \"gui.indrev.tooltip.lf\": \"%s LF\",\n  \"gui.indrev.modular_armor_slot_type\": \"Coloque aqui a peça da sua armadura modular\",\n  \"gui.indrev.module_slot_type\": \"Coloque aqui o módulo desejado\",\n  \"gui.indrev.scan_output_slot_type\": \"Coloque aqui os resultados do escanemanto\",\n  \"gui.indrev.output_slot_type\": \"Slot de saída\",\n  \"gui.indrev.chopper_input_slot_type\": \"Compartimento de machado, muda e/ou farinha de osso\",\n  \"gui.indrev.cooler_slot_type\": \"Espaço para resfriadores\",\n  \"gui.indrev.battery_slot_type\": \"Espaço para baterias\",\n  \"gui.indrev.upgrade_slot_type\": \"Espaço para melhorias\",\n  \"gui.indrev.fishingrod\": \"Compartimento de vara de pesca\",\n  \"indrev.category.rei.pulverizing\": \"Pulverizando\",\n  \"indrev.category.rei.infusing\": \"Infudindo\",\n  \"indrev.category.rei.compressing\": \"Comprimindo\",\n  \"indrev.category.rei.recycling\": \"Reciclando\",\n  \"item.indrev.nikolite_dust\": \"Pó de Nikolita\",\n  \"item.indrev.nikolite_ingot\": \"Barra de Nikolita\",\n  \"block.indrev.nikolite_ore\": \"Minério de Nikolita\",\n  \"item.indrev.modular_armor.upgrade\": \"Módulos instalados:\",\n  \"item.indrev.modular_armor.upgrade.night_vision\": \"Visão noturna %s\",\n  \"item.indrev.modular_armor.upgrade.speed\": \"Velocidade %s\",\n  \"item.indrev.modular_armor.upgrade.jump_boost\": \"Supersalto %s\",\n  \"item.indrev.modular_armor.upgrade.breathing\": \"Respiração %s\",\n  \"item.indrev.modular_armor.upgrade.protection\": \"Proteção %s\",\n  \"item.indrev.modular_armor.upgrade.feather_falling\": \"Peso-pena %s\",\n  \"item.indrev.modular_armor.upgrade.auto_feeder\": \"Alimentação %s\",\n  \"item.indrev.modular_armor.upgrade.charger\": \"Carregador %s\",\n  \"item.indrev.modular_armor.upgrade.solar_panel\": \"Painel solar %s\",\n  \"item.indrev.modular_armor.upgrade.piglin_tricker\": \"Enganar piglin %s\",\n  \"item.indrev.modular_armor.upgrade.fire_resistance\": \"Resistência a fogo %s\",\n  \"item.indrev.steel_helmet\": \"Capacete de aço\",\n  \"item.indrev.steel_chestplate\": \"Peitoral de aço\",\n  \"item.indrev.steel_leggings\": \"Calças de aço\",\n  \"item.indrev.steel_boots\": \"Botas de aço\",\n  \"item.indrev.steel_sword\": \"Espada de aço\",\n  \"item.indrev.steel_pickaxe\": \"Picareta de aço\",\n  \"item.indrev.steel_axe\": \"Machado de aço\",\n  \"item.indrev.steel_shovel\": \"Pá de aço\",\n  \"item.indrev.steel_hoe\": \"Enxada de aço\",\n  \"block.indrev.modular_workbench\": \"Bancada modular\",\n  \"block.indrev.modular_workbench_mk4\": \"Bancada modular\",\n  \"item.indrev.modular_armor_helmet\": \"Capacete modular\",\n  \"item.indrev.modular_armor_chest\": \"Peitoral modular\",\n  \"item.indrev.modular_armor_legs\": \"Calças Modulares\",\n  \"item.indrev.modular_armor_boots\": \"Botas Modulares\",\n  \"item.indrev.module_parts\": \"Pode ser instalado em\",\n  \"item.indrev.module_parts_head\": \"Capacete\",\n  \"item.indrev.module_parts_chest\": \"Peitoral\",\n  \"item.indrev.module_parts_legs\": \"Calças\",\n  \"item.indrev.module_parts_feet\": \"Botas\",\n  \"item.indrev.module_max_level\": \"Nível máximo: %s\",\n  \"item.indrev.module_protection\": \"Módulo de proteção\",\n  \"item.indrev.module_protection.tooltip\": \"Aumenta a proteção da armadura\",\n  \"item.indrev.module_feather_falling\": \"Módulo peso-pena\",\n  \"item.indrev.module_feather_falling.tooltip\": \"Absorve o dano de queda com um custo extra de energia\",\n  \"item.indrev.module_speed\": \"Módulo de velocidade\",\n  \"item.indrev.module_speed.tooltip\": \"Sua velocidade aumenta. O gasto de energia também aumenta durante o uso\",\n  \"item.indrev.module_jump_boost\": \"Módulo de supersalto\",\n  \"item.indrev.module_jump_boost.tooltip\": \"Concede superpulo com um custo extra de energia\",\n  \"item.indrev.module_night_vision\": \"Módulo de visão noturna\",\n  \"item.indrev.module_night_vision.tooltip\": \"Concede visão noturna. O gasto de energia aumenta durante o uso\",\n  \"item.indrev.module_breathing\": \"Módulo de respiração\",\n  \"item.indrev.module_breathing.tooltip\": \"Permite respirar debaixo d'agua. O gasto de energia aumenta durante o uso\",\n  \"item.indrev.module_auto_feeder\": \"Módulo de alimentação\",\n  \"item.indrev.module_auto_feeder.tooltip\": \"Come automaticamente do inventário com um custo extra de energia\",\n  \"item.indrev.module_charger\": \"Módulo de carregamento\",\n  \"item.indrev.module_charger.tooltip\": \"Recarrega itens do seu inventário (não inclui armaduras)\",\n  \"item.indrev.module_solar_panel\": \"Módulo de painel solar\",\n  \"item.indrev.module_solar_panel.tooltip\": \"Recarrega a armadura e o item sendo segurado quando sob a luz do sol\",\n  \"item.indrev.module_piglin_tricker\": \"Módulo de enganar piglin\",\n  \"item.indrev.module_piglin_tricker.tooltip\": \"Faz os piglins pensarem que você está vestindo ouro\",\n  \"item.indrev.module_fire_resistance\": \"Módulo de resistência a fogo\",\n  \"item.indrev.module_fire_resistance.tooltip\": \"Concede resistência a fogo. O gasto de energia aumenta durante o uso\",\n  \"item.indrev.module_color.tooltip\": \"§cC§eo§ar§9e§6§ds\",\n  \"item.indrev.module_color_pink\": \"Módulo de cor rosa\",\n  \"item.indrev.module_color_red\": \"Módulo de cor vermelho\",\n  \"item.indrev.module_color_purple\": \"Módulo de cor roxo\",\n  \"item.indrev.module_color_blue\": \"Módulo de cor azul\",\n  \"item.indrev.module_color_cyan\": \"Módulo de cor ciano\",\n  \"item.indrev.module_color_green\": \"Módulo de cor verde\",\n  \"item.indrev.module_color_yellow\": \"Módulo de cor amarelo\",\n  \"item.indrev.module_color_orange\": \"Módulo de cor laranja\",\n  \"item.indrev.module_color_black\": \"Módulo de cor preto\",\n  \"item.indrev.module_color_brown\": \"Módulo de cor marrom\",\n  \"item.indrev.copper_sword\": \"Espada de cobre\",\n  \"item.indrev.copper_pickaxe\": \"Picareta de cobre\",\n  \"item.indrev.copper_axe\": \"Machado de cobre\",\n  \"item.indrev.copper_shovel\": \"Pá de cobre\",\n  \"item.indrev.copper_hoe\": \"Enxada de cobre\",\n  \"item.indrev.copper_helmet\": \"Capacete de cobre\",\n  \"item.indrev.copper_chestplate\": \"Peitoral de cobre\",\n  \"item.indrev.copper_leggings\": \"Calças de cobre\",\n  \"item.indrev.copper_boots\": \"Botas de cobre\",\n  \"item.indrev.tin_sword\": \"Espada de estanho\",\n  \"item.indrev.tin_pickaxe\": \"Picareta de estanho\",\n  \"item.indrev.tin_axe\": \"Machado de estanho\",\n  \"item.indrev.tin_shovel\": \"Pá de estanho\",\n  \"item.indrev.tin_hoe\": \"Enxada de estanho\",\n  \"item.indrev.tin_helmet\": \"Capacete de estanho\",\n  \"item.indrev.tin_chestplate\": \"Peitoral de estanho\",\n  \"item.indrev.tin_leggings\": \"Calças de estanho\",\n  \"item.indrev.tin_boots\": \"Botas de estanho\",\n  \"advancements.indrev.nikolite\": \"O início\",\n  \"advancements.indrev.nikolite.description\": \"Encontre nikolita\",\n  \"advancements.indrev.machine_block\": \"Fundações\",\n  \"advancements.indrev.machine_block.description\": \"Adquira um bloco de máquina\",\n  \"advancements.indrev.coal_generator\": \"Temos que começar de algum lugar\",\n  \"advancements.indrev.coal_generator.description\": \"Adquira um gerador a carvão\",\n  \"advancements.indrev.basic_solar_generator\": \"Energia sustentável\",\n  \"advancements.indrev.basic_solar_generator.description\": \"Adquira um gerador solar simples\",\n  \"advancements.indrev.advanced_solar_generator\": \"Mais sustentabilidade\",\n  \"advancements.indrev.advanced_solar_generator.description\": \"Adquira um gerador solar avançado\",\n  \"advancements.indrev.heat_generator\": \"Tá pegando fogo, bicho!\",\n  \"advancements.indrev.heat_generator.description\": \"Adquira um gerador a calor\",\n  \"advancements.indrev.biomass_generator\": \"Quem liga pro ecossistema?\",\n  \"advancements.indrev.biomass_generator.description\": \"Adquira um Gerador a biomassa\",\n  \"advancements.indrev.pulverizer\": \"Ficou só o pó\",\n  \"advancements.indrev.pulverizer.description\": \"Adquira um pulverizador\",\n  \"advancements.indrev.electric_furnace\": \"É uma fornalha... Só que melhor!\",\n  \"advancements.indrev.electric_furnace.description\": \"Adquira uma fornalha elétrica\",\n  \"advancements.indrev.compressor\": \"Cuidado com os dedos\",\n  \"advancements.indrev.compressor.description\": \"Adquira um compressor\",\n  \"advancements.indrev.recycler\": \"Camarada do meio ambiente\",\n  \"advancements.indrev.recycler.description\": \"Get a Recycler\",\n  \"advancements.indrev.infuser\": \"Mistureba\",\n  \"advancements.indrev.infuser.description\": \"Adquira um infusor\",\n  \"advancements.indrev.mk2_upgrade\": \"Duas vezes melhor!\",\n  \"advancements.indrev.mk2_upgrade.description\": \"Upgrade a machine to MK2\",\n  \"advancements.indrev.mk3_upgrade\": \"TRÊS VEZES MELHOR!\",\n  \"advancements.indrev.mk3_upgrade.description\": \"Upgrade a machine to MK3\",\n  \"advancements.indrev.mk4_upgrade\": \"PRODUZIR! PRODUZIR! PRODUZIR!\",\n  \"advancements.indrev.mk4_upgrade.description\": \"Upgrade a machine to MK4\",\n  \"advancements.indrev.biomass\": \"É melhor não comer isso\",\n  \"advancements.indrev.biomass.description\": \"Produza biomassa\",\n  \"advancements.indrev.nikolite_ingot\": \"Novos horizontes\",\n  \"advancements.indrev.nikolite_ingot.description\": \"Adquira Barra de nikolita\",\n  \"advancements.indrev.enriched_nikolite_dust\": \"Eletrônicos poderosos\",\n  \"advancements.indrev.enriched_nikolite_dust.description\": \"Adquira Pó de nikolita enriquecida\",\n  \"advancements.indrev.enriched_nikolite_ingot\": \"É TANTO PODER!\",\n  \"advancements.indrev.enriched_nikolite_ingot.description\": \"Adquira Barra de nikolita enriquecida\",\n  \"advancements.indrev.lazuli_flux_container_mk1\": \"É melhor do que nada\",\n  \"advancements.indrev.lazuli_flux_container_mk1.description\": \"Adquira um Reservatório de Lazuli Flux MK1\",\n  \"advancements.indrev.lazuli_flux_container_mk2\": \"Isso deve dar conta\",\n  \"advancements.indrev.lazuli_flux_container_mk2.description\": \"Adquira um Reservatório de Lazuli Flux MK2\",\n  \"advancements.indrev.lazuli_flux_container_mk3\": \"Estamos chegando em algum lugar\",\n  \"advancements.indrev.lazuli_flux_container_mk3.description\": \"Adquira um Reservatório de Lazuli Flux MK3\",\n  \"advancements.indrev.lazuli_flux_container_mk4\": \"Energia ilimitada! Ou quase\",\n  \"advancements.indrev.lazuli_flux_container_mk4.description\": \"Adquira um Reservatório de Lazuli Flux MK4\",\n  \"advancements.indrev.empty_upgrade\": \"Acho que ficaria bonita na parede\",\n  \"advancements.indrev.empty_upgrade.description\": \"Adquira um módulo de melhoria\",\n  \"advancements.indrev.speed_upgrade\": \"Taca-le pau nessa maquininha\",\n  \"advancements.indrev.speed_upgrade.description\": \"Adquira uma melhoria de velocidade\",\n  \"advancements.indrev.buffer_upgrade\": \"Além do alcance\",\n  \"advancements.indrev.buffer_upgrade.description\": \"Adquira uma melhoria de armazenamento\",\n  \"advancements.indrev.energy_upgrade\": \"Eficiência sempre\",\n  \"advancements.indrev.energy_upgrade.description\": \"Adquira uma melhoria de energia\",\n  \"advancements.indrev.cable_mk1\": \"Sem risco de choque\",\n  \"advancements.indrev.cable_mk1.description\": \"Adquira um cabo MK1\",\n  \"advancements.indrev.cable_mk2\": \"Gerenciamento de cabos\",\n  \"advancements.indrev.cable_mk2.description\": \"Adquira um cabo MK2\",\n  \"advancements.indrev.cable_mk3\": \"O que poderia ser melhor do que cabos roxos?\",\n  \"advancements.indrev.cable_mk3.description\": \"Adquira um cabo MK3\",\n  \"advancements.indrev.cable_mk4\": \"Cabos vermelhos, claro!\",\n  \"advancements.indrev.cable_mk4.description\": \"Adquira um cabo MK4\",\n  \"advancements.indrev.modular_workbench\": \"Uma bancada diferenciada, eu diria\",\n  \"advancements.indrev.modular_workbench.description\": \"Adquira uma bancada modular\",\n  \"advancements.indrev.modular_armor\": \"Modularidade\",\n  \"advancements.indrev.modular_armor.description\": \"Adquira uma armadura modular completa\",\n  \"advancements.indrev.gamer_axe\": \"Preciso de mais luzes\",\n  \"advancements.indrev.gamer_axe.description\": \"Adquira um Machado gamer\",\n  \"gui.indrev.tip\": \"Dica: \",\n  \"gui.indrev.tip_0\": \"Lembre-se de configurar entrada e saída de itens.\",\n  \"gui.indrev.tip_1\": \"Não coloque energia acima do limite, as coisas podem ficar quentes!\",\n  \"gui.indrev.tip_2\": \"Refrigere suas máquinas, elas também se estressam.\",\n  \"gui.indrev.tip_3\": \"Use refrigeradores para manter a eficiência\",\n  \"gui.indrev.tip_4\": \"Cresça! Cresça sua fábrica imediatamente!\",\n  \"gui.indrev.tip_5\": \"Você devia dar uma olhada na armadura modular, ela é irada!\",\n  \"gui.indrev.tip_6\": \"Gamers de verdade utilizam o machado gamer! Sim, ele brilha.\",\n  \"gui.indrev.tip_7\": \"Caso tenha algum problema ou sugestão, entre no nosso grupo no Discord!\",\n  \"gui.indrev.tip_8\": \"Faça backup dos seus mundos antes de atualizar\",\n  \"gui.indrev.tip_9\": \"Cubra seus pés na hora de dormir\",\n  \"gui.indrev.modules_installed\": \"Módulos instalados: \",\n  \"gui.indrev.shield\": \"Proteção: \",\n  \"gui.indrev.installing\": \"Instalando\",\n  \"gui.indrev.incompatible\": \"Módulo incompatível\",\n  \"gui.indrev.progress\": \"Progresso: \"\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/lang/ru_ru.json",
    "content": "{\n  \"item.patchouli.industrial_revolution_book.name\": \"Руководство Industrial Revolution\",\n  \"item.indrev.guide_book\": \"Руководство Industrial Revolution\",\n  \"item.patchouli.industrial_revolution_book.landing\": \"Эта книга поможет тебе пройтись насквозь по некоторым важным аспектам мода.$(br2)Если ты найдёшь какие-либо проблемы, захочешь задать вопрос или предложить предложение, открой «Проблему» в $(l:https://github.com/GabrielOlvH/Industrial-Revolution/issues)GitHub$() или присоединяйся в наш $(l:https://discord.com/invite/G4PjhEf)Discord!$()$(br2)Спасибо за игру!\",\n\n  \"itemGroup.indrev.indrev_group\": \"Industrial Revolution\",\n\n  \"block.machines.tooltip.io\": \"Ввод/Вывод: %s\",\n  \"block.indrev.lazuli_flux_container_1\": \"Лазуритовый флакс\",\n  \"block.indrev.lazuli_flux_container_2\": \"Контейнер\",\n\n  \"block.indrev.coal_generator\": \"Угольный генератор\",\n  \"block.indrev.coal_generator_mk1\": \"Угольный генератор мк. 1\",\n  \"block.indrev.heat_generator\": \"Тепловой генератор\",\n  \"block.indrev.heat_generator_mk4\": \"Тепловой генератор мк. 4\",\n\n  \"block.indrev.electric_furnace\": \"Электропечь\",\n  \"block.indrev.electric_furnace_mk1\": \"Электропечь мк. 1\",\n  \"block.indrev.electric_furnace_mk2\": \"Электропечь мк. 2\",\n  \"block.indrev.electric_furnace_mk3\": \"Электропечь мк. 3\",\n  \"block.indrev.electric_furnace_mk4\": \"Электропечь мк. 4\",\n  \"block.indrev.electric_furnace_creative\": \"Творческая электропечь\",\n  \"block.indrev.electric_furnace_factory\": \"Промышленная электропечь\",\n  \"block.indrev.electric_furnace_factory_mk4\": \"Промышленная электропечь мк. 4\",\n\n  \"block.indrev.smelter\": \"Производственная плавильная печь\",\n  \"block.indrev.smelter_mk4\": \"Производственная плавильная печь мк. 4\",\n\n  \"block.indrev.condenser\": \"Конденсатор\",\n  \"block.indrev.condenser_mk4\": \"Конденсатор мк. 4\",\n\n  \"block.indrev.pulverizer\": \"Измельчитель\",\n  \"block.indrev.pulverizer_mk1\": \"Измельчитель мк. 1\",\n  \"block.indrev.pulverizer_mk2\": \"Измельчитель мк. 2\",\n  \"block.indrev.pulverizer_mk3\": \"Измельчитель мк. 3\",\n  \"block.indrev.pulverizer_mk4\": \"Измельчитель мк. 4\",\n  \"block.indrev.pulverizer_creative\": \"Творческий измельчитель\",\n  \"block.indrev.pulverizer_factory\": \"Промышленный измельчитель\",\n  \"block.indrev.pulverizer_factory_mk4\": \"Промышленный измельчитель мк. 4\",\n\n  \"block.indrev.sawmill\": \"Лесопильный завод\",\n  \"block.indrev.sawmill_mk1\": \"Лесопильный завод мк. 1\",\n  \"block.indrev.sawmill_mk2\": \"Лесопильный завод мк. 2\",\n  \"block.indrev.sawmill_mk3\": \"Лесопильный завод мк. 3\",\n  \"block.indrev.sawmill_mk4\": \"Лесопильный завод мк. 4\",\n  \"block.indrev.sawmill_creative\": \"Творческий лесопильный завод\",\n\n  \"block.indrev.compressor\": \"Компрессор\",\n  \"block.indrev.compressor_mk1\": \"Компрессор мк. 1\",\n  \"block.indrev.compressor_mk2\": \"Компрессор мк. 2\",\n  \"block.indrev.compressor_mk3\": \"Компрессор мк. 3\",\n  \"block.indrev.compressor_mk4\": \"Компрессор мк. 4\",\n  \"block.indrev.compressor_creative\": \"Творческий компрессор\",\n  \"block.indrev.compressor_factory\": \"Промышленный компрессор\",\n  \"block.indrev.compressor_factory_mk4\": \"Промышленный компрессор мк. 4\",\n\n  \"block.indrev.solid_infuser\": \"Твердотельный настойник мк.\",\n  \"block.indrev.solid_infuser_mk1\": \"Твердотельный настойник мк. 1\",\n  \"block.indrev.solid_infuser_mk2\": \"Твердотельный настойник мк. 2\",\n  \"block.indrev.solid_infuser_mk3\": \"Твердотельный настойник мк. 3\",\n  \"block.indrev.solid_infuser_mk4\": \"Твердотельный настойник мк. 4\",\n  \"block.indrev.solid_infuser_creative\": \"Творческий твердотельный настойник\",\n  \"block.indrev.solid_infuser_factory\": \"Промышленный твердотельный настойник\",\n  \"block.indrev.solid_infuser_factory_mk4\": \"Промышленный твердотельный настойник мк. 4\",\n\n  \"block.indrev.lazuli_flux_container_mk1\": \"Лазуритовый флаксовый резервуар мк. 1\",\n  \"block.indrev.lazuli_flux_container_mk2\": \"Лазуритовый флаксовый резервуар мк. 2\",\n  \"block.indrev.lazuli_flux_container_mk3\": \"Лазуритовый флаксовый резервуар мк. 3\",\n  \"block.indrev.lazuli_flux_container_mk4\": \"Лазуритовый флаксовый резервуар мк. 4\",\n  \"block.indrev.lazuli_flux_container_creative\": \"Творческий лазуритовый флаксовый резервуар\",\n\n  \"block.indrev.cable_mk1\": \"Кабель мк. 1\",\n  \"block.indrev.cable_mk2\": \"Кабель мк. 2\",\n  \"block.indrev.cable_mk3\": \"Кабель мк. 3\",\n  \"block.indrev.cable_mk4\": \"Кабель мк. 4\",\n\n  \"block.indrev.fluid_pipe_mk1\": \"Жидкостная труба мк. 1\",\n  \"block.indrev.fluid_pipe_mk2\": \"Жидкостная труба мк. 2\",\n  \"block.indrev.fluid_pipe_mk3\": \"Жидкостная труба мк. 3\",\n  \"block.indrev.fluid_pipe_mk4\": \"Жидкостная труба мк. 4\",\n  \"block.indrev.item_pipe_mk1\": \"Предметная труба мк. 1\",\n  \"block.indrev.item_pipe_mk2\": \"Предметная труба мк. 2\",\n  \"block.indrev.item_pipe_mk3\": \"Предметная труба мк. 3\",\n  \"block.indrev.item_pipe_mk4\": \"Предметная труба мк. 4\",\n\n  \"block.indrev.fisher\": \"Рыболов\",\n  \"block.indrev.fisher_mk2\": \"Базовый рыболов\",\n  \"block.indrev.fisher_mk3\": \"Улучшенный рыболов\",\n  \"block.indrev.fisher_mk4\": \"Продвинутый рыболов\",\n\n  \"block.indrev.mining_rig\": \"Станочная буровая машина\",\n  \"block.indrev.mining_rig_mk4\": \"Станочная буровая машина мк. 4\",\n  \"block.indrev.mining_rig.tooltip\": \"Прежде чем использовать Станочную буровую машину, в этом чанке ты должен использовать Сканер чанка.\",\n  \"block.indrev.mining_rig.mined\": \"%s добыто.\",\n\n  \"block.indrev.drill\": \"Сверло буровой установки\",\n  \"block.indrev.drill_top\": \"Сверло буровой установки\",\n  \"block.indrev.drill_middle\": \"Сверло буровой установки\",\n  \"block.indrev.drill_bottom\": \"Сверло буровой установки\",\n  \"block.indrev.drill.faster\": \"%sх быстрее\",\n  \"block.indrev.drill.no_drills\": \"Пустой.\",\n  \"block.indrev.drill.wrong_location\": \"Неверный отчёт для текущего местоположения!\",\n  \"block.indrev.drill.active\": \"Свёрла активны\",\n  \"block.indrev.drill.activating\": \"Не готов\",\n  \"block.indrev.drill.not_enough_power\": \"Недостаточно энергии\",\n  \"block.indrev.drill.power_required\": \"Для работы требуется как минимум %sЛФ.\",\n\n  \"block.indrev.recycler\": \"Переработчик\",\n  \"block.indrev.recycler_mk2\": \"Переработчик мк. 2\",\n\n  \"block.indrev.cable\": \"Кабель\",\n\n  \"block.indrev.farmer\": \"Фермер\",\n  \"block.indrev.farmer_mk1\": \"Фермер мк. 1\",\n  \"block.indrev.farmer_mk2\": \"Фермер мк. 2\",\n  \"block.indrev.farmer_mk3\": \"Фермер мк. 3\",\n  \"block.indrev.farmer_mk4\": \"Фермер мк. 4\",\n  \"block.indrev.farmer_creative\": \"Творческий фермер\",\n\n  \"block.indrev.slaughter\": \"Забойник\",\n  \"block.indrev.slaughter_mk1\": \"Забойник мк. 1\",\n  \"block.indrev.slaughter_mk2\": \"Забойник мк. 2\",\n  \"block.indrev.slaughter_mk3\": \"Забойник мк. 3\",\n  \"block.indrev.slaughter_mk4\": \"Забойник мк. 4\",\n  \"block.indrev.slaughter_creative\": \"Творческий забойник\",\n\n  \"block.indrev.chopper\": \"Лесоруб\",\n  \"block.indrev.chopper_mk1\": \"Лесоруб мк. 1\",\n  \"block.indrev.chopper_mk2\": \"Лесоруб мк. 2\",\n  \"block.indrev.chopper_mk3\": \"Лесоруб мк. 3\",\n  \"block.indrev.chopper_mk4\": \"Лесоруб мк. 4\",\n  \"block.indrev.chopper_creative\": \"Творческий лесоруб\",\n\n  \"block.indrev.rancher\": \"Скотовод\",\n  \"block.indrev.rancher_mk1\": \"Скотовод мк. 1\",\n  \"block.indrev.rancher_mk2\": \"Скотовод мк. 2\",\n  \"block.indrev.rancher_mk3\": \"Скотовод мк. 3\",\n  \"block.indrev.rancher_mk4\": \"Скотовод мк. 4\",\n  \"block.indrev.rancher_creative\": \"Творческий скотовод\",\n\n  \"block.indrev.fluid_infuser\": \"Жидкостный настойник\",\n  \"block.indrev.fluid_infuser_mk1\": \"Жидкостный настойник мк. 1\",\n  \"block.indrev.fluid_infuser_mk2\": \"Жидкостный настойник мк. 2\",\n  \"block.indrev.fluid_infuser_mk3\": \"Жидкостный настойник мк. 3\",\n  \"block.indrev.fluid_infuser_mk4\": \"Жидкостный настойник мк. 4\",\n  \"block.indrev.fluid_infuser_creative\": \"Творческий жидкостный настойник\",\n\n  \"block.indrev.electrolytic_separator\": \"Электролитный отделитель\",\n  \"block.indrev.electrolytic_separator_mk1\": \"Электролитный отделитель мк. 1\",\n  \"block.indrev.electrolytic_separator_mk2\": \"Электролитный отделитель мк. 2\",\n  \"block.indrev.electrolytic_separator_mk3\": \"Электролитный отделитель мк. 3\",\n  \"block.indrev.electrolytic_separator_mk4\": \"Электролитный отделитель мк. 4\",\n  \"block.indrev.electrolytic_separator_creative\": \"Творческий электролитный отделитель\",\n\n  \"block.indrev.drain_mk1\": \"Дренаж мк. 1\",\n  \"block.indrev.tank\": \"Бак\",\n  \"block.indrev.factory\": \"Завод\",\n  \"block.indrev.biomass_generator\": \"Генератор на биомассе\",\n  \"block.indrev.biomass_generator_mk3\": \"Генератор на биомассе мк. 3\",\n  \"block.indrev.solar_generator\": \"Солнечный генератор\",\n  \"block.indrev.solar_generator_mk1\": \"Солнечный генератор мк. 1\",\n  \"block.indrev.solar_generator_mk3\": \"Солнечный генератор мк. 3\",\n  \"block.indrev.charge_pad_mk4\": \"Зарядная площадка мк. 4\",\n  \"block.indrev.charge_pad_mk4.tooltip\": \"Заряжает экипированные предметы или всё, что ты в него положишь!\",\n  \"block.indrev.aoe.range\": \"Диапазон: %s\",\n  \"block.indrev.aoe.toggle.false\": \"Показать диапазон\",\n  \"block.indrev.aoe.toggle.true\": \"Скрыть диапазон\",\n  \"block.indrev.planks\": \"Обшивная доска\",\n  \"block.indrev.plank_block\": \"Блок из обшивных досок\",\n  \"block.indrev.wither_proof_obsidian\": \"Обсидиановая защита от визера\",\n  \"block.indrev.controller\": \"Регулятор\",\n  \"block.indrev.frame\": \"Каркас\",\n  \"block.indrev.duct\": \"Воздуховод\",\n  \"block.indrev.warning_strobe\": \"Предупреждающая лампа\",\n  \"block.indrev.silo\": \"Силос\",\n  \"block.indrev.intake\": \"Воздушный канал\",\n  \"block.indrev.cabinet\": \"Корпус\",\n  \"block.indrev.coolant\": \"Хладагент\",\n  \"block.indrev.molten_iron\": \"Расплавленное железо\",\n  \"block.indrev.molten_gold\": \"Расплавленное золото\",\n  \"block.indrev.molten_copper\": \"Расплавленная медь\",\n  \"block.indrev.molten_tin\": \"Расплавленное олово\",\n  \"block.indrev.molten_netherite\": \"Расплавленный незерит\",\n  \"block.indrev.hydrogen\": \"Водород\",\n  \"item.indrev.hydrogen_bucket\": \"Ведро водорода\",\n  \"block.indrev.oxygen\": \"Кислород\",\n  \"item.indrev.oxygen_bucket\": \"Ведро кислорода\",\n  \"block.indrev.methane\": \"Метан\",\n  \"item.indrev.methane_bucket\": \"Ведро метана\",\n  \"block.indrev.sulfuric_acid\": \"Серная кислота\",\n  \"item.indrev.sulfuric_acid_bucket\": \"Ведро серной кислоты\",\n  \"block.indrev.toxic_mud\": \"Токсичный раствор\",\n  \"item.indrev.toxic_mud_bucket\": \"Ведро токсичного раствора\",\n  \"block.indrev.machine_block\": \"Машинный блок\",\n  \"item.indrev.modular_core\": \"Ядро модульности (Незаряженное)\",\n  \"item.indrev.modular_core_activated\": \"Ядро модульности\",\n  \"item.indrev.sawdust\": \"Опилки\",\n  \"block.indrev.laser_emitter_mk4\": \"Лазерный излучатель\",\n  \"block.indrev.capsule\": \"Капсула\",\n\n  \"block.indrev.tin_ore\": \"Оловянная руда\",\n  \"block.indrev.tin_block\": \"Блок олова\",\n  \"item.indrev.tin_ingot\": \"Оловянный слиток\",\n  \"item.indrev.tin_chunk\": \"Оловянный кусок\",\n  \"item.indrev.tin_dust\": \"Оловянная пыль\",\n  \"item.indrev.tin_plate\": \"Оловянная пластина\",\n  \"item.indrev.tin_gear\": \"Оловянная шестерня\",\n  \"item.indrev.tin_nugget\": \"Кусочек олова\",\n  \"item.indrev.molten_tin_bucket\": \"Ведро расплавленного олова\",\n  \"item.indrev.tin_purified_ore\": \"Очищенная оловянная руда\",\n  \"item.indrev.copper_chunk\": \"Медный кусок\",\n  \"item.indrev.copper_dust\": \"Медная пыль\",\n  \"item.indrev.copper_plate\": \"Медная пластина\",\n  \"item.indrev.copper_gear\": \"Медная шестерня\",\n  \"item.indrev.copper_nugget\": \"Кусочек меди\",\n  \"item.indrev.copper_purified_ore\": \"Очищенная медная руда\",\n  \"item.indrev.molten_copper_bucket\": \"Ведро расплавленной меди\",\n  \"block.indrev.molten_lead\": \"Расплавленный свинец\",\n  \"item.indrev.molten_lead_bucket\": \"Ведро расплавленного свинца\",\n  \"block.indrev.lead_ore\": \"Свинцовая руда\",\n  \"block.indrev.lead_block\": \"Блок свинца\",\n  \"item.indrev.lead_ingot\": \"Свинцовый слиток\",\n  \"item.indrev.lead_chunk\": \"Кусок свинца\",\n  \"item.indrev.lead_dust\": \"Свинцовая пыль\",\n  \"item.indrev.lead_plate\": \"Свинцовая пластина\",\n  \"item.indrev.lead_gear\": \"Свинцовая шестерня\",\n  \"item.indrev.lead_nugget\": \"Кусочек свинца\",\n  \"item.indrev.lead_purified_ore\": \"Очищенная свинцовая руда\",\n  \"item.indrev.raw_lead\": \"Рудный свинец\",\n  \"item.indrev.raw_silver\": \"Рудное серебро\",\n  \"item.indrev.raw_tungsten\": \"Рудный вольфрам\",\n  \"item.indrev.raw_tin\": \"Рудное олово\",\n  \"block.indrev.raw_lead_block\": \"Блок рудного свинца\",\n  \"block.indrev.raw_silver_block\": \"Блок рудного серебра\",\n  \"block.indrev.raw_tungsten_block\": \"Блок рудного вольфрама\",\n  \"block.indrev.raw_tin_block\": \"Блок рудного олова\",\n  \"block.indrev.molten_silver\": \"Расплавленное серебро\",\n  \"item.indrev.molten_silver_bucket\": \"Ведро расплавленного серебра\",\n  \"block.indrev.silver_ore\": \"Серебряная руда\",\n  \"block.indrev.silver_block\": \"Блок серебра\",\n  \"item.indrev.silver_ingot\": \"Серебряный слиток\",\n  \"item.indrev.silver_chunk\": \"Серебряный кусок\",\n  \"item.indrev.silver_dust\": \"Серебряная пыль\",\n  \"item.indrev.silver_plate\": \"Серебряная плита\",\n  \"item.indrev.silver_gear\": \"Серебряная шестерня\",\n  \"item.indrev.silver_nugget\": \"Кусочек серебра\",\n  \"item.indrev.silver_purified_ore\": \"Очищенная серебряная руда\",\n  \"block.indrev.molten_tungsten\": \"Расплавленный вольфрам\",\n  \"item.indrev.molten_tungsten_bucket\": \"Ведро расплавленного вольфрама\",\n  \"block.indrev.tungsten_ore\": \"Вольфрамовая руда\",\n  \"block.indrev.tungsten_block\": \"Блок вольфрама\",\n  \"item.indrev.tungsten_ingot\": \"Вольфрамовый слиток\",\n  \"item.indrev.tungsten_chunk\": \"Вольфрамовый кусок\",\n  \"item.indrev.tungsten_dust\": \"Вольфрамовая пыль\",\n  \"item.indrev.tungsten_plate\": \"Вольфрамовая пластина\",\n  \"item.indrev.tungsten_gear\": \"Вольфрамовая шестерня\",\n  \"item.indrev.tungsten_nugget\": \"Кусочек вольфрама\",\n  \"item.indrev.tungsten_purified_ore\": \"Очищенная вольфрамовая руда\",\n  \"block.indrev.deepslate_tungsten_ore\": \"Вольфрамоносный глубинный сланец\",\n  \"block.indrev.deepslate_tin_ore\": \"Оловоносный глубинный сланец\",\n  \"block.indrev.deepslate_silver_ore\": \"Серебряноносный глубинный сланец\",\n  \"block.indrev.deepslate_nikolite_ore\": \"Николитоносный глубинный сланец\",\n  \"block.indrev.deepslate_lead_ore\": \"Свинцовоносный глубинный сланец\",\n  \"block.indrev.electrum_block\": \"Блок электрума\",\n  \"item.indrev.electrum_ingot\": \"Электрумовый слиток\",\n  \"item.indrev.electrum_dust\": \"Электрумовая пыль\",\n  \"item.indrev.electrum_plate\": \"Электрумовая пластина\",\n  \"item.indrev.electrum_gear\": \"Электрумовая шестерня\",\n  \"item.indrev.electrum_nugget\": \"Кусочек электрума\",\n  \"block.indrev.bronze_block\": \"Блок бронзы\",\n  \"item.indrev.bronze_ingot\": \"Бронзовый слиток\",\n  \"item.indrev.bronze_chunk\": \"Бронзовый кусок\",\n  \"item.indrev.bronze_dust\": \"Бронзовая пыль\",\n  \"item.indrev.bronze_plate\": \"Бронзовая пластина\",\n  \"item.indrev.bronze_gear\": \"Бронзовая шестерня\",\n  \"item.indrev.bronze_nugget\": \"Кусочек бронзы\",\n  \"block.indrev.sulfur_crystal\": \"Блок серного кристалла\",\n  \"item.indrev.sulfur_crystal\": \"Серный кристалл\",\n  \"item.indrev.sulfur_dust\": \"Серная пыль\",\n  \"block.indrev.steel_block\": \"Блок стали\",\n  \"item.indrev.steel_dust\": \"Стальная пыль\",\n  \"item.indrev.steel_ingot\": \"Стальной слиток\",\n  \"item.indrev.steel_plate\": \"Стальная пластина\",\n  \"item.indrev.steel_gear\": \"Стальная шестерня\",\n  \"item.indrev.steel_nugget\": \"Кусочек стали\",\n  \"item.indrev.gold_dust\": \"Золотая пыль\",\n  \"item.indrev.gold_plate\": \"Золотая пластина\",\n  \"item.indrev.gold_chunk\": \"Золотой кусок\",\n  \"item.indrev.gold_purified_ore\": \"Очищенная золотая руда\",\n  \"item.indrev.molten_gold_bucket\": \"Ведро расплавленного золота\",\n  \"item.indrev.iron_dust\": \"Железная пыль\",\n  \"item.indrev.iron_plate\": \"Железная пластина\",\n  \"item.indrev.iron_chunk\": \"Железный кусок\",\n  \"item.indrev.gold_dust\": \"Золотая пыль\",\n  \"item.indrev.gold_plate\": \"Золотая пластина\",\n  \"item.indrev.gold_chunk\": \"Золотой кусок\",\n  \"item.indrev.nikolite_dust\": \"Николитовая пыль\",\n  \"item.indrev.nikolite_ingot\": \"Николитовый слиток\",\n  \"block.indrev.nikolite_ore\": \"Николитовая руда\",\n  \"item.indrev.molten_iron_bucket\": \"Ведро расплавленного железа\",\n  \"item.indrev.iron_purified_ore\": \"Очищенная железная руда\",\n  \"item.indrev.diamond_dust\": \"Алмазная пыль\",\n  \"item.indrev.coal_dust\": \"Угольная пыль\",\n\n  \"block.indrev.pump_mk1\": \"Насос мк. 1\",\n  \"item.indrev.empty_enhancer\": \"Пустой усилитель\",\n  \"item.indrev.buffer_enhancer\": \"Усилитель: Буфер\",\n  \"item.indrev.buffer_enhancer.tooltip\": \"Увеличивает энергоёмкость.\",\n  \"item.indrev.energy_enhancer\": \"Усилитель: Эффективное использование энергии\",\n  \"item.indrev.energy_enhancer.tooltip\": \"Сокращает энергозатраты.\",\n  \"item.indrev.damage_enhancer\": \"Усилитель: Урон\",\n  \"item.indrev.damage_enhancer.tooltip\": \"Увеличивает урон, сделанный Забойником.\",\n  \"item.indrev.speed_enhancer\": \"Усилитель: Скорость\",\n  \"item.indrev.speed_enhancer.tooltip\": \"Увеличивает скорость переработки и потребление энергии.\",\n  \"item.indrev.blast_furnace_enhancer\": \"Усилитель: Горячее дутьё\",\n  \"item.indrev.blast_furnace_enhancer.tooltip\": \"Позволяет электропечи принимать рецепты от доменной печи.\",\n  \"item.indrev.smoker_enhancer\": \"Усилитель: Коптильня\",\n  \"item.indrev.smoker_enhancer.tooltip\": \"Позволяет электропечи принимать рецепты от коптильни.\",\n  \"item.indrev.enhancers.incompatible\": \"Машина не поддерживает данный усилитель.\",\n  \"item.indrev.enhancers.count\": \"Ограничивается %s слотами в машине.\",\n  \"item.indrev.rechargeable.tooltip\": \"Перезаряжаемый\",\n  \"item.indrev.chunk_scanner\": \"Сканер чанка\",\n  \"item.indrev.chunk_scanner.scanning\": \"Сканирование...\",\n  \"item.indrev.chunk_scanner.scanned1\": \"Сканирование окончено! Проверь инвентарь.\",\n  \"item.indrev.chunk_scanner.scanned2\": \"Тип жилы: %s\",\n  \"item.indrev.chunk_scanner.already_scanned\": \"Этот чанк уже просканирован и в нём есть жила %s.\",\n  \"item.indrev.chunk_scanner.tooltip1\": \"Чтобы начать сканирование чанка удерживай пкм.\",\n  \"item.indrev.chunk_scanner.tooltip2\": \"Тип жилы: %s\",\n  \"item.indrev.chunk_scanner.tooltip3\": \"От %s до %s\",\n  \"item.indrev.chunk_scanner.tooltip4\": \"Измерение: %s\",\n  \"item.indrev.chunk_scanner.tooltip5\": \"Пкм для подробностей.\",\n  \"item.indrev.fan\": \"Вентилятор\",\n  \"item.indrev.cooler_cell\": \"Охладительная ячейка\",\n  \"item.indrev.heatsink\": \"Теплоотвод\",\n  \"item.indrev.heat_coil\": \"Термическая катушка\",\n  \"item.indrev.heat_coil.tooltip\": \"Преобразует лазуритный флакс в тепло.\",\n\n  \"item.indrev.stone_drill_head\": \"Каменная головка бура\",\n  \"item.indrev.iron_drill_head\": \"Железная головка бура\",\n  \"item.indrev.diamond_drill_head\": \"Алмазная головка бура\",\n  \"item.indrev.netherite_drill_head\": \"Незеритовая головка бура\",\n  \"item.indrev.mining_drill_mk1\": \"Шахтёрский бур мк. 1\",\n  \"item.indrev.mining_drill_mk2\": \"Шахтёрский бур мк. 2\",\n  \"item.indrev.mining_drill_mk3\": \"Шахтёрский бур мк. 3\",\n  \"item.indrev.mining_drill_mk4\": \"Модульный шахтёрский бур мк. 4\",\n\n  \"item.indrev.energy_reader\": \"Считыватель энергии\",\n  \"item.indrev.energy_reader.use\": \"Запасённая энергия:\",\n  \"item.indrev.energy_reader.use1\": \"%s ЛФ/тик\",\n\n  \"item.indrev.enriched_nikolite_dust\": \"Обогащённая николитовая пыль\",\n  \"item.indrev.enriched_nikolite_ingot\": \"Обогащённый николитовый слиток\",\n\n  \"item.indrev.battery\": \"Батарейка\",\n\n  \"item.indrev.circuit_mk1\": \"Схема мк. 1\",\n  \"item.indrev.circuit_mk2\": \"Схема мк. 2\",\n  \"item.indrev.circuit_mk3\": \"Схема мк. 3\",\n  \"item.indrev.circuit_mk4\": \"Схема мк. 4\",\n\n  \"item.indrev.tier_upgrade_mk2\": \"Улучшение машины мк. 2\",\n  \"item.indrev.tier_upgrade_mk2.tooltip\": \"Улучшает машины мк. 1 на мк. 2.\",\n  \"item.indrev.tier_upgrade_mk3\": \"Улучшение машины мк. 3\",\n  \"item.indrev.tier_upgrade_mk3.tooltip\": \"Улучшает машины мк. 2 на мк. 3.\",\n  \"item.indrev.tier_upgrade_mk4\": \"Улучшение машины мк. 4\",\n  \"item.indrev.tier_upgrade_mk4.tooltip\": \"Улучшает машины мк. 3 на мк. 4.\",\n\n  \"item.indrev.servo_output\": \"Сервопривод вывода\",\n  \"item.indrev.servo_output.tooltip\": \"Проталкивает предметы в соединённые инвентари.\",\n  \"item.indrev.servo_retriever\": \"Сервопривод-извлекатель\",\n  \"item.indrev.servo_retriever.tooltip\": \"Выталкивает предметы из соединённых инвентарей.\",\n  \"item.indrev.servo.mode\": \"Приоритет: \",\n  \"item.indrev.servo.mode.round_robin\": \"Циклически\",\n  \"item.indrev.servo.mode.round_robin.tooltip\": \"Приоритизирует контейнеры с наименьшим количеством предметов/жидкостей.\",\n  \"item.indrev.servo.mode.nearest_first\": \"Сначала ближайший\",\n  \"item.indrev.servo.mode.nearest_first.tooltip\": \"Приоритизирует ближайшие контейнеры.\",\n  \"item.indrev.servo.mode.furthest_first\": \"Сначала дальний\",\n  \"item.indrev.servo.mode.furthest_first.tooltip\": \"Приоритизирует дальние контейнеры.\",\n  \"item.indrev.servo.mode.random\": \"Случайный\",\n  \"item.indrev.servo.mode.random.tooltip\": \"Без приоритетов\",\n\n  \"item.indrev.biomass\": \"Биомасса\",\n  \"item.indrev.untanned_leather\": \"Не дублёная кожа\",\n  \"item.indrev.coolant_bucket\": \"Ведро хладагента\",\n  \"item.indrev.molten_netherite_bucket\": \"Ведро расплавленного незерита\",\n  \"item.indrev.netherite_scrap_purified_ore\": \"Очищенный древний обломок\",\n  \"item.indrev.netherite_scrap_chunk\": \"Кусок древнего обломка\",\n  \"item.indrev.netherite_scrap_dust\": \"Пыль от незеритового лома\",\n  \"item.indrev.hammer\": \"Молот\",\n  \"item.indrev.screwdriver\": \"Отвёртка\",\n  \"item.indrev.wrench\": \"Гаечный ключ\",\n  \"item.indrev.wrench.switch_mode\": \"Режим гаечного ключа: %s\",\n  \"item.indrev.wrench.title\": \"Настраивает машину\",\n  \"item.indrev.wrench.autopush\": \"Авто-толкание\",\n  \"item.indrev.wrench.autopull\": \"Авто-выталкивание\",\n  \"item.indrev.wrench.item\": \"Предмет\",\n  \"item.indrev.wrench.fluid\": \"Жидкость\",\n  \"item.indrev.wrench.energy\": \"Энергия\",\n  \"item.indrev.wrench.output\": \"Выход\",\n  \"item.indrev.wrench.input\": \"Вход\",\n  \"item.indrev.wrench.input_first\": \"Вход (Первый слот)\",\n  \"item.indrev.wrench.input_second\": \"Вход (Второй слот)\",\n  \"item.indrev.wrench.input_output\": \"Вход/Выход\",\n  \"item.indrev.wrench.none\": \"Никакой\",\n  \"item.indrev.wrench.tooltip\": \"Для смены режима Shift + Пкм.\",\n  \"item.indrev.wrench.tooltip1\": \"Режим: %s\",\n  \"item.indrev.wrench.tooltip1.rotate\": \"вращение\",\n  \"item.indrev.wrench.tooltip1.configure\": \"настройки\",\n  \"item.indrev.wrench.mode\": \"Режим: %s\",\n  \"item.indrev.wrench.side.north\": \"север\",\n  \"item.indrev.wrench.side.south\": \"юг\",\n  \"item.indrev.wrench.side.west\": \"запад\",\n  \"item.indrev.wrench.side.east\": \"восток\",\n  \"item.indrev.wrench.side.up\": \"Верх\",\n  \"item.indrev.wrench.side.down\": \"Низ\",\n  \"item.indrev.wrench.side.top\": \"Верхняя сторона\",\n  \"item.indrev.wrench.side.bottom\": \"Нижняя сторона\",\n  \"item.indrev.wrench.side.left\": \"Левая сторона\",\n  \"item.indrev.wrench.side.right\": \"Правая сторона\",\n  \"item.indrev.wrench.side.front\": \"Передняя сторона\",\n  \"item.indrev.wrench.side.back\": \"Задняя сторона\",\n  \"item.indrev.wrench.connected\": \"Подключён к %s\",\n  \"item.indrev.tech_soup\": \"Техносуп\",\n  \"item.indrev.scan_output\": \"Отчёт о ресурсах\",\n  \"item.indrev.portable_charger\": \"Переносной зарядник\",\n  \"item.indrev.gamer_axe\": \"Геймерский топор\",\n\n  \"gui.indrev.button.auto_split\": \"Авто-разделение стэков\",\n  \"gui.indrev.resourcereport.size\": \"Размер жилы: %s\",\n  \"gui.indrev.resourcereport.size.tiny\": \"Крошечный\",\n  \"gui.indrev.resourcereport.size.small\": \"Малый\",\n  \"gui.indrev.resourcereport.size.average\": \"Средний\",\n  \"gui.indrev.resourcereport.size.big\": \"Большой\",\n  \"gui.indrev.resourcereport.size.very_big\": \"Очень большой\",\n  \"gui.indrev.resourcereport.size.enormous\": \"Огромный\",\n  \"gui.indrev.resourcereport.size.gigantic\": \"Гигантский\",\n\n  \"gui.furnace.mode\": \"Режим\",\n  \"gui.furnace.mode.furnace\": \"Печь\",\n  \"gui.furnace.mode.blast_furnace\": \"Доменная печь\",\n  \"gui.furnace.mode.smoker\": \"Коптильня\",\n  \"gui.widget.process\": \"Прогресс: %s\",\n  \"gui.widget.energy\": \"Энергия\",\n  \"gui.widget.temperature\": \"°C\",\n  \"gui.widget.temperature_info.high\": \"Слишком горячо\",\n  \"gui.widget.temperature_info.medium\": \"Полный потенциал\",\n  \"gui.widget.temperature_info.low\": \"Не нагретый\",\n  \"gui.indrev.solar.on\": \"Генерирование\",\n  \"gui.indrev.heatgen.idle\": \"Простаивает\",\n  \"gui.indrev.heatgen.title\": \"Потребление %s мБ\",\n  \"gui.indrev.heatgen.generating\": \"Генерирование %s ЛФ\",\n  \"gui.indrev.heatgen.pertick\": \"в тик\",\n  \"gui.indrev.heatgen.extra\": \"Осторожно, он горячий!\",\n  \"gui.indrev.guide_book_shortcut.contains\": \"Открывает страницу в Руководстве Industrial Revolution.\",\n  \"gui.indrev.guide_book_shortcut.missing\": \"Отсутствует руководство industrial revolution!\",\n  \"gui.indrev.tooltip.maxTransferRate\": \"Пропускная способность: \",\n  \"gui.indrev.tooltip.maxInput\": \"Макс. вход: \",\n  \"gui.indrev.tooltip.maxOutput\": \"Макс. выход: \",\n  \"gui.indrev.tooltip.maxEnergyStored\": \"Энергоёмкость: \",\n  \"gui.indrev.tooltip.seconds\": \"%s секунд.\",\n  \"gui.indrev.tooltip.energyCost\": \"Энергозатраты: \",\n  \"gui.indrev.tooltip.processSpeed\": \"Скорость: \",\n  \"gui.indrev.tooltip.ratio\": \"Генерация: \",\n  \"gui.indrev.tooltip.press_shift\": \"Держи %s для подробной информации.\",\n  \"gui.indrev.tooltip.lftick\": \"%s ЛФ/тик\",\n  \"gui.indrev.tooltip.lf\": \"%s ЛФ\",\n  \"gui.indrev.tooltip.itemsec\": \"%s предметов/сек\",\n  \"gui.indrev.tooltip.fluidsec\": \"%s вёдер/сек\",\n  \"gui.indrev.tooltip.temperatureBoost\": \"Нагревание: \",\n  \"gui.indrev.locked\": \"Заблокировано\",\n  \"gui.indrev.modular_armor_slot_type\": \"Помести предмет.\",\n  \"gui.indrev.module_slot_type\": \"Помести желанный модуль.\",\n  \"gui.indrev.scan_output_slot_type\": \"Отчёт о ресурсах\",\n  \"gui.indrev.output_slot_type\": \"Собрано предметов\",\n  \"gui.indrev.chopper_input_axe\": \"Помести топор.\",\n  \"gui.indrev.chopper_input_bone_meal\": \"Помести костную муку.\",\n  \"gui.indrev.chopper_input_sapling\": \"Помести саженцы.\",\n  \"gui.indrev.farmer_input_slot_type\": \"Семена и костная мука.\",\n  \"gui.indrev.slaughter_input_sword\": \"Помести меч.\",\n  \"gui.indrev.cooler_slot_type\": \"Охладитель\",\n  \"gui.indrev.battery_slot_type\": \"Батарейка\",\n  \"gui.indrev.upgrade_slot_type\": \"Улучшения\",\n  \"gui.indrev.fishingrod\": \"Удочка\",\n\n  \"indrev.category.rei.upgrading\": \"Ты должен создать %s и использовать на нём %s, чтобы улучшить его до %s.\",\n  \"indrev.category.rei.pulverizing\": \"Измельчение\",\n  \"indrev.category.rei.infusing\": \"Вливание\",\n  \"indrev.category.rei.compressing\": \"Сжатие\",\n  \"indrev.category.rei.recycling\": \"Переработка\",\n  \"indrev.category.rei.fluid_infusing\": \"Вливание жидкостью\",\n  \"indrev.category.rei.condensing\": \"Конденсирование\",\n  \"indrev.category.rei.smelting\": \"Плавка\",\n  \"indrev.category.rei.sawmill\": \"Распиливание\",\n  \"indrev.category.rei.module\": \"Создание модуля\",\n\n  \"item.indrev.modular.upgrade\": \"Установлено модулей:\",\n  \"item.indrev.modular.upgrade.night_vision\": \"Ночное зрение %s\",\n  \"item.indrev.modular.upgrade.speed\": \"Скорость %s\",\n  \"item.indrev.modular.upgrade.jump_boost\": \"Прыгучесть %s\",\n  \"item.indrev.modular.upgrade.breathing\": \"Дыхание %s\",\n  \"item.indrev.modular.upgrade.protection\": \"Защита %s\",\n  \"item.indrev.modular.upgrade.feather_falling\": \"Невесомость %s\",\n  \"item.indrev.modular.upgrade.auto_feeder\": \"Авто-кормушка %s\",\n  \"item.indrev.modular.upgrade.charger\": \"Зарядник %s\",\n  \"item.indrev.modular.upgrade.solar_panel\": \"Солнечный генератор %s\",\n  \"item.indrev.modular.upgrade.piglin_tricker\": \"Обман пиглина %s\",\n  \"item.indrev.modular.upgrade.elytra\": \"Встроенные элитры %s\",\n  \"item.indrev.modular.upgrade.jetpack\": \"Встроенный реактивный ранец %s\",\n  \"item.indrev.modular.upgrade.magnet\": \"Магнит %s\",\n  \"item.indrev.modular.upgrade.water_affinity\": \"Подводник %s\",\n  \"item.indrev.modular.upgrade.fire_resistance\": \"Огнестойкость %s\",\n  \"item.indrev.modular.upgrade.efficiency\": \"Эффективность %s\",\n  \"item.indrev.modular.upgrade.range\": \"Диапазон %s\",\n  \"item.indrev.modular.upgrade.fortune\": \"Удача %s\",\n  \"item.indrev.modular.upgrade.silk_touch\": \"Шёлковое касание %s\",\n  \"item.indrev.modular.upgrade.sharpness\": \"Острота %s\",\n  \"item.indrev.modular.upgrade.looting\": \"Добыча %s\",\n  \"item.indrev.modular.upgrade.fire_aspect\": \"Заговор огня %s\",\n  \"item.indrev.modular.upgrade.reach\": \"Досягаемость %s\",\n\n  \"item.indrev.steel_helmet\": \"Стальной шлем\",\n  \"item.indrev.steel_chestplate\": \"Стальной нагрудник\",\n  \"item.indrev.steel_leggings\": \"Стальные поножи\",\n  \"item.indrev.steel_boots\": \"Стальные ботинки\",\n  \"item.indrev.steel_sword\": \"Стальной меч\",\n  \"item.indrev.steel_pickaxe\": \"Стальная кирка\",\n  \"item.indrev.steel_axe\": \"Стальной топор\",\n  \"item.indrev.steel_shovel\": \"Стальная лопата\",\n  \"item.indrev.steel_hoe\": \"Стальная мотыга\",\n  \"item.indrev.bronze_helmet\": \"Бронзовый шлем\",\n  \"item.indrev.bronze_chestplate\": \"Бронзовый нагрудник\",\n  \"item.indrev.bronze_leggings\": \"Бронзовые поножи\",\n  \"item.indrev.bronze_boots\": \"Бронзовые ботинки\",\n  \"item.indrev.bronze_sword\": \"Бронзовый меч\",\n  \"item.indrev.bronze_pickaxe\": \"Бронзовая кирка\",\n  \"item.indrev.bronze_axe\": \"Бронзовый топор\",\n  \"item.indrev.bronze_shovel\": \"Бронзовая лопата\",\n  \"item.indrev.bronze_hoe\": \"Бронзовая мотыга\",\n  \"item.indrev.lead_helmet\": \"Свинцовый шлем\",\n  \"item.indrev.lead_chestplate\": \"Свинцовый нагрудник\",\n  \"item.indrev.lead_leggings\": \"Свинцовые поножи\",\n  \"item.indrev.lead_boots\": \"Свинцовые ботинки\",\n  \"item.indrev.lead_sword\": \"Свинцовый меч\",\n  \"item.indrev.lead_pickaxe\": \"Свинцовая кирка\",\n  \"item.indrev.lead_axe\": \"Свинцовый топор\",\n  \"item.indrev.lead_shovel\": \"Свинцовая лопата\",\n  \"item.indrev.lead_hoe\": \"Свинцовая мотыга\",\n  \"item.indrev.silver_helmet\": \"Серебряный шлем\",\n  \"item.indrev.silver_chestplate\": \"Серебряный нагрудник\",\n  \"item.indrev.silver_leggings\": \"Серебряные поножи\",\n  \"item.indrev.silver_boots\": \"Серебряные ботинки\",\n  \"item.indrev.silver_sword\": \"Серебряный меч\",\n  \"item.indrev.silver_pickaxe\": \"Серебряная кирка\",\n  \"item.indrev.silver_axe\": \"Серебряный топор\",\n  \"item.indrev.silver_shovel\": \"Серебряная лопата\",\n  \"item.indrev.silver_hoe\": \"Серебряная мотыга\",\n\n  \"block.indrev.modular_workbench\": \"Модульный верстак\",\n  \"block.indrev.modular_workbench_mk4\": \"Модульный верстак мк. 4\",\n  \"item.indrev.modular_armor_helmet\": \"Модульный шлем\",\n  \"item.indrev.modular_armor_chest\": \"Модульный нагрудник\",\n  \"item.indrev.modular_armor_legs\": \"Модульные поножи\",\n  \"item.indrev.modular_armor_boots\": \"Модульные ботинки\",\n\n  \"item.indrev.module_parts\": \"Можно установить в:\",\n  \"item.indrev.module_parts_head\": \"шлем\",\n  \"item.indrev.module_parts_chest\": \"нагрудник\",\n  \"item.indrev.module_parts_legs\": \"поножи\",\n  \"item.indrev.module_parts_feet\": \"ботинки\",\n  \"item.indrev.module_parts_drill\": \"буровую установку\",\n  \"item.indrev.module_parts_gamer_axe\": \"Гаймерский топор\",\n  \"item.indrev.module_max_level\": \"Максимальный уровень: %s\",\n  \"item.indrev.module_protection\": \"Модуль: Защита\",\n  \"item.indrev.module_protection.tooltip\": \"Увеличивает защиту, обеспечиваемой бронёй.\",\n  \"item.indrev.module_feather_falling\": \"Модуль: Невесомость\",\n  \"item.indrev.module_feather_falling.tooltip\": \"Нейтрализует урон от падения за стоимость заряда щита.\",\n  \"item.indrev.module_speed\": \"Модуль: Скорость\",\n  \"item.indrev.module_speed.tooltip\": \"Даёт скорость за стоимость энергии.\",\n  \"item.indrev.module_jump_boost\": \"Модуль: Прыгучесть\",\n  \"item.indrev.module_jump_boost.tooltip\": \"Даёт прыжок за стоимость энергии.\",\n  \"item.indrev.module_night_vision\": \"Модуль: Ночное зрение\",\n  \"item.indrev.module_night_vision.tooltip\": \"Даёт ночное зрение за стоимость энергии.\",\n  \"item.indrev.module_breathing\": \"Модуль: Дыхание\",\n  \"item.indrev.module_breathing.tooltip\": \"Даёт подводное дыхание за стоимость энергии.\",\n  \"item.indrev.module_auto_feeder\": \"Модуль: Авто-кормушка\",\n  \"item.indrev.module_auto_feeder.tooltip\": \"Автоматически кормит едой из инвентаря за стоимость энергии.\",\n  \"item.indrev.module_charger\": \"Модуль: Зарядник\",\n  \"item.indrev.module_charger.tooltip\": \"Заряжает любые предметы в инвентаре (не включая броню).\",\n  \"item.indrev.module_solar_panel\": \"Модуль: Солнечный генератор\",\n  \"item.indrev.module_solar_panel.tooltip\": \"Заряжает под солнцем броню и держащий в руке предмет.\",\n  \"item.indrev.module_piglin_tricker\": \"Модуль: Обман пиглина\",\n  \"item.indrev.module_piglin_tricker.tooltip\": \"Обманом заставит Пиглинов думать, что ты носишь золото.\",\n  \"item.indrev.module_elytra\": \"Модуль: Встроенные элитры\",\n  \"item.indrev.module_elytra.tooltip\": \"Устанавливает в нагрудник Укреплённые элитры.\",\n  \"item.indrev.module_jetpack\": \"Модуль: Встроенный реактивный ранец\",\n  \"item.indrev.module_jetpack.tooltip\": \"Устанавливает в нагрудник Реактивный ранец.\",\n  \"item.indrev.module_magnet\": \"Модуль: Магнит\",\n  \"item.indrev.module_magnet.tooltip\": \"Притягивает предметы и опыт.\",\n  \"item.indrev.module_water_affinity\": \"Модуль: Подводник\",\n  \"item.indrev.module_water_affinity.on\": \"Находясь в: %s\",\n  \"item.indrev.module_water_affinity.chestplate\": \"нагруднике\",\n  \"item.indrev.module_water_affinity.leggings\": \"поножах\",\n  \"item.indrev.module_water_affinity.tooltip\": \"ускоряет копание под водой.\",\n  \"item.indrev.module_water_affinity.tooltip1\": \"ускоряет плавание/передвижение под водой.\",\n  \"item.indrev.module_fire_resistance\": \"Модуль: Огнестойкость\",\n  \"item.indrev.module_fire_resistance.tooltip\": \"Даёт огнестойкость за стоимость заряда щита.\",\n  \"item.indrev.module_efficiency\": \"Модуль: Эффективность\",\n  \"item.indrev.module_efficiency.tooltip\": \"Увеличивает скорость добычи инструмента.\",\n  \"item.indrev.module_range\": \"Модуль: Диапазон\",\n  \"item.indrev.module_range.tooltip\": \"Увеличивает радиус блоков Бура, который может сломать за раз.\",\n  \"item.indrev.module_fortune\": \"Модуль: Удача\",\n  \"item.indrev.module_fortune.tooltip\": \"Ведёт себя как зачарование Удача.\",\n  \"item.indrev.module_silk_touch\": \"Модуль: Шёлковое касание\",\n  \"item.indrev.module_silk_touch.tooltip\": \"Ведёт себя как зачарование Шёлковое касание.\",\n  \"item.indrev.module_reach\": \"Модуль: Досягаемость\",\n  \"item.indrev.module_reach.tooltip\": \"Наносит урон сразу нескольким мобам.\",\n  \"item.indrev.module_looting\": \"Модуль: Добыча\",\n  \"item.indrev.module_looting.tooltip\": \"Ведёт себя как зачарование Добыча.\",\n  \"item.indrev.module_fire_aspect\": \"Модуль: Заговор огня\",\n  \"item.indrev.module_fire_aspect.tooltip\": \"Ведёт себя как зачарование Заговор огня.\",\n  \"item.indrev.module_sharpness\": \"Модуль: Острота\",\n  \"item.indrev.module_sharpness.tooltip\": \"Увеличивает наносимый урон.\",\n  \"item.indrev.module_color.tooltip\": \"§cЦ§eв§aе§9т§r\",\n  \"item.indrev.module_color_pink\": \"Модуль: Розовый краситель\",\n  \"item.indrev.module_color_red\": \"Модуль: Красный краситель\",\n  \"item.indrev.module_color_purple\": \"Модуль: Фиолетовый краситель\",\n  \"item.indrev.module_color_blue\": \"Модуль: Синий краситель\",\n  \"item.indrev.module_color_cyan\": \"Модуль: Бирюзовый краситель\",\n  \"item.indrev.module_color_green\": \"Модуль: Зелёный краситель\",\n  \"item.indrev.module_color_yellow\": \"Модуль: Жёлтый краситель\",\n  \"item.indrev.module_color_orange\": \"Модуль: Оранжевый краситель\",\n  \"item.indrev.module_color_black\": \"Модуль: Чёрный краситель\",\n  \"item.indrev.module_color_brown\": \"Модуль: Коричневый краситель\",\n  \"item.indrev.copper_sword\": \"Медный меч\",\n  \"item.indrev.copper_pickaxe\": \"Медная кирка\",\n  \"item.indrev.copper_axe\": \"Медный топор\",\n  \"item.indrev.copper_shovel\": \"Медная лопата\",\n  \"item.indrev.copper_hoe\": \"Медная мотыга\",\n  \"item.indrev.copper_helmet\": \"Медный шлем\",\n  \"item.indrev.copper_chestplate\": \"Медный нагрудник\",\n  \"item.indrev.copper_leggings\": \"Медные поножи\",\n  \"item.indrev.copper_boots\": \"Медные ботинки\",\n  \"item.indrev.tin_sword\": \"Оловянный меч\",\n  \"item.indrev.tin_pickaxe\": \"Оловянная кирка\",\n  \"item.indrev.tin_axe\": \"Оловянный топор\",\n  \"item.indrev.tin_shovel\": \"Оловянная лопата\",\n  \"item.indrev.tin_hoe\": \"Оловянная мотыга\",\n  \"item.indrev.tin_helmet\": \"Оловянный шлем\",\n  \"item.indrev.tin_chestplate\": \"Оловянный нагрудник\",\n  \"item.indrev.tin_leggings\": \"Оловянные поножи\",\n  \"item.indrev.tin_boots\": \"Оловянные ботинки\",\n\n  \"category.indrev\": \"Industrial Revolution\",\n  \"key.indrev.modular\": \"Конфигурация модульных предметов\",\n\n  \"text.multiblock.not_built\": \"Конструкция не построена!\",\n\n  \"advancements.indrev.nikolite\": \"Начало\",\n  \"advancements.indrev.nikolite.description\": \"Найди наколит\",\n  \"advancements.indrev.machine_block\": \"Основы\",\n  \"advancements.indrev.machine_block.description\": \"Создай машинный блок\",\n  \"advancements.indrev.coal_generator\": \"Первые шаги\",\n  \"advancements.indrev.coal_generator.description\": \"Создай угольный генератор\",\n  \"advancements.indrev.basic_solar_generator\": \"Пассивная энергия\",\n  \"advancements.indrev.basic_solar_generator.description\": \"Создай базовую солнечный генератор\",\n  \"advancements.indrev.advanced_solar_generator\": \"Ещё пассивной энергии\",\n  \"advancements.indrev.advanced_solar_generator.description\": \"Создай продвинутый солнечный генератор\",\n  \"advancements.indrev.heat_generator\": \"Становится всё горячее\",\n  \"advancements.indrev.heat_generator.description\": \"Создай тепловой генератор\",\n  \"advancements.indrev.biomass_generator\": \"Не так экологически безвредный\",\n  \"advancements.indrev.biomass_generator.description\": \"Создай генератор на биомассе\",\n  \"advancements.indrev.pulverizer\": \"Становится пылью\",\n  \"advancements.indrev.pulverizer.description\": \"Создай измельчитель\",\n  \"advancements.indrev.electric_furnace\": \"Лучше печи\",\n  \"advancements.indrev.electric_furnace.description\": \"Создай электропечь\",\n  \"advancements.indrev.compressor\": \"Следи за своими пальцами\",\n  \"advancements.indrev.compressor.description\": \"Создай компрессор\",\n  \"advancements.indrev.recycler\": \"Экологически безвредный\",\n  \"advancements.indrev.recycler.description\": \"Создай переработчик\",\n  \"advancements.indrev.solid_infuser\": \"Вливание\",\n  \"advancements.indrev.solid_infuser.description\": \"Создай твердотельный настойник\",\n  \"advancements.indrev.mk2_upgrade\": \"Двойная эффективность\",\n  \"advancements.indrev.mk2_upgrade.description\": \"Улучши машину до мк. 2\",\n  \"advancements.indrev.mk3_upgrade\": \"Тройная эффективность\",\n  \"advancements.indrev.mk3_upgrade.description\": \"Улучши машину до мк. 3\",\n  \"advancements.indrev.mk4_upgrade\": \"Завод. Должен. Расти.\",\n  \"advancements.indrev.mk4_upgrade.description\": \"Улучши машину до мк. 4\",\n  \"advancements.indrev.biomass\": \"Выглядит неприлично...\",\n  \"advancements.indrev.biomass.description\": \"Создай биомассу\",\n  \"advancements.indrev.nikolite_ingot\": \"Новые горизонты\",\n  \"advancements.indrev.nikolite_ingot.description\": \"Создай николитовый слиток\",\n  \"advancements.indrev.enriched_nikolite_dust\": \"Мощная электроника\",\n  \"advancements.indrev.enriched_nikolite_dust.description\": \"Создай обогащённый николитовую пыль\",\n  \"advancements.indrev.enriched_nikolite_ingot\": \"Больше. Энергии.\",\n  \"advancements.indrev.enriched_nikolite_ingot.description\": \"Создай обогащённый николитовый слиток\",\n  \"advancements.indrev.lazuli_flux_container_mk1\": \"Базовый накопитель энергии\",\n  \"advancements.indrev.lazuli_flux_container_mk1.description\": \"Создай лазуритовый флаксовый резервуар мк. 1\",\n  \"advancements.indrev.lazuli_flux_container_mk2\": \"Заурядный накопитель энергии\",\n  \"advancements.indrev.lazuli_flux_container_mk2.description\": \"Создай лазуритовый флаксовый резервуар мк. 2\",\n  \"advancements.indrev.lazuli_flux_container_mk3\": \"Не такой уж заурядный накопитель энергии\",\n  \"advancements.indrev.lazuli_flux_container_mk3.description\": \"Создай лазуритовый флаксовый резервуар мк. 3\",\n  \"advancements.indrev.lazuli_flux_container_mk4\": \"Конечный накопитель энергии\",\n  \"advancements.indrev.lazuli_flux_container_mk4.description\": \"Создай лазуритовый флаксовый резервуар мк. 4\",\n  \"advancements.indrev.empty_enhancer\": \"Милая пластина\",\n  \"advancements.indrev.empty_enhancer.description\": \"Создай пустой усилитель\",\n  \"advancements.indrev.speed_enhancer\": \"Машина включена Бр-р-р-р\",\n  \"advancements.indrev.speed_enhancer.description\": \"Создай улучшение: скорость\",\n  \"advancements.indrev.buffer_enhancer\": \"Сверх предела\",\n  \"advancements.indrev.buffer_enhancer.description\": \"Создай улучшение: буфер\",\n  \"advancements.indrev.energy_enhancer\": \"Всегда эффективен\",\n  \"advancements.indrev.energy_enhancer.description\": \"Создай улучшение: энергия\",\n  \"advancements.indrev.cable_mk1\": \"Жёлтые кабели!\",\n  \"advancements.indrev.cable_mk1.description\": \"Создай кабель мк. 1\",\n  \"advancements.indrev.cable_mk2\": \"Синие кабели!\",\n  \"advancements.indrev.cable_mk2.description\": \"Создай кабель мк. 2\",\n  \"advancements.indrev.cable_mk3\": \"Фиолетовые кабели!\",\n  \"advancements.indrev.cable_mk3.description\": \"Создай кабель мк. 3\",\n  \"advancements.indrev.cable_mk4\": \"Красные кабели!\",\n  \"advancements.indrev.cable_mk4.description\": \"Создай кабель мк. 4\",\n  \"advancements.indrev.modular_workbench\": \"Он не выглядит как Верстак\",\n  \"advancements.indrev.modular_workbench.description\": \"Создай модульный верстак\",\n  \"advancements.indrev.modular_armor\": \"Модульность\",\n  \"advancements.indrev.modular_armor.description\": \"Создай полный набор Модульной брони\",\n  \"advancements.indrev.gamer_axe\": \"Не достаточно RGB\",\n  \"advancements.indrev.gamer_axe.description\": \"Создай геймерский топор\",\n  \"advancements.indrev.chopper_mk4\": \"Авто добыча дерева\",\n  \"advancements.indrev.chopper_mk4.description\": \"Создай лесоруб\",\n  \"advancements.indrev.rancher_mk1\": \"Я, робот-фермер\",\n  \"advancements.indrev.rancher_mk1.description\": \"Создай фермер\",\n  \"advancements.indrev.rancher_mk4\": \"Механизированное производство\",\n  \"advancements.indrev.rancher_mk4.description\": \"Создай скотовод\",\n  \"advancements.indrev.industrial_smelter\": \"Улучшенная переработка руды\",\n  \"advancements.indrev.industrial_smelter.description\": \"Создай производственную плавильную печь\",\n  \"advancements.indrev.condenser\": \"У меня закончились идеи\",\n  \"advancements.indrev.condenser.description\": \"Создай конденсатор\",\n  \"advancements.indrev.blast_furnace_enhancer\": \"Доменная электропечь\",\n  \"advancements.indrev.blast_furnace_enhancer.description\": \"Создай улучшение: горячее дутьё\",\n  \"advancements.indrev.smoker_enhancer\": \"Мечта шефа\",\n  \"advancements.indrev.smoker_enhancer.description\": \"Создай улучшение: коптильня\",\n  \"advancements.indrev.circuit_mk1\": \"Первые машины\",\n  \"advancements.indrev.circuit_mk1.description\": \"Создай схему мк. 1\",\n  \"advancements.indrev.circuit_mk2\": \"Новая технология\",\n  \"advancements.indrev.circuit_mk2.description\": \"Создай схему мк. 2\",\n  \"advancements.indrev.circuit_mk3\": \"Продвинутая технология\",\n  \"advancements.indrev.circuit_mk3.description\": \"Создай схему мк. 3\",\n  \"advancements.indrev.circuit_mk4\": \"Конечная технология\",\n  \"advancements.indrev.circuit_mk4.description\": \"Создай схему мк. 4\",\n  \"advancements.indrev.farmer_mk1\": \"Счастливая ферма\",\n  \"advancements.indrev.farmer_mk1.description\": \"Создай фермер мк. 1\",\n  \"advancements.indrev.slaughter_mk1\": \"Конечный убийца мобов\",\n  \"advancements.indrev.slaughter_mk1.description\": \"Создай забойник мк. 1\",\n\n  \"gui.indrev.tip\": \"ПОДСКАЗКА \",\n  \"gui.indrev.tip_0\": \"Убедись, что настроил вход/выход для предмета и жидкости.\",\n  \"gui.indrev.tip_1\": \"Не использовать кабели, превышающие предел ввода.\",\n  \"gui.indrev.tip_2\": \"Используй охладители, как Крутой(ая) парень/девушка.\",\n  \"gui.indrev.tip_3\": \"Используй охладители, чтобы никогда не терять эффективность.\",\n  \"gui.indrev.tip_4\": \"Завод. Должен. Расти.\",\n  \"gui.indrev.tip_5\": \"Тебе следует проверить Модульную броню.\",\n  \"gui.indrev.tip_6\": \"Реальные геймеры используют Геймерский топор.\",\n  \"gui.indrev.tip_7\": \"Если у тебя есть проблемы или предложения, присоединяйся в наш Discord (Только для англоязычных игроков).\",\n  \"gui.indrev.tip_8\": \"Прежде чем обновиться, всегда делай резервную копию миров.\",\n  \"gui.indrev.tip_9\": \"Прикрой свои пальцы в ночное время.\",\n  \"gui.indrev.modules_installed\": \"Установлено модулей: \",\n  \"gui.indrev.shield\": \"Щит: \",\n  \"gui.indrev.installing\": \"Установка\",\n  \"gui.indrev.incompatible\": \"Нельзя установить.\",\n  \"gui.indrev.max_level\": \"Макс. уровень модуля.\",\n  \"gui.indrev.progress\": \"Прогресс: \",\n  \"gui.indrev.whitelist.true\": \"Белый список\",\n  \"gui.indrev.whitelist.false\": \"Чёрный список\",\n  \"gui.indrev.matchDurability.true\": \"Соответствовать по повреждению предмета.\",\n  \"gui.indrev.matchDurability.false\": \"Игнорировать повреждение предмета.\",\n  \"gui.indrev.matchTag.true\": \"Соответствовать предмет по NBT.\",\n  \"gui.indrev.matchTag.false\": \"Игнорировать NBT предмета.\",\n\n  \"death.attack.acid\": \"%s был растворён в серной кислоте.\",\n  \"death.attack.acid.player\": \"%s был растворён в серной кислоте, при попытке к бегству от %s.\",\n  \"death.attack.laser\": \"%s попал под лазер и поджарился.\",\n  \"death.attack.laser.player\": \"%s попал под лазер и поджарился, при попытке к бегству от %s.\",\n\n  \"vein.indrev.bauxite\": \"Боксит\",\n  \"vein.indrev.certus_quartz\": \"Истинный кварц\",\n  \"vein.indrev.peat\": \"Торф\",\n  \"vein.indrev.lignite\": \"Бурый уголь\",\n  \"vein.indrev.bituminous\": \"Каменный уголь\",\n  \"vein.indrev.anthracite\": \"Антрацит\",\n  \"vein.indrev.cuprite\": \"Куприт\",\n  \"vein.indrev.calaverite\": \"Калаверит\",\n  \"vein.indrev.calaverite_nether\": \"Незерский калаверит\",\n  \"vein.indrev.iridium\": \"Иридий\",\n  \"vein.indrev.siderite\": \"Сидерит\",\n  \"vein.indrev.limonite\": \"Лимонит\",\n  \"vein.indrev.hematite\": \"Гематит\",\n  \"vein.indrev.magnetite\": \"Магнетит\",\n  \"vein.indrev.chalcopryte\": \"Халькопирит\",\n  \"vein.indrev.nikolite\": \"Николит\",\n  \"vein.indrev.quartz\": \"Кварц\",\n  \"vein.indrev.argentite\": \"Сернистое серебро\",\n  \"vein.indrev.chlorargyrite\": \"Кераргирит\",\n  \"vein.indrev.cassiterite\": \"Касситерит\",\n  \"vein.indrev.stannite\": \"Станнит\",\n  \"vein.indrev.scheelite\": \"Шеелит\",\n  \"vein.indrev.wolframite\": \"Вольфрамит\",\n  \"vein.indrev.ferberite\": \"Вольфрамовокислое железо\",\n  \"vein.indrev.galena\": \"Галенит\",\n  \"vein.indrev.glowstonedeposit\": \"Месторождение светокамня\",\n  \"vein.indrev.soul_nether\": \"Месторождение незер-душ\",\n  \"vein.indrev.sulfur_nether\": \"Сера\",\n  \"vein.indrev.denseice\": \"Месторождение плотного льда\",\n\n  \"attribute.indrev.shield\": \"Щит\",\n  \"subtitles.indrev.laser\": \"Лазерный луч\",\n\n  \"item.indrev.reinforced_elytra\": \"Укреплённые элитры\",\n  \"item.indrev.jetpack_mk1\": \"Реактивный ранец мк. 1\",\n  \"item.indrev.jetpack_mk2\": \"Реактивный ранец мк. 2\",\n  \"item.indrev.jetpack_mk3\": \"Реактивный ранец мк. 3\",\n  \"item.indrev.jetpack_mk4\": \"Реактивный ранец мк. 4\",\n\n  \"block.indrev.gas_generator\": \"Газовый генератор\",\n  \"block.indrev.gas_generator_mk4\": \"Газовый генератор марк. 4\",\n\n  \"item.indrev.soot\": \"Копоть\",\n  \"item.indrev.carbon_fiber_plate\": \"Углеволоконная пластина\",\n  \"item.indrev.carbon_fiber_rod\": \"Углеволоконный стержень\",\n  \"item.indrev.carbon_fiber_helmet_frame\": \"Углеволоконный шлемовый каркас\",\n  \"item.indrev.carbon_fiber_chest_frame\": \"Углеволоконный нагрудный каркас\",\n  \"item.indrev.carbon_fiber_legs_frame\": \"Углеволоконный поножный каркас\",\n  \"item.indrev.carbon_fiber_boots_frame\": \"Углеволоконный ботиночный каркас\",\n\n  \"block.indrev.boiler\": \"Котёл\",\n  \"block.indrev.steam_turbine_mk4\": \"Паровая турбина\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/lang/tr_tr.json",
    "content": "{\n  \"item.patchouli.industrial_revolution_book.name\": \"Devrimci Rehber\",\n  \"item.indrev.guide_book\": \"Devrimci Rehber\",\n  \"item.patchouli.industrial_revolution_book.landing\": \"Bu kitap modun bazı önemli bakış açılarından gitmenizi sağlayacak.$(br2)Eğer bir problemle karşılaşırsanız, ya da sorunuz veya önerileriniz varsa, şurada sorun açın $(l:https://github.com/GabrielOlvH/Industrial-Revolution/issues)GitHub$() ya da Discord'umuza $(l:https://discord.com/invite/G4PjhEf)Katılın!$()$(br2)Oynadığınız için teşekkürler!\",\n  \"itemGroup.indrev.indrev_group\": \"Endüstriyel Devrim\",\n  \"block.machines.tooltip.io\": \"I/O: %s\",\n  \"block.indrev.lazuli_flux_container_1\": \"Lazuli Flux\",\n  \"block.indrev.lazuli_flux_container_2\": \"Konteyner\",\n  \"block.indrev.coal_generator\": \"Kömür Jeneratörü\",\n  \"block.indrev.coal_generator_mk1\": \"Kömür Jeneratörü\",\n  \"block.indrev.heat_generator\": \"Isı Jeneratörü\",\n  \"block.indrev.heat_generator_mk4\": \"Isı Jeneratörü\",\n  \"block.indrev.electric_furnace\": \"Elektrikli Fırın\",\n  \"block.indrev.electric_furnace_mk1\": \"Elektrikli Fırın MK1\",\n  \"block.indrev.electric_furnace_mk2\": \"Elektrikli Fırın MK2\",\n  \"block.indrev.electric_furnace_mk3\": \"Elektrikli Fırın MK3\",\n  \"block.indrev.electric_furnace_mk4\": \"Elektrikli Fırın MK4\",\n  \"block.indrev.electric_furnace_creative\": \"Elektrikli Fırın Yaratıcı\",\n  \"block.indrev.electric_furnace_factory\": \"Elektrikli Fırın Fabrikası\",\n  \"block.indrev.electric_furnace_factory_mk4\": \"Elektrikli Fırın Fabrikası MK4\",\n  \"block.indrev.smelter\": \"Endüstriyel Dökümcü\",\n  \"block.indrev.smelter_mk4\": \"Endüstriyel Dökümcü\",\n  \"block.indrev.condenser\": \"Yoğunlaştırıcı\",\n  \"block.indrev.condenser_mk4\": \"Yoğunlaştırıcı\",\n  \"block.indrev.pulverizer\": \"Öğütücü\",\n  \"block.indrev.pulverizer_mk1\": \"Öğütücü MK1\",\n  \"block.indrev.pulverizer_mk2\": \"Öğütücü MK2\",\n  \"block.indrev.pulverizer_mk3\": \"Öğütücü MK3\",\n  \"block.indrev.pulverizer_mk4\": \"Öğütücü MK4\",\n  \"block.indrev.pulverizer_creative\": \"Yaratıcı Öğütücü\",\n  \"block.indrev.pulverizer_factory\": \"Öğütücü Fabrikası\",\n  \"block.indrev.pulverizer_factory_mk4\": \"Öğütücü Fabrikası MK4\",\n  \"block.indrev.sawmill\": \"Keresteci\",\n  \"block.indrev.sawmill_mk1\": \"Keresteci MK1\",\n  \"block.indrev.sawmill_mk2\": \"Keresteci MK2\",\n  \"block.indrev.sawmill_mk3\": \"Keresteci MK3\",\n  \"block.indrev.sawmill_mk4\": \"Keresteci MK4\",\n  \"block.indrev.sawmill_creative\": \"Keresteci Yaratıcı\",\n  \"block.indrev.compressor\": \"Kompresör\",\n  \"block.indrev.compressor_mk1\": \"Kompresör MK1\",\n  \"block.indrev.compressor_mk2\": \"Kompresör MK2\",\n  \"block.indrev.compressor_mk3\": \"Kompresör MK3\",\n  \"block.indrev.compressor_mk4\": \"Kompresör MK4\",\n  \"block.indrev.compressor_creative\": \"Kompresör Yaratıcı\",\n  \"block.indrev.compressor_factory\": \"Kompresör Fabrikası\",\n  \"block.indrev.compressor_factory_mk4\": \"Kompresör Fabrikası MK4\",\n  \"block.indrev.solid_infuser_factory\": \"Katı İnfüzör Fabrikası\",\n  \"block.indrev.solid_infuser\": \"Katı İnfüzör\",\n  \"block.indrev.solid_infuser_mk1\": \"Katı İnfüzör MK1\",\n  \"block.indrev.solid_infuser_mk2\": \"Katı İnfüzör MK2\",\n  \"block.indrev.solid_infuser_mk3\": \"Katı İnfüzör MK3\",\n  \"block.indrev.solid_infuser_mk4\": \"Katı İnfüzör MK4\",\n  \"block.indrev.solid_infuser_creative\": \"Katı İnfüzör Yaratıcı\",\n  \"block.indrev.solid_infuser_factory_mk4\": \"Katı İnfüzör Fabrikası MK4\",\n  \"block.indrev.lazuli_flux_container_mk1\": \"Lazuli Flux Konteyneri MK1\",\n  \"block.indrev.lazuli_flux_container_mk2\": \"Lazuli Flux Konteyneri MK2\",\n  \"block.indrev.lazuli_flux_container_mk3\": \"Lazuli Flux Konteyneri MK3\",\n  \"block.indrev.lazuli_flux_container_mk4\": \"Lazuli Flux Konteyneri MK4\",\n  \"block.indrev.lazuli_flux_container_creative\": \"Yaratıcı Lazuli Flux Konteyneri\",\n  \"block.indrev.cable_mk1\": \"Kablo MK1\",\n  \"block.indrev.cable_mk2\": \"Kablo MK2\",\n  \"block.indrev.cable_mk3\": \"Kablo MK3\",\n  \"block.indrev.cable_mk4\": \"Kablo MK4\",\n  \"block.indrev.fluid_pipe_mk1\": \"Sıvı Borusu MK1\",\n  \"block.indrev.fluid_pipe_mk2\": \"Sıvı Borusu MK2\",\n  \"block.indrev.fluid_pipe_mk3\": \"Sıvı Borusu MK3\",\n  \"block.indrev.fluid_pipe_mk4\": \"Sıvı Borusu MK4\",\n  \"block.indrev.item_pipe_mk1\": \"Eşya Borusu MK1\",\n  \"block.indrev.item_pipe_mk2\": \"Eşya Borusu MK2\",\n  \"block.indrev.item_pipe_mk3\": \"Eşya Borusu MK3\",\n  \"block.indrev.item_pipe_mk4\": \"Eşya Borusu MK4\",\n  \"block.indrev.fisher\": \"Balıkçı\",\n  \"block.indrev.fisher_mk2\": \"Basit Balıkçı\",\n  \"block.indrev.fisher_mk3\": \"İyileştirilmiş Balıkçı\",\n  \"block.indrev.fisher_mk4\": \"Gelişmiş Balıkçı\",\n  \"block.indrev.mining_rig\": \"Madenci Teçhizat Yöneticisi\",\n  \"block.indrev.mining_rig_mk4\": \"Madenci Teçhizat Yöneticisi\",\n  \"block.indrev.mining_rig.tooltip\": \" Madenciyi kullanmadan önce bu Chunk'da Chunk Tarayıcısını kullanmalısınız\",\n  \"block.indrev.mining_rig.mined\": \"%s kazıldı\",\n  \"block.indrev.drill\": \"madencilik teçhizatı matkabı\",\n  \"block.indrev.drill_bottom\": \"madencilik teçhizatı matkabı\",\n  \"block.indrev.drill_middle\": \"madencilik teçhizatı matkabı\",\n  \"block.indrev.drill_top\": \"madencilik teçhizatı matkabı\",\n  \"block.indrev.drill.faster\": \"%sx daha hızlı\",\n  \"block.indrev.drill.no_drills\": \"Boş.\",\n  \"block.indrev.drill.active\": \"Aktif matkaplar\",\n  \"block.indrev.drill.activating\": \"Hazır değil\",\n  \"block.indrev.drill.not_enough_power\": \"Yetersiz güç\",\n  \"block.indrev.drill.power_required\": \"Çalışmak için en az %sLF gerektirir\",\n  \"block.indrev.recycler\": \"Geri Dönüştürücü\",\n  \"block.indrev.recycler_mk2\": \"Geri Dönüştürücü\",\n  \"block.indrev.cable\": \"Kablo\",\n  \"block.indrev.farmer_mk1\": \"Çiftçi MK1\",\n  \"block.indrev.farmer_mk2\": \"Çiftçi MK2\",\n  \"block.indrev.farmer_mk3\": \"Çiftçi MK3\",\n  \"block.indrev.farmer_mk4\": \"Çiftçi MK4\",\n  \"block.indrev.farmer_creative\": \"Yaratıcı Çiftçi\",\n  \"block.indrev.farmer\": \"Çiftçi\",\n  \"block.indrev.slaughter_mk1\": \"Kırımcı MK1\",\n  \"block.indrev.slaughter_mk2\": \"Kırımcı MK2\",\n  \"block.indrev.slaughter_mk3\": \"Kırımcı MK3\",\n  \"block.indrev.slaughter_mk4\": \"Kırımcı MK4\",\n  \"block.indrev.slaughter_creative\": \"Yaratıcı Kırımcı\",\n  \"block.indrev.slaughter\": \"Kırımcı\",\n  \"block.indrev.chopper\": \"Oduncu\",\n  \"block.indrev.chopper_mk1\": \"Oduncu MK1\",\n  \"block.indrev.chopper_mk2\": \"Oduncu MK2\",\n  \"block.indrev.chopper_mk3\": \"Oduncu MK3\",\n  \"block.indrev.chopper_mk4\": \"Oduncu MK4\",\n  \"block.indrev.chopper_creative\": \"Yaratıcı Oduncu\",\n  \"block.indrev.rancher\": \"Besici\",\n  \"block.indrev.rancher_mk1\": \"Besici MK1\",\n  \"block.indrev.rancher_mk2\": \"Besici MK2\",\n  \"block.indrev.rancher_mk3\": \"Besici MK3\",\n  \"block.indrev.rancher_mk4\": \"Besici MK4\",\n  \"block.indrev.rancher_creative\": \"Yaratıcı Çiftlik\",\n  \"block.indrev.fluid_infuser\": \"Sıvı İnfüzör\",\n  \"block.indrev.fluid_infuser_mk1\": \"Sıvı İnfüzör MK1\",\n  \"block.indrev.fluid_infuser_mk2\": \"Sıvı İnfüzör MK2\",\n  \"block.indrev.fluid_infuser_mk3\": \"Sıvı İnfüzör MK3\",\n  \"block.indrev.fluid_infuser_mk4\": \"Sıvı İnfüzör MK4\",\n  \"block.indrev.fluid_infuser_creative\": \"Sıvı İnfüzör Yaratıcı\",\n  \"block.indrev.drain_mk1\": \"Süzgeç\",\n  \"block.indrev.tank\": \"Tank\",\n  \"block.indrev.factory\": \"Fabrika\",\n  \"block.indrev.biomass_generator\": \"Biyoyakıt Jeneratörü\",\n  \"block.indrev.biomass_generator_mk3\": \"Biyoyakıt Jeneratörü\",\n  \"block.indrev.solar_generator\": \"Güneş Jeneratörü\",\n  \"block.indrev.solar_generator_mk1\": \"Güneş Jeneratörü\",\n  \"block.indrev.solar_generator_mk3\": \"Güneş Jeneratörü\",\n  \"block.indrev.charge_pad_mk4\": \"Şarj Pad'i\",\n  \"block.indrev.charge_pad_mk4.tooltip\": \"Takılı ekipmanları ya da içine koyduğun herhangi birşeyi şarj eder!\",\n  \"block.indrev.aoe.range\": \"Menzil: %s\",\n  \"block.indrev.aoe.toggle.false\": \"Menzili göster\",\n  \"block.indrev.aoe.toggle.true\": \"Menzili gizle\",\n  \"block.indrev.planks\": \"Tahta\",\n  \"block.indrev.plank_block\": \"Tahta bloğu\",\n  \"block.indrev.wither_proof_obsidian\": \"Wither Geçirmez Obsidyen\",\n  \"block.indrev.controller\": \"Yönetici\",\n  \"block.indrev.frame\": \"İskelet\",\n  \"block.indrev.duct\": \"Kanal\",\n  \"block.indrev.warning_strobe\": \"Uyarı flaşı\",\n  \"block.indrev.silo\": \"Silo\",\n  \"block.indrev.intake\": \"Giriş\",\n  \"block.indrev.cabinet\": \"Kabinet\",\n  \"block.indrev.coolant\": \"Soğutucu\",\n  \"block.indrev.molten_iron\": \"Erimiş Demir\",\n  \"block.indrev.molten_gold\": \"Erimiş Altın\",\n  \"block.indrev.molten_copper\": \"Erimiş Bakır\",\n  \"block.indrev.molten_tin\": \"Erimiş Tin\",\n  \"block.indrev.molten_netherite\": \"Erimiş Netherit\",\n  \"block.indrev.sulfuric_acid\": \"Sülfürik Asit\",\n  \"item.indrev.sulfuric_acid_bucket\": \"Sülfürik Asit Kovası\",\n  \"block.indrev.toxic_mud\": \"Toksik çamur\",\n  \"item.indrev.toxic_mud_bucket\": \"Toksik çamur Kovası\",\n  \"block.indrev.machine_block\": \"Makine Bloğu\",\n  \"item.indrev.modular_core\": \"Modülerliğin Merkezi (Şarj olmamış)\",\n  \"item.indrev.modular_core_activated\": \"Modülerliğin Merkezi\",\n  \"item.indrev.sawdust\": \"Talaş\",\n  \"block.indrev.laser_emitter_mk4\": \"Laser Yayıcı\",\n  \"block.indrev.capsule\": \"Kapsül\",\n  \"block.indrev.tin_ore\": \"Tin Cevheri\",\n  \"block.indrev.tin_block\": \"Tin bloğu\",\n  \"item.indrev.tin_ingot\": \"Tin Külçesi\",\n  \"item.indrev.tin_chunk\": \"Tin Yığını\",\n  \"item.indrev.tin_dust\": \"Tin Tozu\",\n  \"item.indrev.tin_plate\": \"Tin Levha\",\n  \"item.indrev.tin_gear\": \"Tin Çark\",\n  \"item.indrev.tin_nugget\": \"Tin Parçası\",\n  \"item.indrev.molten_tin_bucket\": \"Erimiş Tin Kovası\",\n  \"item.indrev.tin_purified_ore\": \"Saflaştırılmış Tin Cevheri\",\n  \"item.indrev.copper_chunk\": \"Bakır Yığını\",\n  \"item.indrev.copper_dust\": \"Bakır Tozu\",\n  \"item.indrev.copper_plate\": \"Bakır Levha\",\n  \"item.indrev.copper_gear\": \"Bakır Çark\",\n  \"item.indrev.copper_nugget\": \"Bakır Parçası\",\n  \"item.indrev.copper_purified_ore\": \"Saflaştırılmış Bakır Cevheri\",\n  \"item.indrev.molten_copper_bucket\": \"Erimiş Bakır Kovası\",\n  \"block.indrev.molten_lead\": \"Erimiş Kurşun\",\n  \"item.indrev.molten_lead_bucket\": \"Erimiş Kurşun Kovası\",\n  \"block.indrev.lead_ore\": \"Kurşun Cevheri\",\n  \"block.indrev.lead_block\": \"Kurşun bloğu\",\n  \"item.indrev.lead_ingot\": \"Kurşun Külçesi\",\n  \"item.indrev.lead_chunk\": \"Kurşun Yığını\",\n  \"item.indrev.lead_dust\": \"Kurşun Tozu\",\n  \"item.indrev.lead_plate\": \"Kurşun Levha\",\n  \"item.indrev.lead_gear\": \"Kurşun Çark\",\n  \"item.indrev.lead_nugget\": \"Kurşun Parçası\",\n  \"item.indrev.lead_purified_ore\": \"Saflaştırılmış Kurşun Cevheri\",\n  \"item.indrev.raw_lead\": \"Ham Kurşun\",\n  \"item.indrev.raw_silver\": \"Ham Gümüş\",\n  \"item.indrev.raw_tungsten\": \"Ham Tungsten\",\n  \"item.indrev.raw_tin\": \"Ham Kalay\",\n  \"block.indrev.raw_lead_block\": \"Ham Kurşun Bloğu\",\n  \"block.indrev.raw_silver_block\": \"Ham Gümüş Bloğu\",\n  \"block.indrev.raw_tungsten_block\": \"Ham Tungsten Bloğu\",\n  \"block.indrev.raw_tin_block\": \"Ham Kalay Bloğu\",\n  \"block.indrev.molten_silver\": \"Erimiş Gümüş\",\n  \"item.indrev.molten_silver_bucket\": \"Erimiş Gümüş Kovası\",\n  \"block.indrev.silver_ore\": \"Gümüş Cevheri\",\n  \"block.indrev.silver_block\": \"Gümüş Bloğu\",\n  \"item.indrev.silver_ingot\": \"Gümüş Külçesi\",\n  \"item.indrev.silver_chunk\": \"Gümüş Yığını\",\n  \"item.indrev.silver_dust\": \"Gümüş Tozu\",\n  \"item.indrev.silver_plate\": \"Gümüş Levha\",\n  \"item.indrev.silver_gear\": \"Gümüş Çark\",\n  \"item.indrev.silver_nugget\": \"Gümüs Parçası\",\n  \"item.indrev.silver_purified_ore\": \"Saflaştırılmış Gümüş Cevheri\",\n  \"block.indrev.molten_tungsten\": \"Erimiş Tungsten\",\n  \"item.indrev.molten_tungsten_bucket\": \"Erimiş Tungsten Kovası\",\n  \"block.indrev.tungsten_ore\": \"Tungsten Cevheri\",\n  \"block.indrev.tungsten_block\": \"Tungsten Bloğu\",\n  \"item.indrev.tungsten_ingot\": \"Tungsten Külçesi\",\n  \"item.indrev.tungsten_chunk\": \"Tungsten Yığını\",\n  \"item.indrev.tungsten_dust\": \"Tungsten Tozu\",\n  \"item.indrev.tungsten_plate\": \"Tungsten Levha\",\n  \"item.indrev.tungsten_gear\": \"Tungsten Çark\",\n  \"item.indrev.tungsten_nugget\": \"Tungsten Parçası\",\n  \"item.indrev.tungsten_purified_ore\": \"Saflaştırılmış Tungsten Cevheri\",\n  \"block.indrev.deepslate_tungsten_ore\": \"Derin Kayrak Tungsten Cevheri\",\n  \"block.indrev.deepslate_tin_ore\": \"Derin Kayrak Tin Cevheri\",\n  \"block.indrev.deepslate_silver_ore\": \"Derin Kayrak Gümüş Cevheri\",\n  \"block.indrev.deepslate_nikolite_ore\": \"Derin Kayrak Nikolit Cevheri\",\n  \"block.indrev.deepslate_lead_ore\": \"Derim Kayrak Lead Cevheri\",\n  \"block.indrev.electrum_block\": \"Elektrum Bloğu\",\n  \"item.indrev.electrum_ingot\": \"Elektrum Külçesi\",\n  \"item.indrev.electrum_dust\": \"Elektrum Tozu\",\n  \"item.indrev.electrum_plate\": \"Elektrum Levha\",\n  \"item.indrev.electrum_gear\": \"Elektrum Çark\",\n  \"item.indrev.electrum_nugget\": \"Elektrum Parçası\",\n  \"block.indrev.bronze_block\": \"Bronz Bloğu\",\n  \"item.indrev.bronze_ingot\": \"Bronz Külçesi\",\n  \"item.indrev.bronze_chunk\": \"Bronz Yığını\",\n  \"item.indrev.bronze_dust\": \"Bronz Tozu\",\n  \"item.indrev.bronze_plate\": \"Bronz Levha\",\n  \"item.indrev.bronze_gear\": \"Bronz Çark\",\n  \"item.indrev.bronze_nugget\": \"Bronz Parçası\",\n  \"block.indrev.sulfur_crystal\": \"Sülfür Kristali\",\n  \"item.indrev.sulfur_crystal\": \"Sülfür Kristali\",\n  \"item.indrev.sulfur_dust\": \"Sülfür Tozu\",\n  \"block.indrev.steel_block\": \"Çelik Bloğu\",\n  \"item.indrev.steel_dust\": \"Çelik Tozu\",\n  \"item.indrev.steel_ingot\": \"Çelik Külçesi\",\n  \"item.indrev.steel_plate\": \"Çelik Levha\",\n  \"item.indrev.steel_gear\": \"Çelik Çark\",\n  \"item.indrev.steel_nugget\": \"Çelik Parçası\",\n  \"item.indrev.gold_dust\": \"Altın Tozu\",\n  \"item.indrev.gold_plate\": \"Altın Levha\",\n  \"item.indrev.gold_chunk\": \"Altın Yığını\",\n  \"item.indrev.gold_purified_ore\": \"Saflaştırılmış Altın Cevheri\",\n  \"item.indrev.molten_gold_bucket\": \"Erimiş Altın Kovası\",\n  \"item.indrev.iron_dust\": \"Demir Tozu\",\n  \"item.indrev.iron_plate\": \"Demir Levha\",\n  \"item.indrev.iron_chunk\": \"Demir Yığını\",\n  \"item.indrev.molten_iron_bucket\": \"Erimiş Demir Kovası\",\n  \"item.indrev.iron_purified_ore\": \"Saflaştırılmış Demir Cevheri\",\n  \"item.indrev.diamond_dust\": \"Elmas Tozu\",\n  \"item.indrev.coal_dust\": \"Kömür Tozu\",\n  \"block.indrev.pump_mk1\": \"Pompa\",\n  \"item.indrev.empty_enhancer\": \"Boş Güçlendirici\",\n  \"item.indrev.buffer_enhancer\": \"Arabellek Güçlendiricisi\",\n  \"item.indrev.buffer_enhancer.tooltip\": \"Enerji kapasitesini arttırır\",\n  \"item.indrev.energy_enhancer\": \"Enerji Verimlilik Güçlendiricisi\",\n  \"item.indrev.energy_enhancer.tooltip\": \"Enerji kullanımını azaltır\",\n  \"item.indrev.damage_enhancer\": \"Hasar Güçlendiricisi\",\n  \"item.indrev.damage_enhancer.tooltip\": \"Kırımcı'nın hasarını arttırır\",\n  \"item.indrev.speed_enhancer\": \"Hız Güçlendiricisi\",\n  \"item.indrev.speed_enhancer.tooltip\": \"Üretim hızını ve güç kullanımını arttırır\",\n  \"item.indrev.blast_furnace_enhancer\": \"Maden Fırını Güçlendiricisi\",\n  \"item.indrev.blast_furnace_enhancer.tooltip\": \"Elektrikli Fırın'ın Maden Fırını'nın tariflerini kabul etmesini sağlar\",\n  \"item.indrev.smoker_enhancer\": \"Duman Fırını Güçlendiricisi\",\n  \"item.indrev.smoker_enhancer.tooltip\": \"Elektrikli Fırın'ın Duman Fırını'nın tariflerini kabul etmesini sağlar\",\n  \"item.indrev.enhancers.incompatible\": \"Bu makine bu Güçlendiricisi kabul etmiyor\",\n  \"item.indrev.enhancers.count\": \"Makine tarafından her bölme için %s kadar sınırlandırılmıştır\",\n  \"item.indrev.rechargeable.tooltip\": \"Yeniden şarj edilebilir\",\n  \"item.indrev.chunk_scanner\": \"Chunk Tarayıcısı\",\n  \"item.indrev.chunk_scanner.scanning\": \"Tarıyor...\",\n  \"item.indrev.chunk_scanner.scanned1\": \"Tarama bitti!\",\n  \"item.indrev.chunk_scanner.scanned2\": \"Maden damarı türü: %s\",\n  \"item.indrev.chunk_scanner.already_scanned\": \"Bu Chunk zaten taranmış ve %s damarına sahip\",\n  \"item.indrev.chunk_scanner.tooltip1\": \"Chunk'ı taramak için sağ tıka basılı tutun\",\n  \"item.indrev.chunk_scanner.tooltip2\": \"Damar Türü: %s\",\n  \"item.indrev.chunk_scanner.tooltip3\": \"%s'den %s'ye\",\n  \"item.indrev.chunk_scanner.tooltip4\": \"Boyut: %s\",\n  \"item.indrev.chunk_scanner.tooltip5\": \"Daha fazla okumak için sağ tıklayın\",\n  \"item.indrev.fan\": \"Vantilatör\",\n  \"item.indrev.cooler_cell\": \"Soğutucu Hücre\",\n  \"item.indrev.heatsink\": \"Soğutucu\",\n  \"item.indrev.heat_coil\": \"Isı Bobini\",\n  \"item.indrev.heat_coil.tooltip\": \"Lazuli Flux'ı Isıya çevirir\",\n  \"item.indrev.stone_drill_head\": \"Taş Matkap Ucu\",\n  \"item.indrev.iron_drill_head\": \"Demir Matkap Ucu\",\n  \"item.indrev.diamond_drill_head\": \"Elmas Matkap Ucu\",\n  \"item.indrev.netherite_drill_head\": \"Netherit Matkap Ucu\",\n  \"item.indrev.mining_drill_mk1\": \"Kazı Matkabı MK1\",\n  \"item.indrev.mining_drill_mk2\": \"Kazı Matkabı MK2\",\n  \"item.indrev.mining_drill_mk3\": \"Kazı Matkabı MK3\",\n  \"item.indrev.mining_drill_mk4\": \"Modüler Kazı Matkabı\",\n  \"item.indrev.energy_reader\": \"Enerji Okuyucu\",\n  \"item.indrev.energy_reader.use\": \"Enerji Saklı:\",\n  \"item.indrev.energy_reader.use1\": \"%s LF/tick\",\n  \"item.indrev.enriched_nikolite_dust\": \"Zenginleştirilmiş Nikolit Tozu\",\n  \"item.indrev.enriched_nikolite_ingot\": \"Zenginleştirilmiş Nikolit Cevheri\",\n  \"item.indrev.battery\": \"Batarya\",\n  \"item.indrev.circuit_mk1\": \"MK1 Devre\",\n  \"item.indrev.circuit_mk2\": \"MK2 Devre\",\n  \"item.indrev.circuit_mk3\": \"MK3 Devre\",\n  \"item.indrev.circuit_mk4\": \"MK4 Devre\",\n  \"item.indrev.tier_upgrade_mk2\": \"MK2 Makine Arttırması\",\n  \"item.indrev.tier_upgrade_mk2.tooltip\": \"MK1 Makineleri MK2'ye yükseltir\",\n  \"item.indrev.tier_upgrade_mk3\": \"MK3 Makine Arttırması\",\n  \"item.indrev.tier_upgrade_mk3.tooltip\": \"MK2 Makineleri MK3'e yükseltir\",\n  \"item.indrev.tier_upgrade_mk4\": \"MK4 Makine Arttırması\",\n  \"item.indrev.tier_upgrade_mk4.tooltip\": \"MK3 Makineleri MK4'e yükseltir\",\n  \"item.indrev.servo_output\": \"Çıkış servosu\",\n  \"item.indrev.servo_output.tooltip\": \"Bağlı envanterlere iteler\",\n  \"item.indrev.servo_retriever\": \"Kurtarıcı Servo\",\n  \"item.indrev.servo_retriever.tooltip\": \"Bağlı envanterlerden çeker\",\n  \"item.indrev.servo.mode\": \"Öncelik: \",\n  \"item.indrev.servo.mode.round_robin\": \"Round Robin\",\n  \"item.indrev.servo.mode.round_robin.tooltip\": \"Önceliği en az eşya/sıvıdır\",\n  \"item.indrev.servo.mode.nearest_first\": \"İlk en yakın\",\n  \"item.indrev.servo.mode.nearest_first.tooltip\": \"Öncelik en yakındaki konteynerlerdedir\",\n  \"item.indrev.servo.mode.furthest_first\": \"İlk en uzak\",\n  \"item.indrev.servo.mode.furthest_first.tooltip\": \"Öncelik en uzaktaki konteynerlerdedir\",\n  \"item.indrev.servo.mode.random\": \"Rastgele\",\n  \"item.indrev.servo.mode.random.tooltip\": \"Öncelik yok\",\n  \"item.indrev.biomass\": \"Biyoyakıt\",\n  \"item.indrev.untanned_leather\": \"Bronzlaşmamış Deri\",\n  \"item.indrev.coolant_bucket\": \"Soğutucu Kovası\",\n  \"item.indrev.molten_netherite_bucket\": \"Erimiş Netherit Kovası\",\n  \"item.indrev.netherite_scrap_purified_ore\": \"Saflaştırılmış Netherit Hurdası\",\n  \"item.indrev.netherite_scrap_chunk\": \"Netherit Hurdası Yığını\",\n  \"item.indrev.netherite_scrap_dust\": \"Netherit Hurdası Tozu\",\n  \"item.indrev.hammer\": \"Çekiç\",\n  \"item.indrev.wrench\": \"İngiliz Anahtarı\",\n  \"item.indrev.wrench.switch_mode\": \"Anahtar Modu: %s\",\n  \"item.indrev.wrench.title\": \"Makineyi ayarla\",\n  \"item.indrev.wrench.autopush\": \"Oto itele\",\n  \"item.indrev.wrench.autopull\": \"Oto çek\",\n  \"item.indrev.wrench.item\": \"Eşya\",\n  \"item.indrev.wrench.fluid\": \"Sıvı\",\n  \"item.indrev.wrench.energy\": \"Enerji\",\n  \"item.indrev.wrench.output\": \"Çıkış\",\n  \"item.indrev.wrench.input\": \"Giriş\",\n  \"item.indrev.wrench.input_first\": \"Giriş (İlk Bölme)\",\n  \"item.indrev.wrench.input_second\": \"Giriş (İkinci Bölme)\",\n  \"item.indrev.wrench.input_output\": \"Giriş ve Çıkış\",\n  \"item.indrev.wrench.none\": \"Hiçbiri\",\n  \"item.indrev.wrench.tooltip\": \"Mod değişimi için Shift + sağ tık\",\n  \"item.indrev.wrench.tooltip1\": \"Mod: %s\",\n  \"item.indrev.wrench.tooltip1.rotate\": \"Döndür\",\n  \"item.indrev.wrench.tooltip1.configure\": \"Ayarla\",\n  \"item.indrev.wrench.mode\": \"Mod: %s\",\n  \"item.indrev.wrench.side.north\": \"Kuzey\",\n  \"item.indrev.wrench.side.south\": \"Güney\",\n  \"item.indrev.wrench.side.west\": \"Batı\",\n  \"item.indrev.wrench.side.east\": \"Doğu\",\n  \"item.indrev.wrench.side.up\": \"Yukarı\",\n  \"item.indrev.wrench.side.down\": \"Aşağı\",\n  \"item.indrev.wrench.side.top\": \"Yukarı\",\n  \"item.indrev.wrench.side.bottom\": \"Alt\",\n  \"item.indrev.wrench.side.left\": \"Sol\",\n  \"item.indrev.wrench.side.right\": \"Sağ\",\n  \"item.indrev.wrench.side.front\": \"Ön\",\n  \"item.indrev.wrench.side.back\": \"Arka\",\n  \"item.indrev.wrench.connected\": \"Bağlı olduğu: %s\",\n  \"item.indrev.tech_soup\": \"Teknoloji çorbası\",\n  \"item.indrev.scan_output\": \"Kaynak Raporu\",\n  \"item.indrev.portable_charger\": \"Taşınabilir Şarj Cihazı\",\n  \"item.indrev.gamer_axe\": \"Oyuncu Baltası\",\n  \"gui.indrev.button.auto_split\": \"Stack'leri oto-ayır\",\n  \"gui.indrev.resourcereport.size\": \"Damar Büyüklüğü: %s\",\n  \"gui.indrev.resourcereport.size.tiny\": \"Minik\",\n  \"gui.indrev.resourcereport.size.small\": \"Küçük\",\n  \"gui.indrev.resourcereport.size.average\": \"Ortalama\",\n  \"gui.indrev.resourcereport.size.big\": \"Büyük\",\n  \"gui.indrev.resourcereport.size.very_big\": \"Bayağı Büyük\",\n  \"gui.indrev.resourcereport.size.enormous\": \"Kocaman\",\n  \"gui.indrev.resourcereport.size.gigantic\": \"Koskocaman\",\n  \"gui.furnace.mode\": \"Mod\",\n  \"gui.furnace.mode.furnace\": \"Fırın\",\n  \"gui.furnace.mode.blast_furnace\": \"Maden Fırını\",\n  \"gui.furnace.mode.smoker\": \"Duman Fırını\",\n  \"gui.widget.process\": \"İlerleme: %s\",\n  \"gui.widget.energy\": \"Enerji\",\n  \"gui.widget.temperature\": \"Isı\",\n  \"gui.widget.temperature_info.high\": \"Çok sıcak\",\n  \"gui.widget.temperature_info.medium\": \"Tam potansiyel\",\n  \"gui.widget.temperature_info.low\": \"Isınmadı\",\n  \"gui.indrev.solar.on\": \"Üretiyor\",\n  \"gui.indrev.heatgen.idle\": \"Boşta\",\n  \"gui.indrev.heatgen.title\": \"%s mB harcıyor\",\n  \"gui.indrev.heatgen.generating\": \"%s LF üretiyor\",\n  \"gui.indrev.heatgen.pertick\": \"Tick başına\",\n  \"gui.indrev.heatgen.extra\": \"Dikkatli ol, çok sıcak!\",\n  \"gui.indrev.guide_book_shortcut.contains\": \"Devrimci Rehberi'nde Hakkında Sayfasını açar\",\n  \"gui.indrev.guide_book_shortcut.missing\": \"Devrimci Rehberi eksik!\",\n  \"gui.indrev.tooltip.maxTransferRate\": \"Aktarma kapasitesi: \",\n  \"gui.indrev.tooltip.maxInput\": \"Giriş kapasitesi: \",\n  \"gui.indrev.tooltip.maxOutput\": \"Çıkış kapasitesi: \",\n  \"gui.indrev.tooltip.maxEnergyStored\": \"Enerji kapasitesi: \",\n  \"gui.indrev.tooltip.seconds\": \"%s saniye\",\n  \"gui.indrev.tooltip.energyCost\": \"Enerji maliyeti: \",\n  \"gui.indrev.tooltip.processSpeed\": \"Hız: \",\n  \"gui.indrev.tooltip.ratio\": \"Üretim: \",\n  \"gui.indrev.tooltip.press_shift\": \"Daha fazla bilgi için %s'ye basılı tutun\",\n  \"gui.indrev.tooltip.lftick\": \"%s LF/tick\",\n  \"gui.indrev.tooltip.lf\": \"%s LF\",\n  \"gui.indrev.tooltip.itemsec\": \"%s eşyalar/s\",\n  \"gui.indrev.tooltip.fluidsec\": \"%s kovalar/s\",\n  \"gui.indrev.tooltip.temperatureBoost\": \"Isındı: \",\n  \"gui.indrev.locked\": \"Kilitli\",\n  \"gui.indrev.modular_armor_slot_type\": \"Buraya eşya ekleyin\",\n  \"gui.indrev.module_slot_type\": \"Buraya istenen modülü ekleyin\",\n  \"gui.indrev.scan_output_slot_type\": \"Kaynak Raporu\",\n  \"gui.indrev.output_slot_type\": \"Toplanan Eşyalar\",\n  \"gui.indrev.chopper_input_axe\": \"Balta Ekleyin\",\n  \"gui.indrev.chopper_input_bone_meal\": \"Kemik Tozu ekleyin\",\n  \"gui.indrev.chopper_input_sapling\": \"Fidan ekleyin\",\n  \"gui.indrev.farmer_input_slot_type\": \"Tohumlar ve kemik Tozları\",\n  \"gui.indrev.slaughter_input_sword\": \"Kılıç ekleyin\",\n  \"gui.indrev.cooler_slot_type\": \"Soğutucu\",\n  \"gui.indrev.battery_slot_type\": \"Batarya\",\n  \"gui.indrev.upgrade_slot_type\": \"Arttırmalar\",\n  \"gui.indrev.fishingrod\": \"Balık Oltası\",\n  \"indrev.category.rei.upgrading\": \"Bir %s oluşturmalı ve onu %s'e yükseltmek için %s üzerinde kullanmalısınız.\",\n  \"indrev.category.rei.pulverizing\": \"Öğütülüyor\",\n  \"indrev.category.rei.infusing\": \"İnfüzleniyor\",\n  \"indrev.category.rei.compressing\": \"Sıkıştırılıyor\",\n  \"indrev.category.rei.recycling\": \"Geri Dönüştürülüyor\",\n  \"indrev.category.rei.fluid_infusing\": \"Sıvı İnfüzleniyor\",\n  \"indrev.category.rei.condensing\": \"Yoğunlaştırılıyor\",\n  \"indrev.category.rei.smelting\": \"Pişiriliyor\",\n  \"indrev.category.rei.sawmill\": \"Kereste Fabrikası\",\n  \"indrev.category.rei.module\": \"Modül Üretimi\",\n  \"item.indrev.nikolite_dust\": \"Nikolit Tozu\",\n  \"item.indrev.nikolite_ingot\": \"Nikolit Külçesi\",\n  \"block.indrev.nikolite_ore\": \"Nikolite Cevheri\",\n  \"item.indrev.modular.upgrade\": \"Modüller Kurulu:\",\n  \"item.indrev.modular.upgrade.night_vision\": \"Gece Görüşü %s\",\n  \"item.indrev.modular.upgrade.speed\": \"Hız %s\",\n  \"item.indrev.modular.upgrade.jump_boost\": \"Zıplama Güçlendirmesi %s\",\n  \"item.indrev.modular.upgrade.breathing\": \"Nefes alma %s\",\n  \"item.indrev.modular.upgrade.protection\": \"Koruma %s\",\n  \"item.indrev.modular.upgrade.feather_falling\": \"Tüy Düşüşü %s\",\n  \"item.indrev.modular.upgrade.auto_feeder\": \"Oto Besleme %s\",\n  \"item.indrev.modular.upgrade.charger\": \"Şarj edici %s\",\n  \"item.indrev.modular.upgrade.solar_panel\": \"Güneş Paneli %s\",\n  \"item.indrev.modular.upgrade.piglin_tricker\": \"Piglin Kandırıcı %s\",\n  \"item.indrev.modular.upgrade.fire_resistance\": \"Ateş Koruması %s\",\n  \"item.indrev.modular.upgrade.efficiency\": \"Verimlilik %s\",\n  \"item.indrev.modular.upgrade.range\": \"Alan %s\",\n  \"item.indrev.modular.upgrade.fortune\": \"Servet %s\",\n  \"item.indrev.modular.upgrade.silk_touch\": \"İpeksi Dokunuş %s\",\n  \"item.indrev.modular.upgrade.sharpness\": \"Keskinlik %s\",\n  \"item.indrev.modular.upgrade.looting\": \"Yağma %s\",\n  \"item.indrev.modular.upgrade.fire_aspect\": \"Ateşten Çehre %s\",\n  \"item.indrev.modular.upgrade.reach\": \"Yetişme %s\",\n  \"item.indrev.steel_helmet\": \"Çelik Kask\",\n  \"item.indrev.steel_chestplate\": \"Çelik Göğüslük\",\n  \"item.indrev.steel_leggings\": \"Çelik Pantolon\",\n  \"item.indrev.steel_boots\": \"Çelik Botlar\",\n  \"item.indrev.steel_sword\": \"Çelik Kılıç\",\n  \"item.indrev.steel_pickaxe\": \"Çelik Kazma\",\n  \"item.indrev.steel_axe\": \"Çelik Balta\",\n  \"item.indrev.steel_shovel\": \"Çelik Kürek\",\n  \"item.indrev.steel_hoe\": \"Çelik Çapa\",\n  \"item.indrev.bronze_helmet\": \"Bronz Kask\",\n  \"item.indrev.bronze_chestplate\": \"Bronz Göğüslük\",\n  \"item.indrev.bronze_leggings\": \"Bronz Pantolon\",\n  \"item.indrev.bronze_boots\": \"Bronz Botlar\",\n  \"item.indrev.bronze_sword\": \"Bronz Kılıç\",\n  \"item.indrev.bronze_pickaxe\": \"Bronz Kazma\",\n  \"item.indrev.bronze_axe\": \"Bronz Balta\",\n  \"item.indrev.bronze_shovel\": \"Bronz Kürek\",\n  \"item.indrev.bronze_hoe\": \"Bronz Çapa\",\n  \"item.indrev.lead_helmet\": \"Lead Kask\",\n  \"item.indrev.lead_chestplate\": \"Lead Göğüslük\",\n  \"item.indrev.lead_leggings\": \"Lead Pantolon\",\n  \"item.indrev.lead_boots\": \"Lead Botlar\",\n  \"item.indrev.lead_sword\": \"Lead Kılıç\",\n  \"item.indrev.lead_pickaxe\": \"Lead Kazma\",\n  \"item.indrev.lead_axe\": \"Lead Balta\",\n  \"item.indrev.lead_shovel\": \"Lead Kürek\",\n  \"item.indrev.lead_hoe\": \"Lead Çapa\",\n  \"item.indrev.silver_helmet\": \"Gümüş Kask\",\n  \"item.indrev.silver_chestplate\": \"Gümüş Göğüslük\",\n  \"item.indrev.silver_leggings\": \"Gümüş Pantolon\",\n  \"item.indrev.silver_boots\": \"Gümüş Botlar\",\n  \"item.indrev.silver_sword\": \"Gümüş Kılıç\",\n  \"item.indrev.silver_pickaxe\": \"Gümüş Kazma\",\n  \"item.indrev.silver_axe\": \"Gümüş Balta\",\n  \"item.indrev.silver_shovel\": \"Gümüş Kürek\",\n  \"item.indrev.silver_hoe\": \"Gümüş Çapa\",\n  \"block.indrev.modular_workbench\": \"Modüler ÇalışmaMasası\",\n  \"block.indrev.modular_workbench_mk4\": \"Modüler ÇalışmaMasası\",\n  \"item.indrev.modular_armor_helmet\": \"Modüler Kask\",\n  \"item.indrev.modular_armor_chest\": \"Modüler Göğüslük\",\n  \"item.indrev.modular_armor_legs\": \"Modüler Pantolon\",\n  \"item.indrev.modular_armor_boots\": \"Modüler Botlar\",\n  \"item.indrev.module_parts\": \"Şunlara kurulabilir:\",\n  \"item.indrev.module_parts_head\": \"Kask\",\n  \"item.indrev.module_parts_chest\": \"Göğüslük\",\n  \"item.indrev.module_parts_legs\": \"Pantolon\",\n  \"item.indrev.module_parts_feet\": \"Botlar\",\n  \"item.indrev.module_parts_drill\": \"Kazı Matkabı\",\n  \"item.indrev.module_parts_gamer_axe\": \"Oyuncu Baltası\",\n  \"item.indrev.module_max_level\": \"En yüksek seviye: %s\",\n  \"item.indrev.module_protection\": \"Koruma Modülü \",\n  \"item.indrev.module_protection.tooltip\": \"Zırhın sağladığı korumayı arttırır\",\n  \"item.indrev.module_feather_falling\": \"Tüy Düşüşü Modülü\",\n  \"item.indrev.module_feather_falling.tooltip\": \"Enerji karşılığında düşüş hasarını azaltır.\",\n  \"item.indrev.module_speed\": \"Hız Modülü\",\n  \"item.indrev.module_speed.tooltip\": \"Enerji karşılığında hız efekti verir.\",\n  \"item.indrev.module_jump_boost\": \"Zıplama Desteği Modülü\",\n  \"item.indrev.module_jump_boost.tooltip\": \"Enerji karşılığında Zıplama Desteği efekti verir.\",\n  \"item.indrev.module_night_vision\": \"Gece Görüşü Modülü\",\n  \"item.indrev.module_night_vision.tooltip\": \"Enerji karşılığında Gece Görüşü efekti verir.\",\n  \"item.indrev.module_breathing\": \"Nefes Alma Modülü\",\n  \"item.indrev.module_breathing.tooltip\": \"Enerji karşılığında su altında Nefes Alma efekti verir.\",\n  \"item.indrev.module_auto_feeder\": \"Oto Besleyici Modülü\",\n  \"item.indrev.module_auto_feeder.tooltip\": \"Enerji karşılığında envanterinde otomatik yemek yemeni sağlar.\",\n  \"item.indrev.module_charger\": \"Şarj edici Modülü\",\n  \"item.indrev.module_charger.tooltip\": \"Envanterindeki herhangi bir eşyayı şarj eder (zırh içermez).\",\n  \"item.indrev.module_solar_panel\": \"Güneş Paneli Modülü\",\n  \"item.indrev.module_solar_panel.tooltip\": \"Günışığında zırhını ve elindeki eşyayı şarj eder.\",\n  \"item.indrev.module_piglin_tricker\": \"Piglin Kandırıcı Modülü\",\n  \"item.indrev.module_piglin_tricker.tooltip\": \"Piglinleri altın giyiyormuşsun diye kandırır.\",\n  \"item.indrev.module_fire_resistance\": \"Ateş Direnci Modülü\",\n  \"item.indrev.module_fire_resistance.tooltip\": \"Enerji karşılığında gerektiğinde Ateş Direnci efekti verir.\",\n  \"item.indrev.module_efficiency\": \"Verimlilik Modülü\",\n  \"item.indrev.module_efficiency.tooltip\": \"Kazı aletininin hızını arttırır.\",\n  \"item.indrev.module_range\": \"Alan Modülü\",\n  \"item.indrev.module_range.tooltip\": \"Matkabının bir kerede kurabileceği blok sayısını arttırır.\",\n  \"item.indrev.module_fortune\": \"Servet Modülü\",\n  \"item.indrev.module_fortune.tooltip\": \"Servet büyüsü gibi davranır.\",\n  \"item.indrev.module_silk_touch\": \"İpeksi Dokunuş Modülü\",\n  \"item.indrev.module_silk_touch.tooltip\": \"İpeksi Dokunuş büyüsü gibi davranır.\",\n  \"item.indrev.module_reach\": \"Yetişme Modülü\",\n  \"item.indrev.module_reach.tooltip\": \"Aynı anda birden fazla canavara hasar vurur.\",\n  \"item.indrev.module_looting\": \"Yağma Modülü\",\n  \"item.indrev.module_looting.tooltip\": \"Yağma büyüsü gibi davranır.\",\n  \"item.indrev.module_fire_aspect\": \"Ateşten Çehre Modülü\",\n  \"item.indrev.module_fire_aspect.tooltip\": \"Ateşten Çehre büyüsü gibi davranır.\",\n  \"item.indrev.module_sharpness\": \"Keskinlik Modülü\",\n  \"item.indrev.module_sharpness.tooltip\": \"Vurduğun hasarı arttırır.\",\n  \"item.indrev.module_color.tooltip\": \"§cR§ee§an§9k§6§dl§1e§2r\",\n  \"item.indrev.module_color_pink\": \"Pembe Renk Modülü\",\n  \"item.indrev.module_color_red\": \"Kırmızı Renk Modülü\",\n  \"item.indrev.module_color_purple\": \"Mor Renk Modülü\",\n  \"item.indrev.module_color_blue\": \"Mavi Renk Modülü\",\n  \"item.indrev.module_color_cyan\": \"Camgöbeği Renk Modülü\",\n  \"item.indrev.module_color_green\": \"Yeşil Renk Modülü\",\n  \"item.indrev.module_color_yellow\": \"Sarı Renk Modülü\",\n  \"item.indrev.module_color_orange\": \"Turuncu Renk Modülü\",\n  \"item.indrev.module_color_black\": \"Siyah Renk Modülü\",\n  \"item.indrev.module_color_brown\": \"Kahverengi Renk Modülü\",\n  \"item.indrev.copper_sword\": \"Bakır Kılıç\",\n  \"item.indrev.copper_pickaxe\": \"Bakır Kazma\",\n  \"item.indrev.copper_axe\": \"Bakır Balta\",\n  \"item.indrev.copper_shovel\": \"Bakır Kürek\",\n  \"item.indrev.copper_hoe\": \"Bakır Çapa\",\n  \"item.indrev.copper_helmet\": \"Bakır Kask\",\n  \"item.indrev.copper_chestplate\": \"Bakır Göğüslük\",\n  \"item.indrev.copper_leggings\": \"Bakır Pantolon\",\n  \"item.indrev.copper_boots\": \"Bakır Botlar\",\n  \"item.indrev.tin_sword\": \"Tin Kılıç\",\n  \"item.indrev.tin_pickaxe\": \"Tin Kazma\",\n  \"item.indrev.tin_axe\": \"Tin Balta\",\n  \"item.indrev.tin_shovel\": \"Tin Kürek\",\n  \"item.indrev.tin_hoe\": \"Tin Çapa\",\n  \"item.indrev.tin_helmet\": \"Tin Kask\",\n  \"item.indrev.tin_chestplate\": \"Tin Göğüslük\",\n  \"item.indrev.tin_leggings\": \"Tin Pantolon\",\n  \"item.indrev.tin_boots\": \"Tin Botlar\",\n  \"category.indrev\": \"Endüstriyel Devrim\",\n  \"key.indrev.modular\": \"Modüler eşya Yapılandırması\",\n  \"text.multiblock.not_built\": \"Yapı inşa edilmedi!\",\n  \"advancements.indrev.nikolite\": \"Başlangıç\",\n  \"advancements.indrev.nikolite.description\": \"Nikolit Bulun\",\n  \"advancements.indrev.machine_block\": \"Temeller\",\n  \"advancements.indrev.machine_block.description\": \"Bir Makine Bloğu elde edin\",\n  \"advancements.indrev.coal_generator\": \"İlk Adımlar\",\n  \"advancements.indrev.coal_generator.description\": \"Bir Kömür Jeneratörü elde edin\",\n  \"advancements.indrev.basic_solar_generator\": \"Pasif Enerji\",\n  \"advancements.indrev.basic_solar_generator.description\": \"Bir Basit Güneş Paneli üretin\",\n  \"advancements.indrev.advanced_solar_generator\": \"Daha çok Pasif Enerji\",\n  \"advancements.indrev.advanced_solar_generator.description\": \"Gelişmiş Güneş Jeneratörü elde edin\",\n  \"advancements.indrev.heat_generator\": \"Burası sıcak mı oldu ne\",\n  \"advancements.indrev.heat_generator.description\": \"Bir Isı Jeneratörü elde edin\",\n  \"advancements.indrev.biomass_generator\": \"Çok Çevre-Dostu değil\",\n  \"advancements.indrev.biomass_generator.description\": \"Bir Biyoyakıt Jeneratörü elde edin\",\n  \"advancements.indrev.pulverizer\": \"Burası tozlanıyor mu ne\",\n  \"advancements.indrev.pulverizer.description\": \"Bir Öğütücü elde edin\",\n  \"advancements.indrev.electric_furnace\": \"Daha iyi Fırın\",\n  \"advancements.indrev.electric_furnace.description\": \"Bir Elektrikli Fırın elde edin\",\n  \"advancements.indrev.compressor\": \"Parmaklarına Dikkat et\",\n  \"advancements.indrev.compressor.description\": \"Bir Kompresör elde edin\",\n  \"advancements.indrev.recycler\": \"Çevre-Dostu\",\n  \"advancements.indrev.recycler.description\": \"Bir Geri Dönüştürücü elde edin\",\n  \"advancements.indrev.solid_infuser\": \"İnfüzleniyor\",\n  \"advancements.indrev.solid_infuser.description\": \"Bir Katı İnfüzör elde edin\",\n  \"advancements.indrev.mk2_upgrade\": \"İki kat Verimlilik\",\n  \"advancements.indrev.mk2_upgrade.description\": \"Bir makineyi MK2'ye yükseltin\",\n  \"advancements.indrev.mk3_upgrade\": \"Üç kat Verimlilik\",\n  \"advancements.indrev.mk3_upgrade.description\": \"Bir makineyi MK3'e yükseltin\",\n  \"advancements.indrev.mk4_upgrade\": \"Fabrika. Büyü. Meli\",\n  \"advancements.indrev.mk4_upgrade.description\": \"Bir makineyi MK4'e yükseltin\",\n  \"advancements.indrev.biomass\": \"İğrenç görünüyor...\",\n  \"advancements.indrev.biomass.description\": \"Biyoyakıt elde edin\",\n  \"advancements.indrev.nikolite_ingot\": \"Yeni Ufuklar\",\n  \"advancements.indrev.nikolite_ingot.description\": \"Nikolit Külçesi elde edin\",\n  \"advancements.indrev.enriched_nikolite_dust\": \"Güçlü Elektronikler\",\n  \"advancements.indrev.enriched_nikolite_dust.description\": \"Zenginleştirilmiş Nikolit Tozu elde edin\",\n  \"advancements.indrev.enriched_nikolite_ingot\": \"Daha. Fazla. Güç.\",\n  \"advancements.indrev.enriched_nikolite_ingot.description\": \"Zenginleştirilmiş Nikolit Kulcesi elde edin\",\n  \"advancements.indrev.lazuli_flux_container_mk1\": \"Basit Enerji Deposu\",\n  \"advancements.indrev.lazuli_flux_container_mk1.description\": \"Get a Lazuli Flux Container MK1\",\n  \"advancements.indrev.lazuli_flux_container_mk2\": \"Ortalama Enerji Deposu\",\n  \"advancements.indrev.lazuli_flux_container_mk2.description\": \"Get a Lazuli Flux Container MK2\",\n  \"advancements.indrev.lazuli_flux_container_mk3\": \"Çok ortalama olmayan Enerji Deposu\",\n  \"advancements.indrev.lazuli_flux_container_mk3.description\": \"Get a Lazuli Flux Container MK3\",\n  \"advancements.indrev.lazuli_flux_container_mk4\": \"Nihai Enerji Deposu\",\n  \"advancements.indrev.lazuli_flux_container_mk4.description\": \"Get a Lazuli Flux Container MK4\",\n  \"advancements.indrev.empty_enhancer\": \"Güzel bir Tabak\",\n  \"advancements.indrev.empty_enhancer.description\": \"Bir Boş Güçlendirici elde edin\",\n  \"advancements.indrev.speed_enhancer\": \"Makine goes Brrrr\",\n  \"advancements.indrev.speed_enhancer.description\": \"Bir hız Güçlendiricisi elde edin\",\n  \"advancements.indrev.buffer_enhancer\": \"Sınırların Ötesinde\",\n  \"advancements.indrev.buffer_enhancer.description\": \"Bir Arabellek Güçlendiricisi elde edin\",\n  \"advancements.indrev.energy_enhancer\": \"Her zaman Verimli\",\n  \"advancements.indrev.energy_enhancer.description\": \"Bir Enerji Güçlendiricisi elde edin\",\n  \"advancements.indrev.cable_mk1\": \"Sarı Kablolar!\",\n  \"advancements.indrev.cable_mk1.description\": \"Bir Kablo MK1 elde edin\",\n  \"advancements.indrev.cable_mk2\": \"Mavi Kablolar!\",\n  \"advancements.indrev.cable_mk2.description\": \"Bir Kablo MK1 elde edin\",\n  \"advancements.indrev.cable_mk3\": \"Pembe Kablolar!\",\n  \"advancements.indrev.cable_mk3.description\": \"Bir Kablo MK3 elde edin\",\n  \"advancements.indrev.cable_mk4\": \"Kırmızı Kablolar!\",\n  \"advancements.indrev.cable_mk4.description\": \"Bir Kablo MK4 elde edin\",\n  \"advancements.indrev.modular_workbench\": \"Bir Çalışma Masasına benzemiyor\",\n  \"advancements.indrev.modular_workbench.description\": \"Bir Modüler ÇalışmaMasası elde et\",\n  \"advancements.indrev.modular_armor\": \"Modülerlik\",\n  \"advancements.indrev.modular_armor.description\": \"Modüler Zırhını tamamla\",\n  \"advancements.indrev.gamer_axe\": \"Yeterli RGB yok\",\n  \"advancements.indrev.gamer_axe.description\": \"Oyuncu Baltası'nı elde et\",\n  \"advancements.indrev.chopper_mk4\": \"Oto Odun Farmı\",\n  \"advancements.indrev.chopper_mk4.description\": \"Oduncu elde edin\",\n  \"advancements.indrev.rancher_mk1\": \"Ben, Robot Besici\",\n  \"advancements.indrev.rancher_mk1.description\": \"Bir Besici elde et\",\n  \"advancements.indrev.rancher_mk4\": \"Mekanize Üretim\",\n  \"advancements.indrev.rancher_mk4.description\": \"Bir Besici elde et\",\n  \"advancements.indrev.industrial_smelter\": \"Geliştirilmiş Cevher İşlemesi\",\n  \"advancements.indrev.industrial_smelter.description\": \"Bir Endüstriyel Pişirici üret\",\n  \"advancements.indrev.condenser\": \"Aklımda fikir kalmadı\",\n  \"advancements.indrev.condenser.description\": \"Bir Yoğunlaştırıcı elde et\",\n  \"advancements.indrev.blast_furnace_enhancer\": \"Maden Elektrikli Fırını\",\n  \"advancements.indrev.blast_furnace_enhancer.description\": \"Maden Pişirici arttırmasını elde et\",\n  \"advancements.indrev.smoker_enhancer\": \"Bir şefin rüyası\",\n  \"advancements.indrev.smoker_enhancer.description\": \"Dumanlı Pişirici arttırmasını elde et\",\n  \"advancements.indrev.circuit_mk1\": \"İlk Makineler\",\n  \"advancements.indrev.circuit_mk1.description\": \"Bir MK1 Devresi elde et\",\n  \"advancements.indrev.circuit_mk2\": \"Yeni Teknoloji\",\n  \"advancements.indrev.circuit_mk2.description\": \"Bir MK2 Devresi elde et\",\n  \"advancements.indrev.circuit_mk3\": \"Gelişmiş Teknoloji\",\n  \"advancements.indrev.circuit_mk3.description\": \"Bir MK3 Devresi elde et\",\n  \"advancements.indrev.circuit_mk4\": \"Nihai Teknoloji\",\n  \"advancements.indrev.circuit_mk4.description\": \"Bir MK4 Devresi elde et\",\n  \"advancements.indrev.farmer_mk1\": \"Multi Küçük Çiftlik\",\n  \"advancements.indrev.farmer_mk1.description\": \"Bir MK1 Çiftçi elde et\",\n  \"advancements.indrev.slaughter_mk1\": \"Nihai Canavar öldürücüsü\",\n  \"advancements.indrev.slaughter_mk1.description\": \"Bir MK1 Kırımcı elde et\",\n  \"gui.indrev.tip\": \"İPUCU \",\n  \"gui.indrev.tip_0\": \"Sıvı ve eşya girişleri ve çıkışlarını ayarlamayı unutmayın\",\n  \"gui.indrev.tip_1\": \"Giriş limitinin üzerinde kablo kullanmayın\",\n  \"gui.indrev.tip_2\": \"Havalı adamlar gibi soğutucu kullanın\",\n  \"gui.indrev.tip_3\": \"Verimliliği kaybetmemek için soğutucu kullanın\",\n  \"gui.indrev.tip_4\": \"Fabrika. Büyü. Meli.\",\n  \"gui.indrev.tip_5\": \"Modüler Zırha bi göz atmalısın\",\n  \"gui.indrev.tip_6\": \"Gerçek oyuncular Oyuncu Baltası kullanır\",\n  \"gui.indrev.tip_7\": \"Eğer sorununuz ya da önerileriniz varsa Discord'umuza katılın!\",\n  \"gui.indrev.tip_8\": \"Güncellemeden önce her zaman dünyalarınızı yedekleyin\",\n  \"gui.indrev.tip_9\": \"Tırnaklarınızın üstünü gece kapatın\",\n  \"gui.indrev.modules_installed\": \"Modüller Kurulu: \",\n  \"gui.indrev.shield\": \"Kalkan: \",\n  \"gui.indrev.installing\": \"Kuruluyor\",\n  \"gui.indrev.incompatible\": \"Kurulamaz\",\n  \"gui.indrev.max_level\": \"En yüksek seviyeye ulaşıldı\",\n  \"gui.indrev.progress\": \"İlerleme: \",\n  \"gui.indrev.whitelist.true\": \"Beyaz Liste\",\n  \"gui.indrev.whitelist.false\": \"Kara liste\",\n  \"gui.indrev.matchDurability.true\": \"Eşya hasarını eşle\",\n  \"gui.indrev.matchDurability.false\": \"Eşya hasarını görmezden gel\",\n  \"gui.indrev.matchTag.true\": \"Eşya NBT'sini eşle\",\n  \"gui.indrev.matchTag.false\": \"Eşya NBT'sini görmezden gel\",\n  \"death.attack.acid\": \"%s Sülfürik Asit'te çözüldü\",\n  \"death.attack.acid.player\": \"%s %s kişisinden kaçarken Sülfürik Asit'te çözüldü\",\n  \"death.attack.laser\": \"%s lazere doğru koştu ve kızardı.\",\n  \"death.attack.laser.player\": \"%s %s kişisinden kaçarken lazere doğru koştu ve kızardı\",\n  \"vein.indrev.bauxite\": \"Bauksit\",\n  \"vein.indrev.certus_quartz\": \"Sert Kuvars\",\n  \"vein.indrev.peat\": \"Peat\",\n  \"vein.indrev.lignite\": \"Lignit\",\n  \"vein.indrev.bituminous\": \"Bituminous\",\n  \"vein.indrev.anthracite\": \"Antrasit\",\n  \"vein.indrev.cuprite\": \"Kuprot\",\n  \"vein.indrev.calaverite\": \"Kalaverit\",\n  \"vein.indrev.calaverite_nether\": \"Kalaverit (Nether)\",\n  \"vein.indrev.iridium\": \"İridyum\",\n  \"vein.indrev.siderite\": \"Siderit\",\n  \"vein.indrev.limonite\": \"Limonit\",\n  \"vein.indrev.hematite\": \"Hematit\",\n  \"vein.indrev.magnetite\": \"Magnetit\",\n  \"vein.indrev.chalcopryte\": \"Kalkoprit\",\n  \"vein.indrev.nikolite\": \"Nikolit\",\n  \"vein.indrev.quartz\": \"Kuvars\",\n  \"vein.indrev.argentite\": \"Argentit\",\n  \"vein.indrev.chlorargyrite\": \"Klorargirit\",\n  \"vein.indrev.cassiterite\": \"Kasiterit\",\n  \"vein.indrev.stannite\": \"Stannit\",\n  \"vein.indrev.scheelite\": \"Şelit\",\n  \"vein.indrev.wolframite\": \"Wolframit\",\n  \"vein.indrev.ferberite\": \"Ferberit\",\n  \"vein.indrev.galena\": \"Galena\",\n  \"vein.indrev.glowstonedeposit\": \"Işıktaşı Yatağı\",\n  \"vein.indrev.soul_nether\": \"Nether ruhu Yatağı\",\n  \"vein.indrev.sulfur_nether\": \"Sülfür\",\n  \"vein.indrev.denseice\": \"Yoğun Buz Yatağı\",\n  \"attribute.indrev.shield\": \"Kalkan\",\n  \"subtitles.indrev.laser\": \"Lazer Işını\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/lang/zh_cn.json",
    "content": "{\n  \"item.patchouli.industrial_revolution_book.name\": \"工业革命手册\",\n  \"item.indrev.guide_book\": \"工业革命手册\",\n  \"item.patchouli.industrial_revolution_book.landing\": \"这本书将带你了解本模组的一些重要内容$(br2)若发现漏洞，有什么问题或建议，请在我们的$(l:https://github.com/GabrielOlvH/Industrial-Revolution/issues)GitHub$()上提出问题或加入我们的$(l:https://discord.com/invite/G4PjhEf)Discord！$()$(br2)感谢游玩！\",\n  \"itemGroup.indrev.indrev_group\": \"工业革命\",\n  \"block.machines.tooltip.io\": \"I/O：%s\",\n  \"block.indrev.lazuli_flux_container_1\": \"青金石通量\",\n  \"block.indrev.lazuli_flux_container_2\": \"容器\",\n  \"block.indrev.coal_generator\": \"煤炭发电机\",\n  \"block.indrev.coal_generator_mk1\": \"煤炭发电机\",\n  \"block.indrev.heat_generator\": \"热力发电机\",\n  \"block.indrev.heat_generator_mk4\": \"热力发电机\",\n  \"block.indrev.electric_furnace\": \"电炉\",\n  \"block.indrev.electric_furnace_mk1\": \"电炉MK1\",\n  \"block.indrev.electric_furnace_mk2\": \"电炉MK2\",\n  \"block.indrev.electric_furnace_mk3\": \"电炉MK3\",\n  \"block.indrev.electric_furnace_mk4\": \"电炉MK4\",\n  \"block.indrev.electric_furnace_creative\": \"创造模式电炉\",\n  \"block.indrev.electric_furnace_factory\": \"电炉工厂\",\n  \"block.indrev.electric_furnace_factory_mk4\": \"电炉工厂MK4\",\n  \"block.indrev.smelter\": \"工业熔炉\",\n  \"block.indrev.smelter_mk4\": \"工业熔炉\",\n  \"block.indrev.condenser\": \"聚合机\",\n  \"block.indrev.condenser_mk4\": \"聚合机\",\n  \"block.indrev.pulverizer\": \"粉碎机\",\n  \"block.indrev.pulverizer_mk1\": \"粉碎机MK1\",\n  \"block.indrev.pulverizer_mk2\": \"粉碎机MK2\",\n  \"block.indrev.pulverizer_mk3\": \"粉碎机MK3\",\n  \"block.indrev.pulverizer_mk4\": \"粉碎机MK4\",\n  \"block.indrev.pulverizer_creative\": \"创造模式粉碎机\",\n  \"block.indrev.pulverizer_factory\": \"粉碎工厂\",\n  \"block.indrev.pulverizer_factory_mk4\": \"粉碎工厂MK4\",\n  \"block.indrev.sawmill\": \"锯木机\",\n  \"block.indrev.sawmill_mk1\": \"锯木机MK1\",\n  \"block.indrev.sawmill_mk2\": \"锯木机MK2\",\n  \"block.indrev.sawmill_mk3\": \"锯木机MK3\",\n  \"block.indrev.sawmill_mk4\": \"锯木机MK4\",\n  \"block.indrev.sawmill_creative\": \"创造模式锯木机\",\n  \"block.indrev.compressor\": \"压缩机\",\n  \"block.indrev.compressor_mk1\": \"压缩机MK1\",\n  \"block.indrev.compressor_mk2\": \"压缩机MK2\",\n  \"block.indrev.compressor_mk3\": \"压缩机MK3\",\n  \"block.indrev.compressor_mk4\": \"压缩机MK4\",\n  \"block.indrev.compressor_creative\": \"创造模式压缩机\",\n  \"block.indrev.compressor_factory\": \"压缩工厂\",\n  \"block.indrev.compressor_factory_mk4\": \"压缩工厂MK4\",\n  \"block.indrev.solid_infuser_factory\": \"固体注入工厂\",\n  \"block.indrev.solid_infuser\": \"固体注入机\",\n  \"block.indrev.solid_infuser_mk1\": \"固体注入机MK1\",\n  \"block.indrev.solid_infuser_mk2\": \"固体注入机MK2\",\n  \"block.indrev.solid_infuser_mk3\": \"固体注入机MK3\",\n  \"block.indrev.solid_infuser_mk4\": \"固体注入机MK4\",\n  \"block.indrev.solid_infuser_creative\": \"创造模式固体注入机\",\n  \"block.indrev.solid_infuser_factory_mk4\": \"固体注入工厂MK4\",\n  \"block.indrev.lazuli_flux_container_mk1\": \"青金石通量容器MK1\",\n  \"block.indrev.lazuli_flux_container_mk2\": \"青金石通量容器MK2\",\n  \"block.indrev.lazuli_flux_container_mk3\": \"青金石通量容器MK3\",\n  \"block.indrev.lazuli_flux_container_mk4\": \"青金石通量容器MK4\",\n  \"block.indrev.lazuli_flux_container_creative\": \"创造模式青金石通量容器\",\n  \"block.indrev.cable_mk1\": \"线缆MK1\",\n  \"block.indrev.cable_mk2\": \"线缆MK2\",\n  \"block.indrev.cable_mk3\": \"线缆MK3\",\n  \"block.indrev.cable_mk4\": \"线缆MK4\",\n  \"block.indrev.fluid_pipe_mk1\": \"流体管道MK1\",\n  \"block.indrev.fluid_pipe_mk2\": \"流体管道MK2\",\n  \"block.indrev.fluid_pipe_mk3\": \"流体管道MK3\",\n  \"block.indrev.fluid_pipe_mk4\": \"流体管道MK4\",\n  \"block.indrev.item_pipe_mk1\": \"物品管道MK1\",\n  \"block.indrev.item_pipe_mk2\": \"物品管道MK2\",\n  \"block.indrev.item_pipe_mk3\": \"物品管道MK3\",\n  \"block.indrev.item_pipe_mk4\": \"物品管道MK4\",\n  \"block.indrev.fisher\": \"钓鱼机\",\n  \"block.indrev.fisher_mk2\": \"基础钓鱼机\",\n  \"block.indrev.fisher_mk3\": \"改进钓鱼机\",\n  \"block.indrev.fisher_mk4\": \"高级钓鱼机\",\n  \"block.indrev.mining_rig\": \"钻机计算机\",\n  \"block.indrev.mining_rig_mk4\": \"钻机计算机\",\n  \"block.indrev.mining_rig.tooltip\": \"在当前区块执行开采工作前，须首先执行扫描程序\",\n  \"block.indrev.mining_rig.mined\": \"已挖掘%s\",\n  \"block.indrev.drill\": \"钻机\",\n  \"block.indrev.drill_bottom\": \"钻机\",\n  \"block.indrev.drill_middle\": \"钻机\",\n  \"block.indrev.drill_top\": \"钻机\",\n  \"block.indrev.drill.faster\": \"%s倍速\",\n  \"block.indrev.drill.no_drills\": \"空\",\n  \"block.indrev.drill.wrong_location\": \"扫描报告汇报的地点不正确！\",\n  \"block.indrev.drill.active\": \"钻头激活\",\n  \"block.indrev.drill.activating\": \"未就绪\",\n  \"block.indrev.drill.not_enough_power\": \"能量不足\",\n  \"block.indrev.drill.power_required\": \"正常运作至少需要%sLF\",\n  \"block.indrev.recycler\": \"回收机\",\n  \"block.indrev.recycler_mk2\": \"回收机\",\n  \"block.indrev.cable\": \"线缆\",\n  \"block.indrev.farmer_mk1\": \"耕作机MK1\",\n  \"block.indrev.farmer_mk2\": \"耕作机MK2\",\n  \"block.indrev.farmer_mk3\": \"耕作机MK3\",\n  \"block.indrev.farmer_mk4\": \"耕作机MK4\",\n  \"block.indrev.farmer_creative\": \"创造模式耕作机\",\n  \"block.indrev.farmer\": \"耕作机\",\n  \"block.indrev.slaughter_mk1\": \"屠宰机MK1\",\n  \"block.indrev.slaughter_mk2\": \"屠宰机MK2\",\n  \"block.indrev.slaughter_mk3\": \"屠宰机MK3\",\n  \"block.indrev.slaughter_mk4\": \"屠宰机MK4\",\n  \"block.indrev.slaughter_creative\": \"创造模式屠宰机\",\n  \"block.indrev.slaughter\": \"屠宰机\",\n  \"block.indrev.chopper\": \"伐木机\",\n  \"block.indrev.chopper_mk1\": \"伐木机MK1\",\n  \"block.indrev.chopper_mk2\": \"伐木机MK2\",\n  \"block.indrev.chopper_mk3\": \"伐木机MK3\",\n  \"block.indrev.chopper_mk4\": \"伐木机MK4\",\n  \"block.indrev.chopper_creative\": \"创造模式伐木机\",\n  \"block.indrev.rancher\": \"放牧机\",\n  \"block.indrev.rancher_mk1\": \"放牧机MK1\",\n  \"block.indrev.rancher_mk2\": \"放牧机MK2\",\n  \"block.indrev.rancher_mk3\": \"放牧机MK3\",\n  \"block.indrev.rancher_mk4\": \"放牧机MK4\",\n  \"block.indrev.rancher_creative\": \"创造模式放牧机\",\n  \"block.indrev.fluid_infuser\": \"液体注入机\",\n  \"block.indrev.fluid_infuser_mk1\": \"液体注入机MK1\",\n  \"block.indrev.fluid_infuser_mk2\": \"液体注入机MK2\",\n  \"block.indrev.fluid_infuser_mk3\": \"液体注入机MK3\",\n  \"block.indrev.fluid_infuser_mk4\": \"液体注入机MK4\",\n  \"block.indrev.fluid_infuser_creative\": \"创造模式液体注入机\",\n  \"block.indrev.electrolytic_separator\": \"电解分离机\",\n  \"block.indrev.electrolytic_separator_mk1\": \"电解分离机MK1\",\n  \"block.indrev.electrolytic_separator_mk2\": \"电解分离机MK2\",\n  \"block.indrev.electrolytic_separator_mk3\": \"电解分离机MK3\",\n  \"block.indrev.electrolytic_separator_mk4\": \"电解分离机MK4\",\n  \"block.indrev.electrolytic_separator_creative\": \"创造模式电解分离机\",\n  \"block.indrev.drain_mk1\": \"排液口\",\n  \"block.indrev.tank\": \"储罐\",\n  \"block.indrev.factory\": \"工厂\",\n  \"block.indrev.biomass_generator\": \"生物质发电机\",\n  \"block.indrev.biomass_generator_mk3\": \"生物质发电机\",\n  \"block.indrev.solar_generator\": \"太阳能发电机\",\n  \"block.indrev.solar_generator_mk1\": \"太阳能发电机\",\n  \"block.indrev.solar_generator_mk3\": \"太阳能发电机\",\n  \"block.indrev.charge_pad_mk4\": \"充电台\",\n  \"block.indrev.charge_pad_mk4.tooltip\": \"为你放上去的东西充电！\",\n  \"block.indrev.aoe.range\": \"作用范围：%s\",\n  \"block.indrev.aoe.toggle.false\": \"显示作用范围\",\n  \"block.indrev.aoe.toggle.true\": \"隐藏作用范围\",\n  \"block.indrev.wither_proof_obsidian\": \"防凋灵黑曜石\",\n  \"block.indrev.planks\": \"板材\",\n  \"block.indrev.plank_block\": \"板材方块\",\n  \"block.indrev.controller\": \"控制器\",\n  \"block.indrev.frame\": \"机器框架\",\n  \"block.indrev.duct\": \"通风管道\",\n  \"block.indrev.warning_strobe\": \"警示灯\",\n  \"block.indrev.silo\": \"筒仓\",\n  \"block.indrev.intake\": \"进风口\",\n  \"block.indrev.cabinet\": \"柜子\",\n  \"block.indrev.coolant\": \"制冷液\",\n  \"block.indrev.molten_iron\": \"熔融铁\",\n  \"block.indrev.molten_gold\": \"熔融金\",\n  \"block.indrev.molten_copper\": \"熔融铜\",\n  \"block.indrev.molten_tin\": \"熔融锡\",\n  \"block.indrev.molten_netherite\": \"熔融下界合金\",\n  \"block.indrev.hydrogen\": \"液氢\",\n  \"item.indrev.hydrogen_bucket\": \"液氢桶\",\n  \"block.indrev.oxygen\": \"液氧\",\n  \"item.indrev.oxygen_bucket\": \"液氧桶\",\n  \"block.indrev.methane\": \"甲烷\",\n  \"item.indrev.methane_bucket\": \"甲烷桶\",\n  \"block.indrev.sulfuric_acid\": \"硫酸\",\n  \"item.indrev.sulfuric_acid_bucket\": \"硫酸桶\",\n  \"block.indrev.toxic_mud\": \"毒浆\",\n  \"item.indrev.toxic_mud_bucket\": \"毒浆桶\",\n  \"block.indrev.machine_block\": \"机器方块\",\n  \"item.indrev.modular_core\": \"模块化核心（未充能）\",\n  \"item.indrev.modular_core_activated\": \"模块化核心\",\n  \"item.indrev.sawdust\": \"锯末\",\n  \"block.indrev.laser_emitter_mk4\": \"激光发射器\",\n  \"block.indrev.capsule\": \"微仓\",\n  \"block.indrev.tin_ore\": \"锡矿石\",\n  \"block.indrev.tin_block\": \"锡块\",\n  \"item.indrev.tin_ingot\": \"锡锭\",\n  \"item.indrev.tin_chunk\": \"锡碎块\",\n  \"item.indrev.tin_dust\": \"锡粉\",\n  \"item.indrev.tin_plate\": \"锡板\",\n  \"item.indrev.tin_gear\": \"锡齿轮\",\n  \"item.indrev.tin_nugget\": \"锡粒\",\n  \"item.indrev.molten_tin_bucket\": \"熔融锡桶\",\n  \"item.indrev.tin_purified_ore\": \"精炼锡矿石\",\n  \"item.indrev.copper_chunk\": \"铜碎块\",\n  \"item.indrev.copper_dust\": \"铜粉\",\n  \"item.indrev.copper_plate\": \"铜板\",\n  \"item.indrev.copper_gear\": \"铜齿轮\",\n  \"item.indrev.copper_nugget\": \"铜粒\",\n  \"item.indrev.copper_purified_ore\": \"精炼铜矿石\",\n  \"item.indrev.molten_copper_bucket\": \"熔融铜桶\",\n  \"block.indrev.molten_lead\": \"熔融铅\",\n  \"item.indrev.molten_lead_bucket\": \"熔融铅桶\",\n  \"block.indrev.lead_ore\": \"铅矿石\",\n  \"block.indrev.lead_block\": \"铅块\",\n  \"item.indrev.lead_ingot\": \"铅锭\",\n  \"item.indrev.lead_chunk\": \"铅碎块\",\n  \"item.indrev.lead_dust\": \"铅粉\",\n  \"item.indrev.lead_plate\": \"铅板\",\n  \"item.indrev.lead_gear\": \"铅齿轮\",\n  \"item.indrev.lead_nugget\": \"铅粒\",\n  \"item.indrev.lead_purified_ore\": \"精炼铅矿石\",\n  \"item.indrev.raw_lead\": \"粗铅矿\",\n  \"item.indrev.raw_silver\": \"粗银矿\",\n  \"item.indrev.raw_tungsten\": \"粗钨矿\",\n  \"item.indrev.raw_tin\": \"粗锡矿\",\n  \"block.indrev.raw_lead_block\": \"粗铅块\",\n  \"block.indrev.raw_silver_block\": \"粗银块\",\n  \"block.indrev.raw_tungsten_block\": \"粗钨块\",\n  \"block.indrev.raw_tin_block\": \"粗锡块\",\n  \"block.indrev.molten_silver\": \"熔融银\",\n  \"item.indrev.molten_silver_bucket\": \"熔融银桶\",\n  \"block.indrev.silver_ore\": \"银矿石\",\n  \"block.indrev.silver_block\": \"银块\",\n  \"item.indrev.silver_ingot\": \"银锭\",\n  \"item.indrev.silver_chunk\": \"银碎块\",\n  \"item.indrev.silver_dust\": \"银粉\",\n  \"item.indrev.silver_plate\": \"银板\",\n  \"item.indrev.silver_gear\": \"银齿轮\",\n  \"item.indrev.silver_nugget\": \"银粒\",\n  \"item.indrev.silver_purified_ore\": \"精炼银矿石\",\n  \"block.indrev.molten_tungsten\": \"熔融钨\",\n  \"item.indrev.molten_tungsten_bucket\": \"熔融钨桶\",\n  \"block.indrev.tungsten_ore\": \"钨矿石\",\n  \"block.indrev.tungsten_block\": \"钨块\",\n  \"item.indrev.tungsten_ingot\": \"钨锭\",\n  \"item.indrev.tungsten_chunk\": \"钨碎块\",\n  \"item.indrev.tungsten_dust\": \"钨粉\",\n  \"item.indrev.tungsten_plate\": \"钨板\",\n  \"item.indrev.tungsten_gear\": \"钨齿轮\",\n  \"item.indrev.tungsten_nugget\": \"钨粒\",\n  \"item.indrev.tungsten_purified_ore\": \"精炼钨矿石\",\n  \"block.indrev.deepslate_tungsten_ore\": \"深层钨矿石\",\n  \"block.indrev.deepslate_tin_ore\": \"深层锡矿石\",\n  \"block.indrev.deepslate_silver_ore\": \"深层银矿石\",\n  \"block.indrev.deepslate_nikolite_ore\": \"深层蓝石矿石\",\n  \"block.indrev.deepslate_lead_ore\": \"深层铅矿石\",\n  \"block.indrev.electrum_block\": \"琥珀金块\",\n  \"item.indrev.electrum_ingot\": \"琥珀金锭\",\n  \"item.indrev.electrum_dust\": \"琥珀金粉\",\n  \"item.indrev.electrum_plate\": \"琥珀金板\",\n  \"item.indrev.electrum_gear\": \"琥珀金齿轮\",\n  \"item.indrev.electrum_nugget\": \"琥珀金粒\",\n  \"block.indrev.bronze_block\": \"青铜块\",\n  \"item.indrev.bronze_ingot\": \"青铜锭\",\n  \"item.indrev.bronze_chunk\": \"青铜碎块\",\n  \"item.indrev.bronze_dust\": \"青铜粉\",\n  \"item.indrev.bronze_plate\": \"青铜板\",\n  \"item.indrev.bronze_gear\": \"青铜齿轮\",\n  \"item.indrev.bronze_nugget\": \"青铜粒\",\n  \"block.indrev.sulfur_crystal\": \"硫晶体\",\n  \"item.indrev.sulfur_crystal\": \"硫晶体\",\n  \"item.indrev.sulfur_dust\": \"硫粉\",\n  \"block.indrev.steel_block\": \"钢块\",\n  \"item.indrev.steel_dust\": \"钢粉\",\n  \"item.indrev.steel_ingot\": \"钢锭\",\n  \"item.indrev.steel_plate\": \"钢板\",\n  \"item.indrev.steel_gear\": \"钢齿轮\",\n  \"item.indrev.steel_nugget\": \"钢粒\",\n  \"item.indrev.gold_dust\": \"金粉\",\n  \"item.indrev.gold_plate\": \"金板\",\n  \"item.indrev.gold_chunk\": \"金碎块\",\n  \"item.indrev.gold_purified_ore\": \"精炼金矿石\",\n  \"item.indrev.molten_gold_bucket\": \"熔融金桶\",\n  \"item.indrev.iron_dust\": \"铁粉\",\n  \"item.indrev.iron_plate\": \"铁板\",\n  \"item.indrev.iron_chunk\": \"铁碎块\",\n  \"item.indrev.molten_iron_bucket\": \"熔融铁桶\",\n  \"item.indrev.iron_purified_ore\": \"精炼铁矿石\",\n  \"item.indrev.diamond_dust\": \"钻石粉\",\n  \"item.indrev.coal_dust\": \"煤粉\",\n  \"block.indrev.pump_mk1\": \"液泵\",\n  \"item.indrev.empty_enhancer\": \"空白增益模块\",\n  \"item.indrev.buffer_enhancer\": \"缓存增益\",\n  \"item.indrev.buffer_enhancer.tooltip\": \"增加机器内置缓存\",\n  \"item.indrev.energy_enhancer\": \"能效增益\",\n  \"item.indrev.energy_enhancer.tooltip\": \"降低机器能耗\",\n  \"item.indrev.damage_enhancer\": \"伤害增益\",\n  \"item.indrev.damage_enhancer.tooltip\": \"增加屠宰机造成的伤害\",\n  \"item.indrev.speed_enhancer\": \"速率增益\",\n  \"item.indrev.speed_enhancer.tooltip\": \"使机器更高效运转\",\n  \"item.indrev.blast_furnace_enhancer\": \"高炉增益\",\n  \"item.indrev.blast_furnace_enhancer.tooltip\": \"允许电炉处理高炉配方\",\n  \"item.indrev.smoker_enhancer\": \"烟熏增益\",\n  \"item.indrev.smoker_enhancer.tooltip\": \"允许电炉处理烟熏炉配方\",\n  \"item.indrev.enhancers.incompatible\": \"这台机器不支持这一增益\",\n  \"item.indrev.enhancers.count\": \"机器设置上限为%s每槽位\",\n  \"item.indrev.rechargeable.tooltip\": \"可充电\",\n  \"item.indrev.chunk_scanner\": \"区块扫描仪\",\n  \"item.indrev.chunk_scanner.scanning\": \"扫描中…\",\n  \"item.indrev.chunk_scanner.scanned1\": \"扫描完成！\",\n  \"item.indrev.chunk_scanner.scanned2\": \"矿脉类型：%s\",\n  \"item.indrev.chunk_scanner.already_scanned\": \"该区块已有扫描记录，一共有%s个矿脉\",\n  \"item.indrev.chunk_scanner.tooltip1\": \"按住右键开始扫描区块\",\n  \"item.indrev.chunk_scanner.tooltip2\": \"矿脉类型：%s\",\n  \"item.indrev.chunk_scanner.tooltip3\": \"从%s到%s\",\n  \"item.indrev.chunk_scanner.tooltip4\": \"维度：%s\",\n  \"item.indrev.chunk_scanner.tooltip5\": \"右击阅读更多\",\n  \"item.indrev.fan\": \"风扇\",\n  \"item.indrev.cooler_cell\": \"冷却单元\",\n  \"item.indrev.heatsink\": \"散热片\",\n  \"item.indrev.heat_coil\": \"加热线圈\",\n  \"item.indrev.heat_coil.tooltip\": \"将青金石通量转化为热能\",\n  \"item.indrev.stone_drill_head\": \"石头钻机钻头\",\n  \"item.indrev.iron_drill_head\": \"铁钻机钻头\",\n  \"item.indrev.diamond_drill_head\": \"钻石钻机钻头\",\n  \"item.indrev.netherite_drill_head\": \"下界合金钻机钻头\",\n  \"item.indrev.mining_drill_mk1\": \"采矿钻机MK1\",\n  \"item.indrev.mining_drill_mk2\": \"采矿钻机MK2\",\n  \"item.indrev.mining_drill_mk3\": \"采矿钻机MK3\",\n  \"item.indrev.mining_drill_mk4\": \"采矿钻机MK4\",\n  \"item.indrev.energy_reader\": \"能量示数器\",\n  \"item.indrev.energy_reader.use\": \"已存储能量：\",\n  \"item.indrev.energy_reader.use1\": \"%s LF/刻\",\n  \"item.indrev.enriched_nikolite_dust\": \"富集蓝石粉\",\n  \"item.indrev.enriched_nikolite_ingot\": \"富集蓝石锭\",\n  \"item.indrev.battery\": \"电池\",\n  \"item.indrev.circuit_mk1\": \"电路MK1\",\n  \"item.indrev.circuit_mk2\": \"电路MK2\",\n  \"item.indrev.circuit_mk3\": \"电路MK3\",\n  \"item.indrev.circuit_mk4\": \"电路MK4\",\n  \"item.indrev.tier_upgrade_mk2\": \"MK2机器升级\",\n  \"item.indrev.tier_upgrade_mk2.tooltip\": \"升级MK1机器至MK2\",\n  \"item.indrev.tier_upgrade_mk3\": \"MK3机器升级\",\n  \"item.indrev.tier_upgrade_mk3.tooltip\": \"升级MK2机器至MK3\",\n  \"item.indrev.tier_upgrade_mk4\": \"MK4机器升级\",\n  \"item.indrev.tier_upgrade_mk4.tooltip\": \"升级MK3机器至MK4\",\n  \"item.indrev.servo_output\": \"推送机构\",\n  \"item.indrev.servo_output.tooltip\": \"检索容器后提取，并推送至管道\",\n  \"item.indrev.servo_retriever\": \"收取机构\",\n  \"item.indrev.servo_retriever.tooltip\": \"检索管道后收取，并向容器输送\",\n  \"item.indrev.servo.mode\": \"检索优先级：\",\n  \"item.indrev.servo.mode.round_robin\": \"短板优先（轮询）\",\n  \"item.indrev.servo.mode.round_robin.tooltip\": \"优先考虑物品/液体存量最少的容器\",\n  \"item.indrev.servo.mode.nearest_first\": \"近处优先\",\n  \"item.indrev.servo.mode.nearest_first.tooltip\": \"优先考虑近端的容器\",\n  \"item.indrev.servo.mode.furthest_first\": \"远处优先\",\n  \"item.indrev.servo.mode.furthest_first.tooltip\": \"优先考虑远端的容器\",\n  \"item.indrev.servo.mode.random\": \"随机\",\n  \"item.indrev.servo.mode.random.tooltip\": \"无优先级\",\n  \"item.indrev.biomass\": \"生物质\",\n  \"item.indrev.untanned_leather\": \"生皮革\",\n  \"item.indrev.coolant_bucket\": \"制冷液桶\",\n  \"item.indrev.molten_netherite_bucket\": \"熔融下界合金桶\",\n  \"item.indrev.netherite_scrap_purified_ore\": \"精炼远古残骸\",\n  \"item.indrev.netherite_scrap_chunk\": \"远古残骸碎块\",\n  \"item.indrev.netherite_scrap_dust\": \"下界合金碎片粉\",\n  \"item.indrev.hammer\": \"锤子\",\n  \"item.indrev.screwdriver\": \"螺丝刀\",\n  \"item.indrev.wrench\": \"扳手\",\n  \"item.indrev.wrench.switch_mode\": \"扳手模式：%s\",\n  \"item.indrev.wrench.title\": \" 配置机器\",\n  \"item.indrev.wrench.autopush\": \"自动推入\",\n  \"item.indrev.wrench.autopull\": \"自动拉取\",\n  \"item.indrev.wrench.item\": \"物品\",\n  \"item.indrev.wrench.fluid\": \"液体\",\n  \"item.indrev.wrench.energy\": \"能量\",\n  \"item.indrev.wrench.output\": \"输出\",\n  \"item.indrev.wrench.input\": \"输入\",\n  \"item.indrev.wrench.input_first\": \"输入（一号槽位）\",\n  \"item.indrev.wrench.input_second\": \"输入（二号槽位）\",\n  \"item.indrev.wrench.input_output\": \"输入和输出\",\n  \"item.indrev.wrench.none\": \"无\",\n  \"item.indrev.wrench.tooltip\": \"Shift+右击以更改模式\",\n  \"item.indrev.wrench.tooltip1\": \"模式：%s\",\n  \"item.indrev.wrench.tooltip1.rotate\": \"旋转机器\",\n  \"item.indrev.wrench.tooltip1.configure\": \"配置机器\",\n  \"item.indrev.wrench.mode\": \"模式：%s\",\n  \"item.indrev.wrench.side.north\": \"北\",\n  \"item.indrev.wrench.side.south\": \"南\",\n  \"item.indrev.wrench.side.west\": \"西\",\n  \"item.indrev.wrench.side.east\": \"东\",\n  \"item.indrev.wrench.side.up\": \"上\",\n  \"item.indrev.wrench.side.down\": \"下\",\n  \"item.indrev.wrench.side.top\": \"顶端\",\n  \"item.indrev.wrench.side.bottom\": \"底端\",\n  \"item.indrev.wrench.side.left\": \"左\",\n  \"item.indrev.wrench.side.right\": \"右\",\n  \"item.indrev.wrench.side.front\": \"前\",\n  \"item.indrev.wrench.side.back\": \"后\",\n  \"item.indrev.wrench.connected\": \"已连接至%s\",\n  \"item.indrev.tech_soup\": \"高科技乱炖\",\n  \"item.indrev.scan_output\": \"扫描报告\",\n  \"item.indrev.portable_charger\": \"便携充电器\",\n  \"item.indrev.gamer_axe\": \"头号玩家斧\",\n  \"gui.indrev.button.auto_split\": \"自动分离成组的物品\",\n  \"gui.indrev.resourcereport.size\": \"矿脉大小：%s\",\n  \"gui.indrev.resourcereport.size.tiny\": \"些微\",\n  \"gui.indrev.resourcereport.size.small\": \"小\",\n  \"gui.indrev.resourcereport.size.average\": \"一般\",\n  \"gui.indrev.resourcereport.size.big\": \"大\",\n  \"gui.indrev.resourcereport.size.very_big\": \"很大\",\n  \"gui.indrev.resourcereport.size.enormous\": \"庞大\",\n  \"gui.indrev.resourcereport.size.gigantic\": \"超巨大\",\n  \"gui.furnace.mode\": \"模式\",\n  \"gui.furnace.mode.furnace\": \"熔炉\",\n  \"gui.furnace.mode.blast_furnace\": \"高炉\",\n  \"gui.furnace.mode.smoker\": \"烟熏炉\",\n  \"gui.widget.process\": \"进度：%s\",\n  \"gui.widget.energy\": \"能量\",\n  \"gui.widget.temperature\": \"温度\",\n  \"gui.widget.temperature_info.high\": \"过热\",\n  \"gui.widget.temperature_info.medium\": \"最适\",\n  \"gui.widget.temperature_info.low\": \"过冷\",\n  \"gui.indrev.solar.on\": \"发电中\",\n  \"gui.indrev.heatgen.idle\": \"闲置\",\n  \"gui.indrev.heatgen.title\": \"消耗%s mB\",\n  \"gui.indrev.heatgen.generating\": \"发电%s LF\",\n  \"gui.indrev.heatgen.pertick\": \"每刻\",\n  \"gui.indrev.heatgen.extra\": \"小心烫手！\",\n  \"gui.indrev.guide_book_shortcut.contains\": \"打开工业革命手册\",\n  \"gui.indrev.guide_book_shortcut.missing\": \"缺少工业革命手册！\",\n  \"gui.indrev.tooltip.maxInput\": \"最大输入：\",\n  \"gui.indrev.tooltip.maxOutput\": \"最大输出：\",\n  \"gui.indrev.tooltip.maxEnergyStored\": \"最大储能：\",\n  \"gui.indrev.tooltip.seconds\": \"%s秒\",\n  \"gui.indrev.tooltip.energyCost\": \"能耗：\",\n  \"gui.indrev.tooltip.processSpeed\": \"速率：\",\n  \"gui.indrev.tooltip.ratio\": \"发电：\",\n  \"gui.indrev.tooltip.press_shift\": \"按住 Shift 查看详细信息\",\n  \"gui.indrev.tooltip.maxTransferRate\": \"传输速率：\",\n  \"gui.indrev.tooltip.lftick\": \"%s LF/刻\",\n  \"gui.indrev.tooltip.lf\": \"%s LF\",\n  \"gui.indrev.tooltip.itemsec\": \"%s 物品/秒\",\n  \"gui.indrev.tooltip.fluidsec\": \"%s 桶/秒\",\n  \"gui.indrev.tooltip.temperatureBoost\": \"升温速率\",\n  \"gui.indrev.locked\": \"已锁定\",\n  \"gui.indrev.modular_armor_slot_type\": \"在这里放置模块盔甲\",\n  \"gui.indrev.module_slot_type\": \"在这里放置模块\",\n  \"gui.indrev.scan_output_slot_type\": \"在这里放置扫描结果\",\n  \"gui.indrev.output_slot_type\": \"输出格\",\n  \"gui.indrev.chopper_input_axe\": \"在这里放置斧头\",\n  \"gui.indrev.chopper_input_bone_meal\": \"在这里放置骨粉\",\n  \"gui.indrev.chopper_input_sapling\": \"在这里放置树苗\",\n  \"gui.indrev.farmer_input_slot_type\": \"在这里放置种子或骨粉\",\n  \"gui.indrev.slaughter_input_sword\": \"在这里放置剑\",\n  \"gui.indrev.cooler_slot_type\": \"在这里放置冷却单元\",\n  \"gui.indrev.battery_slot_type\": \"在这里放置电池\",\n  \"gui.indrev.upgrade_slot_type\": \"在这里放置升级\",\n  \"gui.indrev.fishingrod\": \"在这里放置钓竿\",\n  \"indrev.category.rei.upgrading\": \"你必须对%2$s使用%1$s以升级至%3$s\",\n  \"indrev.category.rei.pulverizing\": \"粉碎\",\n  \"indrev.category.rei.infusing\": \"固体注入\",\n  \"indrev.category.rei.compressing\": \"压缩\",\n  \"indrev.category.rei.recycling\": \"回收\",\n  \"indrev.category.rei.fluid_infusing\": \"液体注入\",\n  \"indrev.category.rei.condensing\": \"聚合\",\n  \"indrev.category.rei.smelting\": \"熔炼\",\n  \"indrev.category.rei.sawmill\": \"锯木机\",\n  \"indrev.category.rei.module\": \"模块合成\",\n  \"item.indrev.nikolite_dust\": \"蓝石粉\",\n  \"item.indrev.nikolite_ingot\": \"蓝石锭\",\n  \"block.indrev.nikolite_ore\": \"蓝石矿石\",\n  \"item.indrev.modular.upgrade\": \"已安装模块：\",\n  \"item.indrev.modular.upgrade.night_vision\": \"夜视%s\",\n  \"item.indrev.modular.upgrade.speed\": \"速度%s\",\n  \"item.indrev.modular.upgrade.jump_boost\": \"跳跃提升%s\",\n  \"item.indrev.modular.upgrade.breathing\": \"水下呼吸%s\",\n  \"item.indrev.modular.upgrade.protection\": \"保护%s\",\n  \"item.indrev.modular.upgrade.feather_falling\": \"摔落保护%s\",\n  \"item.indrev.modular.upgrade.auto_feeder\": \"自动进食%s\",\n  \"item.indrev.modular.upgrade.charger\": \"充电器%s\",\n  \"item.indrev.modular.upgrade.solar_panel\": \"太阳能板%s\",\n  \"item.indrev.modular.upgrade.piglin_tricker\": \"欺骗猪灵%s\",\n  \"item.indrev.modular.upgrade.elytra\": \"内置鞘翅%s\",\n  \"item.indrev.modular.upgrade.jetpack\": \"内置喷气背包%s\",\n  \"item.indrev.modular.upgrade.magnet\": \"磁铁%s\",\n  \"item.indrev.modular.upgrade.water_affinity\": \"亲水%s\",\n  \"item.indrev.modular.upgrade.fire_resistance\": \"抗火%s\",\n  \"item.indrev.modular.upgrade.efficiency\": \"效率%s\",\n  \"item.indrev.modular.upgrade.range\": \"范围采掘%s\",\n  \"item.indrev.modular.upgrade.fortune\": \"时运%s\",\n  \"item.indrev.modular.upgrade.silk_touch\": \"精准采集%s\",\n  \"item.indrev.modular.upgrade.sharpness\": \"锋利%s\",\n  \"item.indrev.modular.upgrade.looting\": \"抢夺%s\",\n  \"item.indrev.modular.upgrade.fire_aspect\": \"火焰附加%s\",\n  \"item.indrev.modular.upgrade.reach\": \"延伸%s\",\n  \"item.indrev.steel_helmet\": \"钢头盔\",\n  \"item.indrev.steel_chestplate\": \"钢胸甲\",\n  \"item.indrev.steel_leggings\": \"钢护腿\",\n  \"item.indrev.steel_boots\": \"钢靴子\",\n  \"item.indrev.steel_sword\": \"钢剑\",\n  \"item.indrev.steel_pickaxe\": \"钢镐\",\n  \"item.indrev.steel_axe\": \"钢斧\",\n  \"item.indrev.steel_shovel\": \"钢锹\",\n  \"item.indrev.steel_hoe\": \"钢锄\",\n  \"item.indrev.bronze_helmet\": \"青铜头盔\",\n  \"item.indrev.bronze_chestplate\": \"青铜胸甲\",\n  \"item.indrev.bronze_leggings\": \"青铜护腿\",\n  \"item.indrev.bronze_boots\": \"青铜靴子\",\n  \"item.indrev.bronze_sword\": \"青铜剑\",\n  \"item.indrev.bronze_pickaxe\": \"青铜镐\",\n  \"item.indrev.bronze_axe\": \"青铜斧\",\n  \"item.indrev.bronze_shovel\": \"青铜锹\",\n  \"item.indrev.bronze_hoe\": \"青铜锄\",\n  \"item.indrev.lead_helmet\": \"铅头盔\",\n  \"item.indrev.lead_chestplate\": \"铅胸甲\",\n  \"item.indrev.lead_leggings\": \"铅护腿\",\n  \"item.indrev.lead_boots\": \"铅靴子\",\n  \"item.indrev.lead_sword\": \"铅剑\",\n  \"item.indrev.lead_pickaxe\": \"铅镐\",\n  \"item.indrev.lead_axe\": \"铅斧\",\n  \"item.indrev.lead_shovel\": \"铅锹\",\n  \"item.indrev.lead_hoe\": \"铅锄\",\n  \"item.indrev.silver_helmet\": \"银头盔\",\n  \"item.indrev.silver_chestplate\": \"银胸甲\",\n  \"item.indrev.silver_leggings\": \"银护腿\",\n  \"item.indrev.silver_boots\": \"银靴子\",\n  \"item.indrev.silver_sword\": \"银剑\",\n  \"item.indrev.silver_pickaxe\": \"银镐\",\n  \"item.indrev.silver_axe\": \"银斧\",\n  \"item.indrev.silver_shovel\": \"银锹\",\n  \"item.indrev.silver_hoe\": \"银锄\",\n  \"block.indrev.modular_workbench\": \"模块工作台\",\n  \"block.indrev.modular_workbench_mk4\": \"模块工作台\",\n  \"item.indrev.modular_armor_helmet\": \"模块头盔\",\n  \"item.indrev.modular_armor_chest\": \"模块胸甲\",\n  \"item.indrev.modular_armor_legs\": \"模块护腿\",\n  \"item.indrev.modular_armor_boots\": \"模块靴子\",\n  \"item.indrev.module_parts\": \"可安装到\",\n  \"item.indrev.module_parts_head\": \"头盔\",\n  \"item.indrev.module_parts_chest\": \"胸甲\",\n  \"item.indrev.module_parts_legs\": \"护腿\",\n  \"item.indrev.module_parts_feet\": \"靴子\",\n  \"item.indrev.module_parts_drill\": \"采矿钻机\",\n  \"item.indrev.module_parts_gamer_axe\": \"头号玩家斧\",\n  \"item.indrev.module_max_level\": \"等级上限：%s\",\n  \"item.indrev.module_protection\": \"保护模块\",\n  \"item.indrev.module_protection.tooltip\": \"增强护甲的保护功能。\",\n  \"item.indrev.module_feather_falling\": \"羽落模块\",\n  \"item.indrev.module_feather_falling.tooltip\": \"消耗能量以免疫摔落伤害。\",\n  \"item.indrev.module_speed\": \"速度模块\",\n  \"item.indrev.module_speed.tooltip\": \"消耗能量以增加行进速度。\",\n  \"item.indrev.module_jump_boost\": \"跳跃提升模块\",\n  \"item.indrev.module_jump_boost.tooltip\": \"消耗能量以增加跳跃高度。\",\n  \"item.indrev.module_night_vision\": \"夜视模块\",\n  \"item.indrev.module_night_vision.tooltip\": \"消耗能量以长时夜视。\",\n  \"item.indrev.module_breathing\": \"呼吸模块\",\n  \"item.indrev.module_breathing.tooltip\": \"消耗能量以在水下呼吸。\",\n  \"item.indrev.module_auto_feeder\": \"自动进食模块\",\n  \"item.indrev.module_auto_feeder.tooltip\": \"消耗能量以自动喂食背包里的食物。\",\n  \"item.indrev.module_charger\": \"充电器模块\",\n  \"item.indrev.module_charger.tooltip\": \"为你背包里的物品充电（不含盔甲）。\",\n  \"item.indrev.module_solar_panel\": \"太阳能板模块\",\n  \"item.indrev.module_solar_panel.tooltip\": \"在阳光下给你的盔甲和手持物品充电。\",\n  \"item.indrev.module_piglin_tricker\": \"欺骗猪灵模块\",\n  \"item.indrev.module_piglin_tricker.tooltip\": \"让猪灵以为你穿着金盔甲。\",\n  \"item.indrev.module_elytra\": \"内置鞘翅模块\",\n  \"item.indrev.module_elytra.tooltip\": \"为你的胸甲安装一个强化鞘翅。\",\n  \"item.indrev.module_jetpack\": \"内置喷气背包模块\",\n  \"item.indrev.module_jetpack.tooltip\": \"为你的胸甲安装一个喷气背包。\",\n  \"item.indrev.module_magnet\": \"磁铁模块\",\n  \"item.indrev.module_magnet.tooltip\": \"吸附你身边的物品和经验值球。\",\n  \"item.indrev.module_water_affinity\": \"亲水模块\",\n  \"item.indrev.module_water_affinity.on\": \"装备在%s上时，\",\n  \"item.indrev.module_water_affinity.chestplate\": \"胸甲\",\n  \"item.indrev.module_water_affinity.leggings\": \"护腿\",\n  \"item.indrev.module_water_affinity.tooltip\": \"获得水下速掘。\",\n  \"item.indrev.module_water_affinity.tooltip1\": \"增加水下机动性。\",\n  \"item.indrev.module_fire_resistance\": \"抗火模块\",\n  \"item.indrev.module_fire_resistance.tooltip\": \"消耗能量以获得抗火效果。\",\n  \"item.indrev.module_efficiency\": \"效率模块\",\n  \"item.indrev.module_efficiency.tooltip\": \"增加工具的挖掘速度。\",\n  \"item.indrev.module_range\": \"范围采掘模块\",\n  \"item.indrev.module_range.tooltip\": \"增加工具的采掘范围。\",\n  \"item.indrev.module_fortune\": \"时运模块\",\n  \"item.indrev.module_fortune.tooltip\": \"同时运附魔。\",\n  \"item.indrev.module_silk_touch\": \"精准采集模块\",\n  \"item.indrev.module_silk_touch.tooltip\": \"同精准采集附魔。\",\n  \"item.indrev.module_reach\": \"延伸模块\",\n  \"item.indrev.module_reach.tooltip\": \"可以一次性伤害多个生物。\",\n  \"item.indrev.module_looting\": \"抢夺模块\",\n  \"item.indrev.module_looting.tooltip\": \"同抢夺附魔。\",\n  \"item.indrev.module_fire_aspect\": \"火焰附加模块\",\n  \"item.indrev.module_fire_aspect.tooltip\": \"同火焰附加附魔。\",\n  \"item.indrev.module_sharpness\": \"锋利模块\",\n  \"item.indrev.module_sharpness.tooltip\": \"增加武器伤害。\",\n  \"item.indrev.module_color.tooltip\": \"§c颜§e色§4模§b块\",\n  \"item.indrev.module_color_pink\": \"粉色模块\",\n  \"item.indrev.module_color_red\": \"红色模块\",\n  \"item.indrev.module_color_purple\": \"紫色模块\",\n  \"item.indrev.module_color_blue\": \"蓝色模块\",\n  \"item.indrev.module_color_cyan\": \"青色模块\",\n  \"item.indrev.module_color_green\": \"绿色模块\",\n  \"item.indrev.module_color_yellow\": \"黄色模块\",\n  \"item.indrev.module_color_orange\": \"橙色模块\",\n  \"item.indrev.module_color_black\": \"黑色模块\",\n  \"item.indrev.module_color_brown\": \"棕色模块\",\n  \"item.indrev.copper_sword\": \"铜剑\",\n  \"item.indrev.copper_pickaxe\": \"铜镐\",\n  \"item.indrev.copper_axe\": \"铜斧\",\n  \"item.indrev.copper_shovel\": \"铜锹\",\n  \"item.indrev.copper_hoe\": \"铜锄\",\n  \"item.indrev.copper_helmet\": \"铜头盔\",\n  \"item.indrev.copper_chestplate\": \"铜胸甲\",\n  \"item.indrev.copper_leggings\": \"铜护腿\",\n  \"item.indrev.copper_boots\": \"铜靴子\",\n  \"item.indrev.tin_sword\": \"锡剑\",\n  \"item.indrev.tin_pickaxe\": \"锡镐\",\n  \"item.indrev.tin_axe\": \"锡斧\",\n  \"item.indrev.tin_shovel\": \"锡锹\",\n  \"item.indrev.tin_hoe\": \"锡锄\",\n  \"item.indrev.tin_helmet\": \"锡头盔\",\n  \"item.indrev.tin_chestplate\": \"锡胸甲\",\n  \"item.indrev.tin_leggings\": \"锡护腿\",\n  \"item.indrev.tin_boots\": \"锡靴子\",\n  \"category.indrev\": \"工业革命\",\n  \"key.indrev.modular\": \"模块化物品设置\",\n  \"text.multiblock.not_built\": \"多方块结构不正确！\",\n  \"advancements.indrev.nikolite\": \"工业革命的开端\",\n  \"advancements.indrev.nikolite.description\": \"找到蓝石\",\n  \"advancements.indrev.machine_block\": \"夯实基础\",\n  \"advancements.indrev.machine_block.description\": \"获得一台机器\",\n  \"advancements.indrev.coal_generator\": \"革命第一步\",\n  \"advancements.indrev.coal_generator.description\": \"获得一台煤炭发电机\",\n  \"advancements.indrev.basic_solar_generator\": \"被动能源\",\n  \"advancements.indrev.basic_solar_generator.description\": \"获得一台太阳能发动机\",\n  \"advancements.indrev.advanced_solar_generator\": \"更多被动能源\",\n  \"advancements.indrev.advanced_solar_generator.description\": \"获得以太高级太阳能发动机\",\n  \"advancements.indrev.heat_generator\": \"燃起来了！\",\n  \"advancements.indrev.heat_generator.description\": \"获得一台热力发动机\",\n  \"advancements.indrev.biomass_generator\": \"也没那么环保嘛……\",\n  \"advancements.indrev.biomass_generator.description\": \"获得一台生物质发动机\",\n  \"advancements.indrev.pulverizer\": \"粉尘弥漫\",\n  \"advancements.indrev.pulverizer.description\": \"获得一台粉碎机\",\n  \"advancements.indrev.electric_furnace\": \"更好的熔炉\",\n  \"advancements.indrev.electric_furnace.description\": \"获得一台电炉\",\n  \"advancements.indrev.compressor\": \"小心夹手\",\n  \"advancements.indrev.compressor.description\": \"获得一台压缩机\",\n  \"advancements.indrev.recycler\": \"保护大自然\",\n  \"advancements.indrev.recycler.description\": \"获得一台回收机\",\n  \"advancements.indrev.solid_infuser\": \"注入灵魂\",\n  \"advancements.indrev.solid_infuser.description\": \"获得一台固体注入机\",\n  \"advancements.indrev.mk2_upgrade\": \"效率加倍！！\",\n  \"advancements.indrev.mk2_upgrade.description\": \"升级机器至MK2\",\n  \"advancements.indrev.mk3_upgrade\": \"效率再加倍！！！\",\n  \"advancements.indrev.mk3_upgrade.description\": \"升级机器至MK3\",\n  \"advancements.indrev.mk4_upgrade\": \"工厂！给我！办起来！\",\n  \"advancements.indrev.mk4_upgrade.description\": \"升级机器至MK4\",\n  \"advancements.indrev.biomass\": \"噫呃……\",\n  \"advancements.indrev.biomass.description\": \"获得生物质\",\n  \"advancements.indrev.nikolite_ingot\": \"崭新黎明\",\n  \"advancements.indrev.nikolite_ingot.description\": \"获得蓝石锭\",\n  \"advancements.indrev.enriched_nikolite_dust\": \"强力电子元件\",\n  \"advancements.indrev.enriched_nikolite_dust.description\": \"获得富集蓝石粉\",\n  \"advancements.indrev.enriched_nikolite_ingot\": \"搞快点！\",\n  \"advancements.indrev.enriched_nikolite_ingot.description\": \"获得富集蓝石锭\",\n  \"advancements.indrev.lazuli_flux_container_mk1\": \"基础的能源存储\",\n  \"advancements.indrev.lazuli_flux_container_mk1.description\": \"获得一台青金石通量容器MK1\",\n  \"advancements.indrev.lazuli_flux_container_mk2\": \"一般的能源存储\",\n  \"advancements.indrev.lazuli_flux_container_mk2.description\": \"获得一台青金石通量容器MK2\",\n  \"advancements.indrev.lazuli_flux_container_mk3\": \"不那么一般的能源存储\",\n  \"advancements.indrev.lazuli_flux_container_mk3.description\": \"获得一台青金石通量容器MK3\",\n  \"advancements.indrev.lazuli_flux_container_mk4\": \"究极能源存储\",\n  \"advancements.indrev.lazuli_flux_container_mk4.description\": \"获得一台青金石通量容器MK4\",\n  \"advancements.indrev.empty_enhancer\": \"“平平”无奇\",\n  \"advancements.indrev.empty_enhancer.description\": \"获得一个空白升级模块\",\n  \"advancements.indrev.speed_enhancer\": \"哈哈，机器轰隆隆隆隆\",\n  \"advancements.indrev.speed_enhancer.description\": \"获得速率升级\",\n  \"advancements.indrev.buffer_enhancer\": \"超越极限\",\n  \"advancements.indrev.buffer_enhancer.description\": \"获得缓冲升级\",\n  \"advancements.indrev.energy_enhancer\": \"卓有成效\",\n  \"advancements.indrev.energy_enhancer.description\": \"获得能效升级\",\n  \"advancements.indrev.cable_mk1\": \"橙线缆！\",\n  \"advancements.indrev.cable_mk1.description\": \"获得一根MK1线缆\",\n  \"advancements.indrev.cable_mk2\": \"蓝线缆！\",\n  \"advancements.indrev.cable_mk2.description\": \"获得一根MK2线缆\",\n  \"advancements.indrev.cable_mk3\": \"紫线缆！\",\n  \"advancements.indrev.cable_mk3.description\": \"获得一根MK3线缆\",\n  \"advancements.indrev.cable_mk4\": \"红线缆！\",\n  \"advancements.indrev.cable_mk4.description\": \"获得一根MK4线缆\",\n  \"advancements.indrev.modular_workbench\": \"这看着可不像工作台\",\n  \"advancements.indrev.modular_workbench.description\": \"获得模块工作台\",\n  \"advancements.indrev.modular_armor\": \"模块主义\",\n  \"advancements.indrev.modular_armor.description\": \"获得模块套装\",\n  \"advancements.indrev.gamer_axe\": \"灯大灯亮灯能闪！\",\n  \"advancements.indrev.gamer_axe.description\": \"获得游戏爱好者之斧\",\n  \"advancements.indrev.chopper_mk4\": \"自动树场\",\n  \"advancements.indrev.chopper_mk4.description\": \"获得一台伐木机\",\n  \"advancements.indrev.rancher_mk1\": \"我，机器农\",\n  \"advancements.indrev.rancher_mk1.description\": \"获得一台耕作机\",\n  \"advancements.indrev.rancher_mk4\": \"机械牧民\",\n  \"advancements.indrev.rancher_mk4.description\": \"获得一台放牧机\",\n  \"advancements.indrev.industrial_smelter\": \"高级冶金工业\",\n  \"advancements.indrev.industrial_smelter.description\": \"获得一台工业熔炉\",\n  \"advancements.indrev.condenser\": \"救救孩子，不会起成就名了\",\n  \"advancements.indrev.condenser.description\": \"获得一台聚合器\",\n  \"advancements.indrev.blast_furnace_enhancer\": \"电动高炉\",\n  \"advancements.indrev.blast_furnace_enhancer.description\": \"获得高炉升级\",\n  \"advancements.indrev.smoker_enhancer\": \"新东方厨师梦\",\n  \"advancements.indrev.smoker_enhancer.description\": \"获得烟熏炉升级\",\n  \"advancements.indrev.circuit_mk1\": \"机器第一步\",\n  \"advancements.indrev.circuit_mk1.description\": \"获得一个MK1电路\",\n  \"advancements.indrev.circuit_mk2\": \"新新科技\",\n  \"advancements.indrev.circuit_mk2.description\": \"获得一个MK2电路\",\n  \"advancements.indrev.circuit_mk3\": \"高级科技\",\n  \"advancements.indrev.circuit_mk3.description\": \"获得一个MK3电路\",\n  \"advancements.indrev.circuit_mk4\": \"究极科技\",\n  \"advancements.indrev.circuit_mk4.description\": \"获得一个MK4电路\",\n  \"advancements.indrev.farmer_mk1\": \"农家乐\",\n  \"advancements.indrev.farmer_mk1.description\": \"获得一台MK1耕作机\",\n  \"advancements.indrev.slaughter_mk1\": \"究极杀手\",\n  \"advancements.indrev.slaughter_mk1.description\": \"获得一台MK1屠宰机\",\n  \"gui.indrev.tip\": \"小贴士：\",\n  \"gui.indrev.tip_0\": \"请认真配置物品的输入和输出接口哦\",\n  \"gui.indrev.tip_1\": \"请勿使用超过输入上限的线缆\",\n  \"gui.indrev.tip_2\": \"酷酷地使用冷却单元才好呢\",\n  \"gui.indrev.tip_3\": \"用冷却单元来维持机器高效运行吧\",\n  \"gui.indrev.tip_4\": \"工厂！给我！办起来！！！\",\n  \"gui.indrev.tip_5\": \"来试试模块盔甲吧\",\n  \"gui.indrev.tip_6\": \"头号玩家就用头号玩家斧\",\n  \"gui.indrev.tip_7\": \"如果你遇到问题或有建议，请加入我们的Discord！\",\n  \"gui.indrev.tip_8\": \"在更新之前记得要好好备份你的世界\",\n  \"gui.indrev.tip_9\": \"睡觉前记得盖好被子\",\n  \"gui.indrev.modules_installed\": \"已安装模块：\",\n  \"gui.indrev.shield\": \"护盾：\",\n  \"gui.indrev.installing\": \"安装中\",\n  \"gui.indrev.incompatible\": \"不兼容的模块\",\n  \"gui.indrev.max_level\": \"已达最大升级\",\n  \"gui.indrev.progress\": \"进度：\",\n  \"gui.indrev.whitelist.true\": \"白名单\",\n  \"gui.indrev.whitelist.false\": \"黑名单\",\n  \"gui.indrev.matchDurability.true\": \"匹配物品耐久\",\n  \"gui.indrev.matchDurability.false\": \"忽略物品耐久\",\n  \"gui.indrev.matchTag.true\": \"匹配物品NBT\",\n  \"gui.indrev.matchTag.false\": \"忽略物品NBT\",\n  \"death.attack.acid\": \"%s掉进硫酸中融化了\",\n  \"death.attack.acid.player\": \"%s在尝试逃脱%s时掉进硫酸中融化了\",\n  \"death.attack.laser\": \"%s被激光烤焦了\",\n  \"death.attack.laser.player\": \"%s在尝试逃脱%s时被激光烧焦了\",\n  \"vein.indrev.bauxite\": \"铝土矿脉\",\n  \"vein.indrev.certus_quartz\": \"赛特斯石英矿脉\",\n  \"vein.indrev.peat\": \"泥煤矿脉\",\n  \"vein.indrev.lignite\": \"褐煤矿脉\",\n  \"vein.indrev.bituminous\": \"沥青矿脉\",\n  \"vein.indrev.anthracite\": \"白煤矿脉\",\n  \"vein.indrev.cuprite\": \"赤铜矿脉\",\n  \"vein.indrev.calaverite\": \"碲金矿脉\",\n  \"vein.indrev.calaverite_nether\": \"下界碲金矿脉\",\n  \"vein.indrev.iridium\": \"铱矿脉\",\n  \"vein.indrev.siderite\": \"菱铁矿脉\",\n  \"vein.indrev.limonite\": \"褐铁矿脉\",\n  \"vein.indrev.hematite\": \"赤铁矿脉\",\n  \"vein.indrev.magnetite\": \"磁铁矿脉\",\n  \"vein.indrev.chalcopryte\": \"黄铜矿脉\",\n  \"vein.indrev.nikolite\": \"蓝石矿脉\",\n  \"vein.indrev.quartz\": \"石英矿脉\",\n  \"vein.indrev.argentite\": \"辉银矿脉\",\n  \"vein.indrev.chlorargyrite\": \"角银矿脉\",\n  \"vein.indrev.cassiterite\": \"锡石矿脉\",\n  \"vein.indrev.stannite\": \"黄锡矿脉\",\n  \"vein.indrev.scheelite\": \"白钨矿脉\",\n  \"vein.indrev.wolframite\": \"黑钨矿脉\",\n  \"vein.indrev.ferberite\": \"钨铁矿脉\",\n  \"vein.indrev.galena\": \"方铅矿脉\",\n  \"vein.indrev.glowstonedeposit\": \"荧石矿床\",\n  \"vein.indrev.soul_nether\": \"下界灵魂矿床\",\n  \"vein.indrev.sulfur_nether\": \"硫矿\",\n  \"vein.indrev.denseice\": \"坚冰矿床\",\n  \"attribute.indrev.shield\": \"护盾\",\n  \"subtitles.indrev.laser\": \"激光：激发\",\n  \"item.indrev.reinforced_elytra\": \"强化鞘翅\",\n  \"item.indrev.jetpack_mk1\": \"喷气背包MK1\",\n  \"item.indrev.jetpack_mk2\": \"喷气背包MK2\",\n  \"item.indrev.jetpack_mk3\": \"喷气背包MK3\",\n  \"item.indrev.jetpack_mk4\": \"喷气背包MK4\",\n  \"block.indrev.gas_generator\": \"燃气发电机\",\n  \"block.indrev.gas_generator_mk4\": \"燃气发电机\",\n  \"item.indrev.soot\": \"烟灰\",\n  \"item.indrev.carbon_fiber_plate\": \"碳纤维板\",\n  \"item.indrev.carbon_fiber_rod\": \"碳纤维棒\",\n  \"item.indrev.carbon_fiber_helmet_frame\": \"碳纤维胸甲框架\",\n  \"item.indrev.carbon_fiber_chest_frame\": \"碳纤维头盔框架\",\n  \"item.indrev.carbon_fiber_legs_frame\": \"碳纤维护腿框架\",\n  \"item.indrev.carbon_fiber_boots_frame\": \"碳纤维靴子框架\",\n  \"block.indrev.boiler\": \"锅炉\",\n  \"block.indrev.steam_turbine_mk4\": \"蒸汽轮机\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/block_base.json",
    "content": "{\n    \"parent\": \"minecraft:block/cube_all\",\n    \"textures\": {\n      \"all\": \"indrev:block/block_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/block_highlight.json",
    "content": "{\n    \"parent\": \"minecraft:block/cube_all\",\n    \"textures\": {\n      \"all\": \"indrev:block/block_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/block_shadow.json",
    "content": "{\n    \"parent\": \"minecraft:block/cube_all\",\n    \"textures\": {\n      \"all\": \"indrev:block/block_shadow\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/bronze_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/bronze_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/cabinet.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"minecraft:block/block\",\n\t\"texture_size\": [64, 64],\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/cabinet\",\n\t\t\"particle\": \"indrev:block/cabinet\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 4, 4], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [4, 4, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [4, 0, 8, 4], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [4, 12, 0, 8], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [8, 8, 4, 12], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/cable_center_mk1.json",
    "content": "{\n  \"textures\": {\n    \"texture\": \"indrev:block/cable_center_emissive_mk1\",\n    \"1\": \"indrev:block/cable_center\"\n  },\n  \"elements\": [\n    {\n      \"from\": [5.5, 5.5, 5.5],\n      \"to\": [10.5, 10.5, 10.5],\n      \"faces\": {\n        \"north\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"east\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"south\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"west\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"up\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"down\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"}\n      }\n    }\n  ],\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [75, 45, 0],\n      \"translation\": [0, 2.5, 0]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [0, 45, 0]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [0, 225, 0]\n    },\n    \"ground\": {\n      \"translation\": [0, 2, 0]\n    },\n    \"gui\": {\n      \"rotation\": [30, 225, 0],\n      \"scale\": [1.25, 1.25, 1.25]\n    },\n    \"head\": {\n      \"rotation\": [0, 180, 0],\n      \"translation\": [0, 13, 7],\n      \"scale\": [2, 2, 2]\n    },\n    \"fixed\": {\n      \"rotation\": [0, 180, 0],\n      \"scale\": [2, 2, 2]\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/cable_center_mk2.json",
    "content": "{\n  \"textures\": {\n    \"texture\": \"indrev:block/cable_center_emissive_mk2\",\n    \"1\": \"indrev:block/cable_center\"\n  },\n  \"elements\": [\n    {\n      \"from\": [5.5, 5.5, 5.5],\n      \"to\": [10.5, 10.5, 10.5],\n      \"faces\": {\n        \"north\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"east\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"south\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"west\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"up\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"down\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"}\n      }\n    }\n  ],\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [75, 45, 0],\n      \"translation\": [0, 2.5, 0]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [0, 45, 0]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [0, 225, 0]\n    },\n    \"ground\": {\n      \"translation\": [0, 2, 0]\n    },\n    \"gui\": {\n      \"rotation\": [30, 225, 0],\n      \"scale\": [1.25, 1.25, 1.25]\n    },\n    \"head\": {\n      \"rotation\": [0, 180, 0],\n      \"translation\": [0, 13, 7],\n      \"scale\": [2, 2, 2]\n    },\n    \"fixed\": {\n      \"rotation\": [0, 180, 0],\n      \"scale\": [2, 2, 2]\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/cable_center_mk3.json",
    "content": "{\n  \"textures\": {\n    \"texture\": \"indrev:block/cable_center_emissive_mk3\",\n    \"1\": \"indrev:block/cable_center\"\n  },\n  \"elements\": [\n    {\n      \"from\": [5.5, 5.5, 5.5],\n      \"to\": [10.5, 10.5, 10.5],\n      \"faces\": {\n        \"north\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"east\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"south\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"west\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"up\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"down\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"}\n      }\n    }\n  ],\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [75, 45, 0],\n      \"translation\": [0, 2.5, 0]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [0, 45, 0]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [0, 225, 0]\n    },\n    \"ground\": {\n      \"translation\": [0, 2, 0]\n    },\n    \"gui\": {\n      \"rotation\": [30, 225, 0],\n      \"scale\": [1.25, 1.25, 1.25]\n    },\n    \"head\": {\n      \"rotation\": [0, 180, 0],\n      \"translation\": [0, 13, 7],\n      \"scale\": [2, 2, 2]\n    },\n    \"fixed\": {\n      \"rotation\": [0, 180, 0],\n      \"scale\": [2, 2, 2]\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/cable_center_mk4.json",
    "content": "{\n  \"textures\": {\n    \"texture\": \"indrev:block/cable_center_emissive_mk4\",\n    \"1\": \"indrev:block/cable_center\"\n  },\n  \"elements\": [\n    {\n      \"from\": [5.5, 5.5, 5.5],\n      \"to\": [10.5, 10.5, 10.5],\n      \"faces\": {\n        \"north\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"east\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"south\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"west\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"up\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"},\n        \"down\": {\"uv\": [3, 3, 13, 13], \"texture\": \"#1\"}\n      }\n    }\n  ],\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [75, 45, 0],\n      \"translation\": [0, 2.5, 0]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [0, 45, 0]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [0, 225, 0]\n    },\n    \"ground\": {\n      \"translation\": [0, 2, 0]\n    },\n    \"gui\": {\n      \"rotation\": [30, 225, 0],\n      \"scale\": [1.25, 1.25, 1.25]\n    },\n    \"head\": {\n      \"rotation\": [0, 180, 0],\n      \"translation\": [0, 13, 7],\n      \"scale\": [2, 2, 2]\n    },\n    \"fixed\": {\n      \"rotation\": [0, 180, 0],\n      \"scale\": [2, 2, 2]\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/cable_side_mk1.json",
    "content": "{\n  \"textures\": {\n    \"texture\": \"indrev:block/cable_wire_emissive_mk1\",\n    \"1\": \"indrev:block/cable_wrap\"\n  },\n  \"elements\": [\n    {\n      \"from\": [5.99, 0, 5.99],\n      \"to\": [10.01, 6.01, 10.01],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 4, 13]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"east\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"south\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"west\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"up\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"}\n      }\n    },\n    {\n      \"from\": [6.5, 0, 6.5],\n      \"to\": [9.5, 6, 9.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [17.3, 8, 16.2]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"east\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"south\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"west\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"up\": {\"uv\": [3, 3, 0, 0], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"down\": {\"uv\": [3, 6, 0, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"}\n      }\n    }\n  ],\n  \"jmx\": {\n    \"textures\": {\n      \"1\": \"indrev:block/cable_wrap\",\n      \"texture\": \"indrev:block/cable_wire_emissive_mk1\",\n      \"particle\": \"indrev:block/cable_wire_emissive_mk1\"\n    },\n    \"materials\": {\n      \"jmx_0\": {\n        \"layer0\": \"cutout\",\n        \"emissive0\": true,\n        \"ambient_occlusion0\": false,\n        \"diffuse0\": false\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/cable_side_mk2.json",
    "content": "{\n  \"textures\": {\n    \"texture\": \"indrev:block/cable_wire_emissive_mk2\",\n    \"1\": \"indrev:block/cable_wrap\"\n  },\n  \"elements\": [\n    {\n      \"from\": [5.99, 0, 5.99],\n      \"to\": [10.01, 6.01, 10.01],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 4, 13]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"east\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"south\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"west\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"up\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"}\n      }\n    },\n    {\n      \"from\": [6.5, 0, 6.5],\n      \"to\": [9.5, 6, 9.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [17.3, 8, 16.2]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"east\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"south\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"west\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"up\": {\"uv\": [3, 3, 0, 0], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"down\": {\"uv\": [3, 6, 0, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"}\n      }\n    }\n  ],\n  \"jmx\": {\n    \"textures\": {\n      \"1\": \"indrev:block/cable_wrap\",\n      \"texture\": \"indrev:block/cable_wire_emissive_mk2\",\n      \"particle\": \"indrev:block/cable_wire_emissive_mk2\"\n    },\n    \"materials\": {\n      \"jmx_0\": {\n        \"layer0\": \"cutout\",\n        \"emissive0\": true,\n        \"ambient_occlusion0\": false,\n        \"diffuse0\": false\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/cable_side_mk3.json",
    "content": "{\n  \"textures\": {\n    \"texture\": \"indrev:block/cable_wire_emissive_mk3\",\n    \"1\": \"indrev:block/cable_wrap\"\n  },\n  \"elements\": [\n    {\n      \"from\": [5.99, 0, 5.99],\n      \"to\": [10.01, 6.01, 10.01],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 4, 13]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"east\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"south\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"west\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"up\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"}\n      }\n    },\n    {\n      \"from\": [6.5, 0, 6.5],\n      \"to\": [9.5, 6, 9.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [17.3, 8, 16.2]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"east\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"south\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"west\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"up\": {\"uv\": [3, 3, 0, 0], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"down\": {\"uv\": [3, 6, 0, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"}\n      }\n    }\n  ],\n  \"jmx\": {\n    \"textures\": {\n      \"1\": \"indrev:block/cable_wrap\",\n      \"texture\": \"indrev:block/cable_wire_emissive_mk3\",\n      \"particle\": \"indrev:block/cable_wire_emissive_mk3\"\n    },\n    \"materials\": {\n      \"jmx_0\": {\n        \"layer0\": \"cutout\",\n        \"emissive0\": true,\n        \"ambient_occlusion0\": false,\n        \"diffuse0\": false\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/cable_side_mk4.json",
    "content": "{\n  \"textures\": {\n    \"texture\": \"indrev:block/cable_wire_emissive_mk4\",\n    \"1\": \"indrev:block/cable_wrap\"\n  },\n  \"elements\": [\n    {\n      \"from\": [5.99, 0, 5.99],\n      \"to\": [10.01, 6.01, 10.01],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 4, 13]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"east\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"south\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"west\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n        \"up\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"}\n      }\n    },\n    {\n      \"from\": [6.5, 0, 6.5],\n      \"to\": [9.5, 6, 9.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [17.3, 8, 16.2]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"east\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"south\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"west\": {\"uv\": [0, 3, 3, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"up\": {\"uv\": [3, 3, 0, 0], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"},\n        \"down\": {\"uv\": [3, 6, 0, 9], \"texture\": \"#texture\", \"jmx_tex0\": \"#texture\", \"jmx_material\": \"#jmx_0\"}\n      }\n    }\n  ],\n  \"jmx\": {\n    \"textures\": {\n      \"1\": \"indrev:block/cable_wrap\",\n      \"texture\": \"indrev:block/cable_wire_emissive_mk4\",\n      \"particle\": \"indrev:block/cable_wire_emissive_mk4\"\n    },\n    \"materials\": {\n      \"jmx_0\": {\n        \"layer0\": \"cutout\",\n        \"emissive0\": true,\n        \"ambient_occlusion0\": false,\n        \"diffuse0\": false\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/capsule.json",
    "content": "{\n\t\"parent\": \"block/block\",\n\t\"credit\": \"Made with Blockbench\",\n\t\"texture_size\": [4, 4],\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/capsule\",\n\t\t\"particle\": \"indrev:block/capsule\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 16, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 22, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [48, 2, 64, 4], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [48, 4, 64, 6], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [48, 6, 64, 8], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [48, 8, 64, 10], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [16, 16, 0, 0], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [9, 9, 7, 7], \"rotation\": 270, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [16, 0, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [64, 10, 48, 8], \"rotation\": 90, \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [16, 16, 0, 0], \"rotation\": 180, \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [64, 6, 48, 4], \"rotation\": 270, \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [9, 7, 7, 9], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [48, 6, 64, 8], \"rotation\": 90, \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [48, 2, 64, 4], \"rotation\": 90, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 16, 0],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 8, 0]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [16, 16, 0, 0], \"rotation\": 180, \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [48, 6, 64, 4], \"rotation\": 270, \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [9, 9, 7, 7], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [48, 10, 64, 8], \"rotation\": 90, \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [48, 4, 64, 2], \"rotation\": 180, \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [48, 8, 64, 6], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [0, 16, 16],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [0, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [64, 6, 48, 4], \"rotation\": 270, \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [9, 7, 7, 9], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [64, 10, 48, 8], \"rotation\": 90, \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [16, 16, 0, 0], \"rotation\": 180, \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [48, 6, 64, 8], \"rotation\": 270, \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [48, 2, 64, 4], \"rotation\": 270, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0, 0, 16],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 8, 16]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [9, 9, 7, 7], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [48, 10, 64, 8], \"rotation\": 90, \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [16, 16, 0, 0], \"rotation\": 180, \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [48, 6, 64, 4], \"rotation\": 270, \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [48, 4, 64, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [48, 8, 64, 6], \"rotation\": 180, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 0, 16],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, -6, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [48, 4, 64, 2], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [48, 6, 64, 4], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [48, 8, 64, 6], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [48, 10, 64, 8], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [9, 9, 7, 7], \"rotation\": 270, \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [16, 16, 0, 0], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/charge_pad_mk4.json",
    "content": "{\n  \"parent\": \"minecraft:block/block\",\n  \"credit\": \"Made with Blockbench\",\n  \"textures\": {\n    \"0\": \"indrev:block/machine_block\",\n    \"1\": \"indrev:block/charger_top\",\n    \"2\": \"indrev:block/charger_side\",\n    \"3\": \"indrev:block/charge_base\",\n    \"particle\": \"indrev:block/machine_block\"\n  },\n  \"elements\": [\n    {\n      \"name\": \"base\",\n      \"from\": [\n        1,\n        0,\n        1\n      ],\n      \"to\": [\n        15,\n        0.1,\n        15\n      ],\n      \"rotation\": {\n        \"angle\": 0,\n        \"axis\": \"y\",\n        \"origin\": [\n          9,\n          8,\n          9\n        ]\n      },\n      \"faces\": {\n        \"north\": {\n          \"uv\": [\n            0,\n            10.333,\n            13.5,\n            10.633\n          ],\n          \"texture\": \"#0\"\n        },\n        \"east\": {\n          \"uv\": [\n            0,\n            10.333,\n            13.5,\n            10.633\n          ],\n          \"texture\": \"#0\"\n        },\n        \"south\": {\n          \"uv\": [\n            0,\n            10.333,\n            13.5,\n            10.633\n          ],\n          \"texture\": \"#0\"\n        },\n        \"west\": {\n          \"uv\": [\n            0,\n            10.333,\n            13.5,\n            10.633\n          ],\n          \"texture\": \"#0\"\n        },\n        \"up\": {\n          \"uv\": [\n            5.333,\n            10.667,\n            10.666,\n            16\n          ],\n          \"texture\": \"#0\"\n        },\n        \"down\": {\n          \"uv\": [\n            5.333,\n            10.667,\n            10.666,\n            16\n          ],\n          \"texture\": \"#0\"\n        }\n      }\n    },\n    {\n      \"name\": \"base2\",\n      \"from\": [\n        1.25,\n        0,\n        1.25\n      ],\n      \"to\": [\n        14.75,\n        0.3,\n        14.75\n      ],\n      \"rotation\": {\n        \"angle\": 0,\n        \"axis\": \"y\",\n        \"origin\": [\n          9,\n          8,\n          9\n        ]\n      },\n      \"faces\": {\n        \"north\": {\n          \"uv\": [\n            0,\n            10.333,\n            13.5,\n            10.633\n          ],\n          \"texture\": \"#0\"\n        },\n        \"east\": {\n          \"uv\": [\n            0,\n            10.333,\n            13.5,\n            10.633\n          ],\n          \"texture\": \"#0\"\n        },\n        \"south\": {\n          \"uv\": [\n            0,\n            10.333,\n            13.5,\n            10.633\n          ],\n          \"texture\": \"#0\"\n        },\n        \"west\": {\n          \"uv\": [\n            0,\n            10.333,\n            13.5,\n            10.633\n          ],\n          \"texture\": \"#0\"\n        },\n        \"up\": {\n          \"uv\": [\n            0,\n            0,\n            16,\n            16\n          ],\n          \"texture\": \"#3\"\n        }\n      }\n    },\n    {\n      \"name\": \"arm\",\n      \"from\": [\n        7,\n        0,\n        2\n      ],\n      \"to\": [\n        9,\n        15,\n        4\n      ],\n      \"rotation\": {\n        \"angle\": 0,\n        \"axis\": \"y\",\n        \"origin\": [\n          15,\n          8,\n          10\n        ]\n      },\n      \"faces\": {\n        \"north\": {\n          \"uv\": [\n            0,\n            0,\n            4,\n            15\n          ],\n          \"texture\": \"#2\"\n        },\n        \"east\": {\n          \"uv\": [\n            0,\n            0,\n            4,\n            15\n          ],\n          \"texture\": \"#2\"\n        },\n        \"south\": {\n          \"uv\": [\n            0,\n            0,\n            4,\n            15\n          ],\n          \"texture\": \"#2\"\n        },\n        \"west\": {\n          \"uv\": [\n            0,\n            0,\n            4,\n            15\n          ],\n          \"texture\": \"#2\"\n        }\n      }\n    },\n    {\n      \"name\": \"arm2\",\n      \"from\": [\n        8,\n        15,\n        2\n      ],\n      \"to\": [\n        9,\n        15.5,\n        3\n      ],\n      \"rotation\": {\n        \"angle\": 0,\n        \"axis\": \"y\",\n        \"origin\": [\n          16,\n          23,\n          10\n        ]\n      },\n      \"faces\": {\n        \"north\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"east\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"south\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"west\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"up\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"down\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        }\n      }\n    },\n    {\n      \"name\": \"arm2\",\n      \"from\": [\n        7,\n        15,\n        2\n      ],\n      \"to\": [\n        8,\n        15.5,\n        3\n      ],\n      \"rotation\": {\n        \"angle\": 0,\n        \"axis\": \"y\",\n        \"origin\": [\n          15,\n          23,\n          10\n        ]\n      },\n      \"faces\": {\n        \"north\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"east\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"south\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"west\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"up\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"down\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        }\n      }\n    },\n    {\n      \"name\": \"arm2\",\n      \"from\": [\n        7,\n        15,\n        3\n      ],\n      \"to\": [\n        8,\n        15.2,\n        4\n      ],\n      \"rotation\": {\n        \"angle\": 0,\n        \"axis\": \"y\",\n        \"origin\": [\n          15,\n          23,\n          11\n        ]\n      },\n      \"faces\": {\n        \"north\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"east\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"south\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"west\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"up\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        },\n        \"down\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            1\n          ],\n          \"texture\": \"#2\"\n        }\n      }\n    },\n    {\n      \"name\": \"arm2\",\n      \"from\": [\n        8,\n        15,\n        3\n      ],\n      \"to\": [\n        9,\n        15.2,\n        4\n      ],\n      \"rotation\": {\n        \"angle\": 0,\n        \"axis\": \"y\",\n        \"origin\": [\n          16,\n          23,\n          11\n        ]\n      },\n      \"faces\": {\n        \"east\": {\n          \"uv\": [\n            0,\n            0,\n            1,\n            0.2\n          ],\n          \"texture\": \"#2\"\n        }\n      }\n    },\n    {\n      \"name\": \"top\",\n      \"from\": [\n        4.75,\n        13.1,\n        1.75\n      ],\n      \"to\": [\n        11.25,\n        13.4,\n        8.25\n      ],\n      \"rotation\": {\n        \"angle\": 22.5,\n        \"axis\": \"x\",\n        \"origin\": [\n          13,\n          21,\n          10\n        ]\n      },\n      \"faces\": {\n        \"north\": {\n          \"uv\": [\n            0,\n            0,\n            6.5,\n            0.3\n          ],\n          \"texture\": \"#1\"\n        },\n        \"east\": {\n          \"uv\": [\n            0,\n            0,\n            6.5,\n            0.3\n          ],\n          \"texture\": \"#1\"\n        },\n        \"south\": {\n          \"uv\": [\n            0,\n            0,\n            6.5,\n            0.3\n          ],\n          \"texture\": \"#1\"\n        },\n        \"west\": {\n          \"uv\": [\n            0,\n            0,\n            6.5,\n            0.3\n          ],\n          \"texture\": \"#1\"\n        },\n        \"up\": {\n          \"uv\": [\n            0,\n            0,\n            16,\n            16\n          ],\n          \"texture\": \"#1\"\n        },\n        \"down\": {\n          \"uv\": [\n            0,\n            0,\n            6.5,\n            0.3\n          ],\n          \"texture\": \"#1\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/controller.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"minecraft:block/block\",\n\t\"texture_size\": [64, 64],\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/controller\",\n\t\t\"particle\": \"indrev:block/controller\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 4, 4], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [4, 0, 8, 4], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [4, 4, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [4, 12, 0, 8], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [8, 8, 4, 12], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/deepslate_lead_ore.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/deepslate_lead_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/deepslate_nikolite_ore.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/deepslate_nikolite_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/deepslate_silver_ore.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/deepslate_silver_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/deepslate_tin_ore.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/deepslate_tin_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/deepslate_tungsten_ore.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/deepslate_tungsten_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/diamond_drill_head.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"texture_size\": [32, 32],\n\t\"textures\": {\n\t\t\"0\": \"indrev:item/diamond_drill_head\",\n\t\t\"particle\": \"indrev:item/diamond_drill_head\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [2.5, 6, 8.01],\n\t\t\t\"to\": [14.5, 18, 8.01],\n\t\t\t\"rotation\": {\"angle\": 45, \"axis\": \"z\", \"origin\": [8, 11.5, 8.01]},\n\t\t\t\"faces\": {\n\t\t\t\t\"south\": {\"uv\": [2, 3, 13, 14], \"rotation\": 270, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [2.5, 6, 7.99],\n\t\t\t\"to\": [14.5, 18, 7.99],\n\t\t\t\"rotation\": {\"angle\": 45, \"axis\": \"z\", \"origin\": [8, 11.5, 7.99]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [2, 3, 13, 14], \"rotation\": 180, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.99, 6, 2.5],\n\t\t\t\"to\": [7.99, 18, 14.5],\n\t\t\t\"rotation\": {\"angle\": -45, \"axis\": \"x\", \"origin\": [7.99, 11.5, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"west\": {\"uv\": [2, 3, 13, 14], \"rotation\": 270, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.01, 6, 2.5],\n\t\t\t\"to\": [8.01, 18, 14.5],\n\t\t\t\"rotation\": {\"angle\": -45, \"axis\": \"x\", \"origin\": [8.01, 11.5, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [2, 3, 13, 14], \"rotation\": 180, \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/drain_mk1.json",
    "content": "{\n  \"parent\": \"indrev:block/machine\",\n  \"textures\": {\n    \"texture\": \"indrev:block/drain\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/drill.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"block/block\",\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/drill_top\",\n\t\t\"1\": \"indrev:block/drill_middle\",\n\t\t\"2\": \"indrev:block/drill_bottom\",\n\t\t\"particle\": \"indrev:block/drill_top\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [5, 10.6, 6],\n\t\t\t\"to\": [10.3, 15.9, 11.3],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [7.65, 13.25, 8.65]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 4, 4], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 8, 4, 12], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 5.3, 6],\n\t\t\t\"to\": [10.3, 10.6, 11.3],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [7.65, 7.95, 8.65]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 4, 4], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [0, 8, 4, 12], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 3, 6],\n\t\t\t\"to\": [10.3, 5.3, 6],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [7.65, 2.65, 8.65]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 5.75, 4, 7.5], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 3.75, 4], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [4, 0, 7.75, 4], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 3, 6],\n\t\t\t\"to\": [5, 5.3, 11.3],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [7.65, 2.65, 8.65]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 5.75, 4, 7.5], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 3.75, 4], \"rotation\": 270, \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [4, 0, 7.75, 4], \"rotation\": 90, \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [10.3, 3, 6],\n\t\t\t\"to\": [10.3, 5.3, 11.3],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [7.65, 2.65, 8.65]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 5.75, 4, 7.5], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 3.75, 4], \"rotation\": 90, \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [4, 0, 7.75, 4], \"rotation\": 270, \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 3, 11.3],\n\t\t\t\"to\": [10.3, 5.3, 11.3],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [7.65, 2.65, 13.95]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 5.75, 4, 7.5], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 3.75, 4], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [4, 0, 7.75, 4], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [20.5, 0, 0],\n\t\t\t\"translation\": [0, 4.25, -2.5],\n\t\t\t\"scale\": [1.3, 1.3, 1.3]\n\t\t},\n\t\t\"thirdperson_lefthand\": {\n\t\t\t\"rotation\": [20.5, 0, 0],\n\t\t\t\"translation\": [0, 4.25, -2.5],\n\t\t\t\"scale\": [1.3, 1.3, 1.3]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [-11, 0, 8],\n\t\t\t\"scale\": [1.3, 1.3, 1.3]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [-11, 0, 8],\n\t\t\t\"scale\": [1.3, 1.3, 1.3]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0],\n\t\t\t\"translation\": [0.25, -1.25, 0],\n\t\t\t\"scale\": [0.95, 0.95, 0.95]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0],\n\t\t\t\"scale\": [1.3, 1.3, 1.3]\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/drill_bottom.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"1\": \"indrev:block/drill_bottom\",\n\t\t\"particle\": \"indrev:block/drill_bottom\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 9, 0.01],\n\t\t\t\"to\": [16, 16, 0.01],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 23, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [0, 5.75, 4, 7.5], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0, 9, 15.99],\n\t\t\t\"to\": [16, 16, 15.99],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 23, 24]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 5.75, 4, 7.5], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0.01, 9, 0],\n\t\t\t\"to\": [0.01, 16, 16],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 17, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [0, 5.75, 4, 7.5], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [15.99, 9, 0],\n\t\t\t\"to\": [15.99, 16, 16],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [24, 17, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [0, 4, 4, 5.75], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [0, 5.75, 4, 7.5], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0.01, 16.01, 0.01],\n\t\t\t\"to\": [15.99, 15.99, 15.99],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 24, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 4, 0.25], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 4, 0.25], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 4, 0.25], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 4, 0.25], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 4, 4], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 4, 4], \"texture\": \"#1\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/drill_middle.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/drill_middle\",\n\t\t\"particle\": \"indrev:block/drill_middle\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 4, 4], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 8, 4, 12], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/drill_top.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"texture_size\": [64, 64],\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/drill_top\",\n\t\t\"particle\": \"indrev:block/drill_top\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [4, 4, 0, 0], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 8, 4, 12], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/drill_top_on.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"texture_size\": [64, 64],\n  \"textures\": {\n    \"0\": \"indrev:block/drill_top\",\n    \"1\": \"indrev:block/drill_top_tracer_emissive_on\",\n    \"2\": \"indrev:block/drill_top_emissive_on\",\n    \"particle\": \"indrev:block/drill_top\"\n  },\n  \"elements\": [\n    {\n      \"from\": [0, 0, 0],\n      \"to\": [16, 16, 16],\n      \"faces\": {\n        \"north\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n        \"east\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n        \"south\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n        \"west\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n        \"up\": {\"uv\": [4, 4, 0, 0], \"texture\": \"#0\"},\n        \"down\": {\"uv\": [0, 8, 4, 12], \"texture\": \"#0\"}\n      }\n    },\n    {\n      \"from\": [-0.1, 7.9, -0.1],\n      \"to\": [16.1, 9.1, 16.1],\n      \"faces\": {\n        \"north\": {\"uv\": [0, 7, 16, 8], \"texture\": \"#2\", \"jmx_tex0\": \"#2\", \"jmx_material\": \"#jmx_0\"},\n        \"east\": {\"uv\": [0, 7, 16, 8], \"texture\": \"#2\", \"jmx_tex0\": \"#2\", \"jmx_material\": \"#jmx_0\"},\n        \"south\": {\"uv\": [0, 7, 16, 8], \"texture\": \"#2\", \"jmx_tex0\": \"#2\", \"jmx_material\": \"#jmx_0\"},\n        \"west\": {\"uv\": [0, 7, 16, 8], \"texture\": \"#2\", \"jmx_tex0\": \"#2\", \"jmx_material\": \"#jmx_0\"},\n        \"up\": {\"uv\": [0, 0, 2, 2], \"texture\": \"#2\", \"jmx_tex0\": \"#2\", \"jmx_material\": \"#jmx_0\"},\n        \"down\": {\"uv\": [0, 0, 2, 2], \"texture\": \"#2\", \"jmx_tex0\": \"#2\", \"jmx_material\": \"#jmx_0\"}\n      }\n    },\n    {\n      \"from\": [-0.1, 12.9, -0.1],\n      \"to\": [16.1, 16.1, 16.1],\n      \"faces\": {\n        \"north\": {\"uv\": [4, 4, 8, 4.75], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"},\n        \"east\": {\"uv\": [0, 4, 4, 4.75], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"},\n        \"south\": {\"uv\": [12, 4, 16, 4.75], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"},\n        \"west\": {\"uv\": [8, 4, 12, 4.75], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"},\n        \"up\": {\"uv\": [8, 4, 4, 0], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"},\n        \"down\": {\"uv\": [4, 12, 0, 16], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"}\n      }\n    }\n  ],\n  \"jmx\": {\n    \"textures\": {\n      \"0\": \"indrev:block/drill_top\",\n      \"1\": \"indrev:block/drill_top_tracer_emissive_on\",\n      \"2\": \"indrev:block/drill_top_emissive_on\"\n    },\n    \"materials\": {\n      \"jmx_0\": {\n        \"layer0\": \"cutout\",\n        \"emissive0\": true,\n        \"ambient_occlusion0\": false,\n        \"diffuse0\": false\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/duct.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"block/block\",\n\t\"texture_size\": [32, 32],\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/duct-opening\",\n\t\t\"1\": \"indrev:block/duct-tube-upper\",\n\t\t\"2\": \"indrev:block/duct-tube-lower\",\n\t\t\"particle\": \"indrev:block/duct-opening\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [2, 2, 15],\n\t\t\t\"to\": [14, 14, 16],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 9, 15.5]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 6, 6, 12], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [6, 0, 6.5, 6], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [6, 0, 6.5, 6], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [12.5, 0.5, 6.5, 0], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [12.5, 0, 6.5, 0.5], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [2, 0, 2],\n\t\t\t\"to\": [14, 1, 14],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 0.5, 9]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [6, 0, 6.5, 6.5], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [6.5, 0, 12.5, 0.5], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [6, 0, 6.5, 6.5], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [6.5, 0, 12.5, 0.5], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 6, 6, 12], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [3, 3, 3],\n\t\t\t\"to\": [13, 13, 15],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [11, 11, 11]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [11, 11, 16, 16], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 6, 5], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [0, 11, 5, 16], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [0, 5, 6, 10], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [11, 12, 6, 6], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [11, 0, 6, 6], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [3.03, 1.03, 3.03],\n\t\t\t\"to\": [12.99, 3.99, 12.99],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [11, 9, 11]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [5, 8.5, 10, 10], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [5, 2.5, 10, 4], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [5, 0.5, 10, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [5, 0.5, 10, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [5, 5, 0, 0], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [5, 5, 0, 10], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/electrum_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/electrum_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/fishing_farm_mk2.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"texture_size\": [32, 32],\n  \"parent\": \"minecraft:block/block\",\n  \"textures\": {\n    \"1\": \"indrev:block/machine_block\",\n    \"wood_top\": \"indrev:block/wood_top\",\n    \"particle\": \"indrev:block/machine_block\"\n  },\n  \"elements\": [\n    {\n      \"from\": [1, 13, 7],\n      \"to\": [2, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 19, 17]},\n      \"faces\": {\n        \"east\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [14, 13, 7],\n      \"to\": [15, 16, 14],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 19, 15]},\n      \"faces\": {\n        \"west\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 13, 0],\n      \"to\": [16, 16, 7],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 20, 12]},\n      \"faces\": {\n        \"north\": {\"uv\": [8, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [8, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 0, 16, 3.5], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 5, 3.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [5, 15, 2],\n      \"to\": [12, 17, 4],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [12, 21, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 5, 3.5, 5.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [4.5, 9, 5.5, 9.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [5.5, 15, 9, 15.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [4.5, 9, 8, 10], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [6, 15, 1],\n      \"to\": [11, 17, 5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [13, 21, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [1, 8, 3.5, 8.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [1.5, 11.5, 3.5, 12], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [1, 9, 3.5, 9.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [1.5, 11.5, 3.5, 12], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [1, 6, 3.5, 8], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [6, 16, 2],\n      \"to\": [11, 18, 4],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [14, 22, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [7, 12, 9.5, 12.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [4.5, 9, 5.5, 9.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [7, 11.5, 9.5, 12], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [4.5, 9, 5.5, 9.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [0, 4, 2.5, 5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [8, 15, 5],\n      \"to\": [9, 17, 6],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [16, 21, 13]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [4, 15, 1],\n      \"to\": [5, 17, 5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [12, 21, 12]},\n      \"faces\": {\n        \"north\": {\"uv\": [4.5, 11, 5, 11.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [4.5, 13, 6.5, 13.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [4.5, 11, 5, 11.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [4.5, 13.5, 6.5, 14], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [4.5, 11, 5, 13], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [2, 13, 14],\n      \"to\": [15, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 19, 22]},\n      \"faces\": {\n        \"north\": {\"uv\": [8.5, 2.5, 15, 4], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [9, 0, 15.5, 0.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 13, 7],\n      \"to\": [1, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 19, 15]},\n      \"faces\": {\n        \"west\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 3, 8.5, 7], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 0.5, 3.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [15, 13, 7],\n      \"to\": [16, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [23, 20, 21]},\n      \"faces\": {\n        \"east\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 3, 8.5, 7], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 0.5, 3.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 13, 15],\n      \"to\": [16, 16, 16],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 19, 23]},\n      \"faces\": {\n        \"east\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 0, 16, 0.5], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 5, 0.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 0, 0],\n      \"to\": [16, 13, 16],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 8, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"east\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"south\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"west\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"up\": {\"uv\": [6, 0.333, 9.6665, 4.833], \"texture\": \"#1\"},\n        \"down\": {\"uv\": [5.6665, 10.9995, 10.333, 15.666], \"texture\": \"#1\"}\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/fishing_farm_mk3.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"texture_size\": [32, 32],\n  \"parent\": \"minecraft:block/block\",\n  \"textures\": {\n    \"1\": \"indrev:block/machine_block\",\n    \"wood_top\": \"indrev:block/wood_top\",\n    \"particle\": \"indrev:block/machine_block\"\n  },\n  \"elements\": [\n    {\n      \"from\": [1, 13, 7],\n      \"to\": [2, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 19, 17]},\n      \"faces\": {\n        \"east\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [14, 13, 7],\n      \"to\": [15, 16, 14],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 19, 15]},\n      \"faces\": {\n        \"west\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 13, 0],\n      \"to\": [16, 16, 7],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 20, 12]},\n      \"faces\": {\n        \"north\": {\"uv\": [8, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [8, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 0, 16, 3.5], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 5, 3.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [5, 15, 2],\n      \"to\": [12, 17, 4],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [12, 21, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 5, 3.5, 5.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [4.5, 9, 5.5, 9.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [5.5, 15, 9, 15.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [4.5, 9, 8, 10], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [6, 15, 1],\n      \"to\": [11, 17, 5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [13, 21, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [1, 8, 3.5, 8.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [1.5, 11.5, 3.5, 12], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [1, 9, 3.5, 9.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [1.5, 11.5, 3.5, 12], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [1, 6, 3.5, 8], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [6, 16, 2],\n      \"to\": [11, 18, 4],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [14, 22, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [7, 12, 9.5, 12.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [4.5, 9, 5.5, 9.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [7, 11.5, 9.5, 12], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [4.5, 9, 5.5, 9.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [0, 4, 2.5, 5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [8, 15, 5],\n      \"to\": [9, 17, 6],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [16, 21, 13]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [4, 15, 1],\n      \"to\": [5, 17, 5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [12, 21, 12]},\n      \"faces\": {\n        \"north\": {\"uv\": [4.5, 11, 5, 11.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [4.5, 13, 6.5, 13.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [4.5, 11, 5, 11.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [4.5, 13.5, 6.5, 14], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [4.5, 11, 5, 13], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [2, 13, 14],\n      \"to\": [15, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 19, 22]},\n      \"faces\": {\n        \"north\": {\"uv\": [8.5, 2.5, 15, 4], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [9, 0, 15.5, 0.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 13, 7],\n      \"to\": [1, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 19, 15]},\n      \"faces\": {\n        \"west\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 3, 8.5, 7], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 0.5, 3.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [15, 13, 7],\n      \"to\": [16, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [23, 20, 21]},\n      \"faces\": {\n        \"east\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 3, 8.5, 7], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 0.5, 3.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 13, 15],\n      \"to\": [16, 16, 16],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 19, 23]},\n      \"faces\": {\n        \"east\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 0, 16, 0.5], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 5, 0.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 0, 0],\n      \"to\": [16, 13, 16],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 8, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"east\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"south\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"west\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"up\": {\"uv\": [6, 0.333, 9.6665, 4.833], \"texture\": \"#1\"},\n        \"down\": {\"uv\": [5.6665, 10.9995, 10.333, 15.666], \"texture\": \"#1\"}\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/fishing_farm_mk4.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"texture_size\": [32, 32],\n  \"parent\": \"minecraft:block/block\",\n  \"textures\": {\n    \"1\": \"indrev:block/machine_block\",\n    \"wood_top\": \"indrev:block/wood_top\",\n    \"particle\": \"indrev:block/machine_block\"\n  },\n  \"elements\": [\n    {\n      \"from\": [1, 13, 7],\n      \"to\": [2, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 19, 17]},\n      \"faces\": {\n        \"east\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [14, 13, 7],\n      \"to\": [15, 16, 14],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 19, 15]},\n      \"faces\": {\n        \"west\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8.5, 1, 9, 5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 13, 0],\n      \"to\": [16, 16, 7],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 20, 12]},\n      \"faces\": {\n        \"north\": {\"uv\": [8, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [8, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 0, 16, 3.5], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 5, 3.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [5, 15, 2],\n      \"to\": [12, 17, 4],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [12, 21, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 5, 3.5, 5.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [4.5, 9, 5.5, 9.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [5.5, 15, 9, 15.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [4.5, 9, 8, 10], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [6, 15, 1],\n      \"to\": [11, 17, 5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [13, 21, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [1, 8, 3.5, 8.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [1.5, 11.5, 3.5, 12], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [1, 9, 3.5, 9.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [1.5, 11.5, 3.5, 12], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [1, 6, 3.5, 8], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [6, 16, 2],\n      \"to\": [11, 18, 4],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [14, 22, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [7, 12, 9.5, 12.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [4.5, 9, 5.5, 9.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [7, 11.5, 9.5, 12], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [4.5, 9, 5.5, 9.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [0, 4, 2.5, 5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [8, 15, 5],\n      \"to\": [9, 17, 6],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [16, 21, 13]},\n      \"faces\": {\n        \"north\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 4, 0.5, 4.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [4, 15, 1],\n      \"to\": [5, 17, 5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [12, 21, 12]},\n      \"faces\": {\n        \"north\": {\"uv\": [4.5, 11, 5, 11.5], \"texture\": \"#wood_top\"},\n        \"east\": {\"uv\": [4.5, 13, 6.5, 13.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [4.5, 11, 5, 11.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [4.5, 13.5, 6.5, 14], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [4.5, 11, 5, 13], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [2, 13, 14],\n      \"to\": [15, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 19, 22]},\n      \"faces\": {\n        \"north\": {\"uv\": [8.5, 2.5, 15, 4], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [9, 0, 15.5, 0.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 13, 7],\n      \"to\": [1, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 19, 15]},\n      \"faces\": {\n        \"west\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 3, 8.5, 7], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 0.5, 3.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [15, 13, 7],\n      \"to\": [16, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [23, 20, 21]},\n      \"faces\": {\n        \"east\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 3, 8.5, 7], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 0.5, 3.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 13, 15],\n      \"to\": [16, 16, 16],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 19, 23]},\n      \"faces\": {\n        \"east\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"south\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"west\": {\"uv\": [15.5, 0, 16, 1.5], \"texture\": \"#wood_top\"},\n        \"up\": {\"uv\": [8, 0, 16, 0.5], \"texture\": \"#wood_top\"},\n        \"down\": {\"uv\": [0, 0, 5, 0.5], \"texture\": \"#wood_top\"}\n      }\n    },\n    {\n      \"from\": [0, 0, 0],\n      \"to\": [16, 13, 16],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 8, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"east\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"south\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"west\": {\"uv\": [5.333, 5.6665, 10.665, 10.832], \"texture\": \"#1\"},\n        \"up\": {\"uv\": [6, 0.333, 9.6665, 4.833], \"texture\": \"#1\"},\n        \"down\": {\"uv\": [5.6665, 10.9995, 10.333, 15.666], \"texture\": \"#1\"}\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/fluid_pipe_center_mk1.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"2\": \"indrev:block/fluid_pipe_center_mk1\",\n\t\t\"particle\": \"indrev:block/fluid_pipe_center_mk1\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6, 6, 6],\n\t\t\t\"to\": [10, 10, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0],\n\t\t\t\"scale\": [1, 1, 1]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/fluid_pipe_center_mk2.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"2\": \"indrev:block/fluid_pipe_center_mk2\",\n\t\t\"particle\": \"indrev:block/fluid_pipe_center_mk2\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6, 6, 6],\n\t\t\t\"to\": [10, 10, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0],\n\t\t\t\"scale\": [1, 1, 1]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/fluid_pipe_center_mk3.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"2\": \"indrev:block/fluid_pipe_center_mk3\",\n\t\t\"particle\": \"indrev:block/fluid_pipe_center_mk3\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6, 6, 6],\n\t\t\t\"to\": [10, 10, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0],\n\t\t\t\"scale\": [1, 1, 1]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/fluid_pipe_center_mk4.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"2\": \"indrev:block/fluid_pipe_center_mk4\",\n\t\t\"particle\": \"indrev:block/fluid_pipe_center_mk4\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6, 6, 6],\n\t\t\t\"to\": [10, 10, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0],\n\t\t\t\"scale\": [1.0, 1.0, 1.0]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/fluid_pipe_side_mk1.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"1\": \"indrev:block/fluid_pipe_side_mk1\",\n\t\t\"particle\": \"indrev:block/fluid_pipe_side_mk1\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6, 0, 6],\n\t\t\t\"to\": [10, 6, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [4, 10, 0, 6], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [4, 6, 0, 10], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 4, 5],\n\t\t\t\"to\": [11, 5, 11],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 12, 6, 13], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 13, 6, 14], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [0, 10, 6, 11], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [0, 11, 6, 12], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [6, 6, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [6, 0, 0, 6], \"texture\": \"#1\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/fluid_pipe_side_mk2.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"1\": \"indrev:block/fluid_pipe_side_mk2\",\n\t\t\"particle\": \"indrev:block/fluid_pipe_side_mk2\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6, 0, 6],\n\t\t\t\"to\": [10, 6, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [4, 10, 0, 6], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [4, 6, 0, 10], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 4, 5],\n\t\t\t\"to\": [11, 5, 11],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 12, 6, 13], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 13, 6, 14], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [0, 10, 6, 11], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [0, 11, 6, 12], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [6, 6, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [6, 0, 0, 6], \"texture\": \"#1\"}\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/fluid_pipe_side_mk3.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"1\": \"indrev:block/fluid_pipe_side_mk3\",\n\t\t\"particle\": \"indrev:block/fluid_pipe_side_mk3\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6, 0, 6],\n\t\t\t\"to\": [10, 6, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [4, 10, 0, 6], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [4, 6, 0, 10], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 4, 5],\n\t\t\t\"to\": [11, 5, 11],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 12, 6, 13], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 13, 6, 14], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [0, 10, 6, 11], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [0, 11, 6, 12], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [6, 6, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [6, 0, 0, 6], \"texture\": \"#1\"}\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/fluid_pipe_side_mk4.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"1\": \"indrev:block/fluid_pipe_side_mk4\",\n\t\t\"particle\": \"indrev:block/fluid_pipe_side_mk4\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6, 0, 6],\n\t\t\t\"to\": [10, 6, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [6, 0, 10, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [4, 10, 0, 6], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [4, 6, 0, 10], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 4, 5],\n\t\t\t\"to\": [11, 5, 11],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 12, 6, 13], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 13, 6, 14], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [0, 10, 6, 11], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [0, 11, 6, 12], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [6, 6, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [6, 0, 0, 6], \"texture\": \"#1\"}\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/frame.json",
    "content": "{\n\t\"parent\": \"minecraft:block/block\",\n\t\"texture_size\": [64, 64],\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/frame\",\n\t\t\"particle\": \"indrev:block/frame\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [16, 8, 8, 0], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [8, 8, 0, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/gray_lava.json",
    "content": "{\n  \"textures\": {\n    \"particle\": \"indrev:block/gray_lava_still\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/heat_generator_mk4.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"block/block\",\n\t\"texture_size\": [64, 64],\n\t\"textures\": {\n\t\t\"2\": \"indrev:block/heat_generator_inside\",\n\t\t\"texture\": \"indrev:block/heat_generator\",\n\t\t\"particle\": \"indrev:block/heat_generator\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [5.33333, 5.33333, 10.66667, 10.66667], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [0, 5.33333, 5.33333, 10.66667], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [10.66667, 10.66667, 16, 16], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [10.66667, 5.33333, 16, 10.66667], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [5.33333, 0, 10.66667, 5.33333], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [5.33333, 10.66667, 10.66667, 16], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0.01, 0.99, 0.01],\n\t\t\t\"to\": [15.99, 1.01, 4.99],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 4, 0], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1.25, 0], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 4, 0], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1.25, 0], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [8, 3.5, 4, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [4, 4.5, 0, 5.75], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0.01, 9.99, 0.01],\n\t\t\t\"to\": [15.99, 10.01, 4.99],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 4, 0], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1.25, 0], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 4, 0], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1.25, 0], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [4, 7, 0, 5.75], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [8, 2.25, 4, 3.5], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0.02, 1.02, 5.02],\n\t\t\t\"to\": [15.98, 9.98, 4.98],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 4, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 0, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 2.25, 4, 4.5], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 0, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [4, 0, 0, 0], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [4, 0, 0, 0], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0.01, 1.01, 0.01],\n\t\t\t\"to\": [15.99, 9.99, 0.0],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [4, 4, 8, 6.25], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 0, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [4, 0, 8, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 0, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [4, 0, 0, 0], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [4, 0, 0, 0], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0.02, 1.01, 0.01],\n\t\t\t\"to\": [0, 9.99, 4.99],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 0, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [6, 7.5, 7.25, 9.75], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 0, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [7.25, 7.5, 8.5, 9.75], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 1.25, 0, 0], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 0, 1.25], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [16, 1.01, 0.01],\n\t\t\t\"to\": [15.98, 9.99, 4.99],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 0, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [8, 0, 9.25, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 0, 2.25], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [8, 2.25, 9.25, 4.5], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 1.25, 0, 0], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 0, 1.25], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/intake.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"minecraft:block/block\",\n\t\"texture_size\": [64, 64],\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/intake\",\n\t\t\"particle\": \"indrev:block/intake\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [4, 4, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [4, 4, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [4, 4, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [4, 4, 0, 0], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [8, 0, 4, 4], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/iron_drill_head.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"texture_size\": [32, 32],\n\t\"textures\": {\n\t\t\"0\": \"indrev:item/iron_drill_head\",\n\t\t\"particle\": \"indrev:item/iron_drill_head\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [2.5, 6, 8.01],\n\t\t\t\"to\": [14.5, 18, 8.01],\n\t\t\t\"rotation\": {\"angle\": 45, \"axis\": \"z\", \"origin\": [8, 11.5, 8.01]},\n\t\t\t\"faces\": {\n\t\t\t\t\"south\": {\"uv\": [2, 3, 13, 14], \"rotation\": 270, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [2.5, 6, 7.99],\n\t\t\t\"to\": [14.5, 18, 7.99],\n\t\t\t\"rotation\": {\"angle\": 45, \"axis\": \"z\", \"origin\": [8, 11.5, 7.99]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [2, 3, 13, 14], \"rotation\": 180, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.99, 6, 2.5],\n\t\t\t\"to\": [7.99, 18, 14.5],\n\t\t\t\"rotation\": {\"angle\": -45, \"axis\": \"x\", \"origin\": [7.99, 11.5, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"west\": {\"uv\": [2, 3, 13, 14], \"rotation\": 270, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.01, 6, 2.5],\n\t\t\t\"to\": [8.01, 18, 14.5],\n\t\t\t\"rotation\": {\"angle\": -45, \"axis\": \"x\", \"origin\": [8.01, 11.5, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [2, 3, 13, 14], \"rotation\": 180, \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/item_pipe_center_mk1.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"2\": \"indrev:block/item_pipe_center_mk1\",\n\t\t\"particle\": \"indrev:block/item_pipe_center_mk1\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6.5, 6.5, 6.5],\n\t\t\t\"to\": [7.5, 7.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 6.5, 8.5],\n\t\t\t\"to\": [8.5, 7.5, 9.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 6.5, 6.5],\n\t\t\t\"to\": [8.5, 7.5, 7.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 8.5, 6.5],\n\t\t\t\"to\": [7.5, 9.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 7.5, 6.5],\n\t\t\t\"to\": [7.5, 8.5, 7.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 7.5, 6.5],\n\t\t\t\"to\": [9.5, 8.5, 7.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 7.5, 8.5],\n\t\t\t\"to\": [9.5, 8.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 7.5, 8.5],\n\t\t\t\"to\": [7.5, 8.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 8.5, 8.5],\n\t\t\t\"to\": [8.5, 9.5, 9.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 8.5, 6.5],\n\t\t\t\"to\": [8.5, 9.5, 7.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 6.5, 6.5],\n\t\t\t\"to\": [9.5, 7.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 8.5, 6.5],\n\t\t\t\"to\": [9.5, 9.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/item_pipe_center_mk2.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"2\": \"indrev:block/item_pipe_center_mk2\",\n\t\t\"particle\": \"indrev:block/item_pipe_center_mk2\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6.5, 6.5, 6.5],\n\t\t\t\"to\": [7.5, 7.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 6.5, 8.5],\n\t\t\t\"to\": [8.5, 7.5, 9.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 6.5, 6.5],\n\t\t\t\"to\": [8.5, 7.5, 7.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 8.5, 6.5],\n\t\t\t\"to\": [7.5, 9.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 7.5, 6.5],\n\t\t\t\"to\": [7.5, 8.5, 7.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 7.5, 6.5],\n\t\t\t\"to\": [9.5, 8.5, 7.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 7.5, 8.5],\n\t\t\t\"to\": [9.5, 8.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 7.5, 8.5],\n\t\t\t\"to\": [7.5, 8.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 8.5, 8.5],\n\t\t\t\"to\": [8.5, 9.5, 9.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 8.5, 6.5],\n\t\t\t\"to\": [8.5, 9.5, 7.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 6.5, 6.5],\n\t\t\t\"to\": [9.5, 7.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 8.5, 6.5],\n\t\t\t\"to\": [9.5, 9.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/item_pipe_center_mk3.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"2\": \"indrev:block/item_pipe_center_mk3\",\n\t\t\"particle\": \"indrev:block/item_pipe_center_mk3\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6.5, 6.5, 6.5],\n\t\t\t\"to\": [7.5, 7.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 6.5, 8.5],\n\t\t\t\"to\": [8.5, 7.5, 9.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 6.5, 6.5],\n\t\t\t\"to\": [8.5, 7.5, 7.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 8.5, 6.5],\n\t\t\t\"to\": [7.5, 9.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 7.5, 6.5],\n\t\t\t\"to\": [7.5, 8.5, 7.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 7.5, 6.5],\n\t\t\t\"to\": [9.5, 8.5, 7.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 7.5, 8.5],\n\t\t\t\"to\": [9.5, 8.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 7.5, 8.5],\n\t\t\t\"to\": [7.5, 8.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 8.5, 8.5],\n\t\t\t\"to\": [8.5, 9.5, 9.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 8.5, 6.5],\n\t\t\t\"to\": [8.5, 9.5, 7.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 6.5, 6.5],\n\t\t\t\"to\": [9.5, 7.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 8.5, 6.5],\n\t\t\t\"to\": [9.5, 9.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/item_pipe_center_mk4.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"2\": \"indrev:block/item_pipe_center_mk4\",\n\t\t\"particle\": \"indrev:block/item_pipe_center_mk4\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6.5, 6.5, 6.5],\n\t\t\t\"to\": [7.5, 7.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 6.5, 8.5],\n\t\t\t\"to\": [8.5, 7.5, 9.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 6.5, 6.5],\n\t\t\t\"to\": [8.5, 7.5, 7.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 8.5, 6.5],\n\t\t\t\"to\": [7.5, 9.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 7.5, 6.5],\n\t\t\t\"to\": [7.5, 8.5, 7.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 7.5, 6.5],\n\t\t\t\"to\": [9.5, 8.5, 7.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 7.5, 8.5],\n\t\t\t\"to\": [9.5, 8.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.5, 7.5, 8.5],\n\t\t\t\"to\": [7.5, 8.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 3, 1, 5], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 2], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 8.5, 8.5],\n\t\t\t\"to\": [8.5, 9.5, 9.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.5, 8.5, 6.5],\n\t\t\t\"to\": [8.5, 9.5, 7.5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6.5, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 3, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 2, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 6.5, 6.5],\n\t\t\t\"to\": [9.5, 7.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.5, 8.5, 6.5],\n\t\t\t\"to\": [9.5, 9.5, 9.5],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [0, 1, 4, 2], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [0, 2, 4, 3], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/item_pipe_side_mk1.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"1\": \"indrev:block/item_pipe_side_mk1\",\n\t\t\"particle\": \"indrev:block/item_pipe_side_mk1\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6.51, 0, 8.51],\n\t\t\t\"to\": [7.49, 6.5, 9.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.51, 0, 6.51],\n\t\t\t\"to\": [7.49, 6.5, 7.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.51, 0, 8.51],\n\t\t\t\"to\": [9.49, 6.5, 9.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.51, 0, 6.51],\n\t\t\t\"to\": [9.49, 6.5, 7.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/item_pipe_side_mk2.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"1\": \"indrev:block/item_pipe_side_mk2\",\n\t\t\"particle\": \"indrev:block/item_pipe_side_mk2\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6.51, 0, 8.51],\n\t\t\t\"to\": [7.49, 6.5, 9.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.51, 0, 6.51],\n\t\t\t\"to\": [7.49, 6.5, 7.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.51, 0, 8.51],\n\t\t\t\"to\": [9.49, 6.5, 9.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.51, 0, 6.51],\n\t\t\t\"to\": [9.49, 6.5, 7.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/item_pipe_side_mk3.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"1\": \"indrev:block/item_pipe_side_mk3\",\n\t\t\"particle\": \"indrev:block/item_pipe_side_mk3\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6.51, 0, 8.51],\n\t\t\t\"to\": [7.49, 6.5, 9.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.51, 0, 6.51],\n\t\t\t\"to\": [7.49, 6.5, 7.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.51, 0, 8.51],\n\t\t\t\"to\": [9.49, 6.5, 9.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.51, 0, 6.51],\n\t\t\t\"to\": [9.49, 6.5, 7.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/item_pipe_side_mk4.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"1\": \"indrev:block/item_pipe_side_mk4\",\n\t\t\"particle\": \"indrev:block/item_pipe_side_mk4\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6.51, 0, 8.51],\n\t\t\t\"to\": [7.49, 6.5, 9.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.51, 0, 6.51],\n\t\t\t\"to\": [7.49, 6.5, 7.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.51, 0, 8.51],\n\t\t\t\"to\": [9.49, 6.5, 9.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.51, 0, 6.51],\n\t\t\t\"to\": [9.49, 6.5, 7.49],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"z\", \"origin\": [8, 2.625, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 0, 2, 6], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 1, 6], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [3, 0, 4, 6], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [2, 0, 3, 6], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [1, 1, 0, 0], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [12, 4, 8, 8], \"texture\": \"#1\"}\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/laser.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"texture_size\": [64, 64],\n\t\"parent\": \"block/block\",\n\t\"textures\": {\n\t\t\"5\": \"indrev:block/laser\",\n\t\t\"particle\": \"indrev:block/laser\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 3],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 4, 4], \"texture\": \"#5\"},\n\t\t\t\t\"east\": {\"uv\": [4, 4, 7.25, 8], \"texture\": \"#5\"},\n\t\t\t\t\"south\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#5\"},\n\t\t\t\t\"west\": {\"uv\": [4, 0, 7.25, 4], \"texture\": \"#5\"},\n\t\t\t\t\"up\": {\"uv\": [11.25, 8, 7.25, 4.75], \"texture\": \"#5\"},\n\t\t\t\t\"down\": {\"uv\": [11.25, 0, 7.25, 3.25], \"texture\": \"#5\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6, 6, 0],\n\t\t\t\"to\": [10, 10, 2],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6, 8, 4]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [3.5, 11.5, 4.5, 12.5], \"texture\": \"#5\"},\n\t\t\t\t\"east\": {\"uv\": [3, 11.5, 3.5, 12.5], \"texture\": \"#5\"},\n\t\t\t\t\"west\": {\"uv\": [4.5, 11.5, 5, 12.5], \"texture\": \"#5\"},\n\t\t\t\t\"up\": {\"uv\": [4.5, 11.5, 3.5, 11], \"texture\": \"#5\"},\n\t\t\t\t\"down\": {\"uv\": [4.5, 11, 3.5, 11.5], \"texture\": \"#5\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 11, 1],\n\t\t\t\"to\": [11, 15, 4],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"x\", \"origin\": [8, 13.5, 2]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [3.25, 9.5, 4.75, 10.5], \"texture\": \"#5\"},\n\t\t\t\t\"east\": {\"uv\": [2.5, 9.5, 3.25, 10.5], \"texture\": \"#5\"},\n\t\t\t\t\"west\": {\"uv\": [4.75, 9.5, 5.5, 10.5], \"texture\": \"#5\"},\n\t\t\t\t\"up\": {\"uv\": [4.75, 9.5, 3.25, 8.75], \"texture\": \"#5\"},\n\t\t\t\t\"down\": {\"uv\": [4.75, 13, 3.25, 13.75], \"texture\": \"#5\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 1, 1],\n\t\t\t\"to\": [11, 5, 4],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"x\", \"origin\": [8, 2.5, 2]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [3.25, 13.5, 4.75, 14.5], \"texture\": \"#5\"},\n\t\t\t\t\"east\": {\"uv\": [2.5, 13.5, 3.25, 14.5], \"texture\": \"#5\"},\n\t\t\t\t\"west\": {\"uv\": [4.75, 13.5, 5.5, 14.5], \"texture\": \"#5\"},\n\t\t\t\t\"up\": {\"uv\": [4.75, 13.75, 3.25, 13], \"texture\": \"#5\"},\n\t\t\t\t\"down\": {\"uv\": [4.75, 14.75, 3.25, 15.5], \"texture\": \"#5\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [1, 5, 1],\n\t\t\t\"to\": [5, 11, 4],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"y\", \"origin\": [2.5, 8, 2]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [5.75, 11.25, 6.75, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"east\": {\"uv\": [5, 11.25, 5.75, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"west\": {\"uv\": [6.75, 11.25, 7.5, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"up\": {\"uv\": [6.5, 11.25, 5.5, 10.5], \"texture\": \"#5\"},\n\t\t\t\t\"down\": {\"uv\": [6.5, 12.75, 5.5, 13.5], \"texture\": \"#5\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [11, 5, 1],\n\t\t\t\"to\": [15, 11, 4],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"y\", \"origin\": [13.5, 8, 2]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1.25, 11.25, 2.25, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"east\": {\"uv\": [0.5, 11.25, 1.25, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"west\": {\"uv\": [2.25, 11.25, 3, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"up\": {\"uv\": [2.5, 11.25, 1.5, 10.5], \"texture\": \"#5\"},\n\t\t\t\t\"down\": {\"uv\": [2.5, 12.75, 1.5, 13.5], \"texture\": \"#5\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/laser_on.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"block/block\",\n\t\"texture_size\": [32, 32],\n\t\"textures\": {\n\t\t\"1\": \"indrev:block/laser_emissive\",\n\t\t\"5\": \"indrev:block/laser_on\",\n\t\t\"particle\": \"indrev:block/laser_on\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 3],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 4, 4], \"texture\": \"#5\"},\n\t\t\t\t\"east\": {\"uv\": [4, 4, 7.25, 8], \"texture\": \"#5\"},\n\t\t\t\t\"south\": {\"uv\": [0, 4, 4, 8], \"texture\": \"#5\"},\n\t\t\t\t\"west\": {\"uv\": [4, 0, 7.25, 4], \"texture\": \"#5\"},\n\t\t\t\t\"up\": {\"uv\": [11.25, 8, 7.25, 4.75], \"texture\": \"#5\"},\n\t\t\t\t\"down\": {\"uv\": [11.25, 0, 7.25, 3.25], \"texture\": \"#5\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6, 6, 0],\n\t\t\t\"to\": [10, 10, 2],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [6, 8, 4]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [3, 11, 5, 13], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"},\n\t\t\t\t\"east\": {\"uv\": [2, 11, 3, 13], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"},\n\t\t\t\t\"west\": {\"uv\": [5, 11, 6, 13], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"},\n\t\t\t\t\"up\": {\"uv\": [5, 11, 3, 10], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"},\n\t\t\t\t\"down\": {\"uv\": [5, 10, 3, 11], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 11, 1],\n\t\t\t\"to\": [11, 15, 4],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"x\", \"origin\": [8, 13.5, 2]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [3.25, 9.5, 4.75, 10.5], \"texture\": \"#5\"},\n\t\t\t\t\"east\": {\"uv\": [2.5, 9.5, 3.25, 10.5], \"texture\": \"#5\"},\n\t\t\t\t\"west\": {\"uv\": [4.75, 9.5, 5.5, 10.5], \"texture\": \"#5\"},\n\t\t\t\t\"up\": {\"uv\": [4.75, 9.5, 3.25, 8.75], \"texture\": \"#5\"},\n\t\t\t\t\"down\": {\"uv\": [4.75, 13, 3.25, 13.75], \"texture\": \"#5\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5, 1, 1],\n\t\t\t\"to\": [11, 5, 4],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"x\", \"origin\": [8, 2.5, 2]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [3.25, 13.5, 4.75, 14.5], \"texture\": \"#5\"},\n\t\t\t\t\"east\": {\"uv\": [2.5, 13.5, 3.25, 14.5], \"texture\": \"#5\"},\n\t\t\t\t\"west\": {\"uv\": [4.75, 13.5, 5.5, 14.5], \"texture\": \"#5\"},\n\t\t\t\t\"up\": {\"uv\": [4.75, 13.75, 3.25, 13], \"texture\": \"#5\"},\n\t\t\t\t\"down\": {\"uv\": [4.75, 14.75, 3.25, 15.5], \"texture\": \"#5\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [1, 5, 1],\n\t\t\t\"to\": [5, 11, 4],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"y\", \"origin\": [2.5, 8, 2]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [5.75, 11.25, 6.75, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"east\": {\"uv\": [5, 11.25, 5.75, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"west\": {\"uv\": [6.75, 11.25, 7.5, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"up\": {\"uv\": [6.5, 11.25, 5.5, 10.5], \"texture\": \"#5\"},\n\t\t\t\t\"down\": {\"uv\": [6.5, 12.75, 5.5, 13.5], \"texture\": \"#5\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [11, 5, 1],\n\t\t\t\"to\": [15, 11, 4],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"y\", \"origin\": [13.5, 8, 2]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1.25, 11.25, 2.25, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"east\": {\"uv\": [0.5, 11.25, 1.25, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"west\": {\"uv\": [2.25, 11.25, 3, 12.75], \"texture\": \"#5\"},\n\t\t\t\t\"up\": {\"uv\": [2.5, 11.25, 1.5, 10.5], \"texture\": \"#5\"},\n\t\t\t\t\"down\": {\"uv\": [2.5, 12.75, 1.5, 13.5], \"texture\": \"#5\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [4, 4, 2.95],\n\t\t\t\"to\": [12, 12, 2.95],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [12, 12, 9.975]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [2, 2, 6, 6], \"texture\": \"#1\", \"jmx_tex0\": \"#1\", \"jmx_material\": \"#jmx_0\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"jmx\": {\n\t\t\"textures\": {\n\t\t\t\"1\": \"indrev:block/laser_emissive\",\n\t\t\t\"5\": \"indrev:block/laser_on\",\n\t\t\t\"particle\": \"indrev:block/laser_on\"\n\t\t},\n\t\t\"materials\": {\n\t\t\t\"jmx_0\": {\n\t\t\t\t\"layer0\": \"cutout\",\n\t\t\t\t\"emissive0\": true,\n\t\t\t\t\"ambient_occlusion0\": false,\n\t\t\t\t\"diffuse0\": false\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/lazuli_flux_container.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"block/block\",\n\t\"texture_size\": [64, 64],\n\t\"textures\": {\n\t\t\"particle\": \"indrev:block/lazuli_flux_container\",\n\t\t\"texture\": \"indrev:block/lazuli_flux_container\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 8, 8, 16], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [0, 8, 8, 16], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [0, 8, 8, 16], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [0, 8, 8, 16], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [8, 8, 0, 0], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [16, 0, 8, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/lazuli_flux_container_input.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"parent\": \"block/block\",\n  \"textures\": {\n    \"texture\": \"indrev:block/lazuli_flux_container_input\"\n  },\n  \"elements\": [\n    {\n      \"from\": [0, 0, -0.01],\n      \"to\": [16, 16, 16],\n      \"faces\": {\n        \"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#texture\"}\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/lazuli_flux_container_item_lf_level.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"parent\": \"block/block\",\n  \"textures\": {\n    \"texture\": \"indrev:block/lazuli_flux_container_item_lf_level\"\n  },\n  \"elements\": [\n    {\n      \"from\": [0, 0, -0.01],\n      \"to\": [16, 16, 16],\n      \"faces\": {\n        \"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#texture\"}\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/lazuli_flux_container_mk1_overlay.json",
    "content": "{\n  \"textures\": {\n    \"texture\": \"indrev:block/lazuli_flux_container_mk1_overlay\"\n  },\n  \"elements\": [\n    {\n      \"from\": [0, 0, -0.01],\n      \"to\": [16, 16, 16],\n      \"faces\": {\n        \"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#texture\", \"jmx_tex1\": \"#texture\", \"jmx_material\": \"#jmx_1\"}\n      }\n    }\n  ],\n\n  \"jmx\": {\n    \"textures\": {\n      \"texture\": \"indrev:block/lazuli_flux_container_mk1_overlay\"\n    },\n    \"materials\": {\n      \"jmx_1\": {\n        \"layer1\": \"cutout\",\n        \"emissive1\": true,\n        \"ambient_occlusion1\": false,\n        \"diffuse1\": false\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/lazuli_flux_container_mk2_overlay.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"parent\": \"block/block\",\n  \"textures\": {\n    \"texture\": \"indrev:block/lazuli_flux_container_mk2_overlay\"\n  },\n  \"elements\": [\n    {\n      \"from\": [0, 0, -0.01],\n      \"to\": [16, 16, 16],\n      \"faces\": {\n        \"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#texture\", \"jmx_tex1\": \"#texture\", \"jmx_material\": \"#jmx_1\"}\n      }\n    }\n  ],\n\n  \"jmx\": {\n    \"textures\": {\n      \"texture\": \"indrev:block/lazuli_flux_container_mk2_overlay\"\n    },\n    \"materials\": {\n      \"jmx_1\": {\n        \"layer1\": \"cutout\",\n        \"emissive1\": true,\n        \"ambient_occlusion1\": false,\n        \"diffuse1\": false\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/lazuli_flux_container_mk3_overlay.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"parent\": \"block/block\",\n  \"textures\": {\n    \"texture\": \"indrev:block/lazuli_flux_container_mk3_overlay\"\n  },\n  \"elements\": [\n    {\n      \"from\": [0, 0, -0.01],\n      \"to\": [16, 16, 16],\n      \"faces\": {\n        \"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#texture\", \"jmx_tex1\": \"#texture\", \"jmx_material\": \"#jmx_1\"}\n      }\n    }\n  ],\n\n  \"jmx\": {\n    \"textures\": {\n      \"texture\": \"indrev:block/lazuli_flux_container_mk3_overlay\"\n    },\n    \"materials\": {\n      \"jmx_1\": {\n        \"layer1\": \"cutout\",\n        \"emissive1\": true,\n        \"ambient_occlusion1\": false,\n        \"diffuse1\": false\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/lazuli_flux_container_mk4_overlay.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"parent\": \"block/block\",\n  \"textures\": {\n    \"texture\": \"indrev:block/lazuli_flux_container_mk4_overlay\"\n  },\n  \"elements\": [\n    {\n      \"from\": [0, 0, -0.01],\n      \"to\": [16, 16, 16],\n      \"faces\": {\n        \"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#texture\", \"jmx_tex1\": \"#texture\", \"jmx_material\": \"#jmx_1\"}\n      }\n    }\n  ],\n\n  \"jmx\": {\n    \"textures\": {\n      \"texture\": \"indrev:block/lazuli_flux_container_mk4_overlay\"\n    },\n    \"materials\": {\n      \"jmx_1\": {\n        \"layer1\": \"cutout\",\n        \"emissive1\": true,\n        \"ambient_occlusion1\": false,\n        \"diffuse1\": false\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/lazuli_flux_container_output.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"parent\": \"block/block\",\n  \"textures\": {\n    \"texture\": \"indrev:block/lazuli_flux_container_output\"\n  },\n  \"elements\": [\n    {\n      \"from\": [0, 0, -0.01],\n      \"to\": [16, 16, 16],\n      \"faces\": {\n        \"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#texture\"}\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/lead_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/lead_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/lead_ore.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/lead_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/machine.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"parent\": \"minecraft:block/block\",\n  \"textures\": {\n    \"particle\": \"minecraft:block/iron_block\"\n  },\n  \"elements\": [\n    {\n      \"from\": [\n        0,\n        0,\n        0\n      ],\n      \"to\": [\n        16,\n        16,\n        16\n      ],\n      \"faces\": {\n        \"north\": {\n          \"uv\": [\n            5.333,\n            5.333,\n            10.666,\n            10.666\n          ],\n          \"texture\": \"#texture\",\n          \"cullface\": \"north\"\n        },\n        \"east\": {\n          \"uv\": [\n            0,\n            5.333,\n            5.332,\n            10.666\n          ],\n          \"texture\": \"#texture\",\n          \"cullface\": \"east\"\n        },\n        \"south\": {\n          \"uv\": [\n            10.667,\n            10.667,\n            16,\n            15.999\n          ],\n          \"texture\": \"#texture\",\n          \"cullface\": \"south\"\n        },\n        \"west\": {\n          \"uv\": [\n            10.667,\n            5.333,\n            16,\n            10.665\n          ],\n          \"texture\": \"#texture\",\n          \"cullface\": \"west\"\n        },\n        \"up\": {\n          \"uv\": [\n            5.333,\n            0,\n            10.666,\n            5.333\n          ],\n          \"texture\": \"#texture\",\n          \"cullface\": \"up\"\n        },\n        \"down\": {\n          \"uv\": [\n            5.333,\n            10.667,\n            10.666,\n            15.998\n          ],\n          \"texture\": \"#texture\",\n          \"cullface\": \"down\"\n        }\n      }\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/machine_block.json",
    "content": "{\n  \"parent\": \"indrev:block/machine\",\n  \"textures\": {\n    \"texture\": \"indrev:block/machine_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/modular_workbench_mk4.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"block/cube_all\",\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/machine_block\",\n\t\t\"2\": \"indrev:block/capsule\",\n\t\t\"particle\": \"indrev:block/machine_block\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 1, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [5.33333, 10.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [5.33333, 10.66667, 10.66667, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [1, 1, 1],\n\t\t\t\"to\": [15, 2, 15],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [5.66667, 11, 10.33333, 15.66667], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [5.66667, 11, 10.33333, 15.66667], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0, 15, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [5.33333, 10.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [5.33333, 10.66667, 10.66667, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [1, 14, 1],\n\t\t\t\"to\": [15, 15, 15],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [5.33333, 15.66667, 10.66667, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [5.66667, 11, 10.33333, 15.66667], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [5.66667, 11, 10.33333, 15.66667], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [2, 2, 13],\n\t\t\t\"to\": [3, 14, 14],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [13, 2, 2],\n\t\t\t\"to\": [14, 14, 3],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [13, 2, 13],\n\t\t\t\"to\": [14, 14, 14],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [2, 2, 2],\n\t\t\t\"to\": [3, 14, 3],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 10.33333, 5.33333, 10.66667], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [3, 2, 3],\n\t\t\t\"to\": [13, 14, 13],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [3, 2, 13, 14], \"texture\": \"#2\"},\n\t\t\t\t\"east\": {\"uv\": [3, 2, 13, 14], \"texture\": \"#2\"},\n\t\t\t\t\"south\": {\"uv\": [3, 2, 13, 14], \"texture\": \"#2\"},\n\t\t\t\t\"west\": {\"uv\": [3, 2, 13, 14], \"texture\": \"#2\"},\n\t\t\t\t\"up\": {\"uv\": [3, 2, 13, 14], \"texture\": \"#2\"},\n\t\t\t\t\"down\": {\"uv\": [3, 2, 13, 14], \"texture\": \"#2\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/netherite_drill_head.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"texture_size\": [32, 32],\n\t\"textures\": {\n\t\t\"0\": \"indrev:item/netherite_drill_head\",\n\t\t\"particle\": \"indrev:item/netherite_drill_head\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [2.5, 6, 8.01],\n\t\t\t\"to\": [14.5, 18, 8.01],\n\t\t\t\"rotation\": {\"angle\": 45, \"axis\": \"z\", \"origin\": [8, 11.5, 8.01]},\n\t\t\t\"faces\": {\n\t\t\t\t\"south\": {\"uv\": [2, 3, 13, 14], \"rotation\": 270, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [2.5, 6, 7.99],\n\t\t\t\"to\": [14.5, 18, 7.99],\n\t\t\t\"rotation\": {\"angle\": 45, \"axis\": \"z\", \"origin\": [8, 11.5, 7.99]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [2, 3, 13, 14], \"rotation\": 180, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.99, 6, 2.5],\n\t\t\t\"to\": [7.99, 18, 14.5],\n\t\t\t\"rotation\": {\"angle\": -45, \"axis\": \"x\", \"origin\": [7.99, 11.5, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"west\": {\"uv\": [2, 3, 13, 14], \"rotation\": 270, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.01, 6, 2.5],\n\t\t\t\"to\": [8.01, 18, 14.5],\n\t\t\t\"rotation\": {\"angle\": -45, \"axis\": \"x\", \"origin\": [8.01, 11.5, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [2, 3, 13, 14], \"rotation\": 180, \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/nikolite_ore.json",
    "content": "{\n  \"parent\": \"minecraft:block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/nikolite_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/ore_base.json",
    "content": "{\n    \"parent\": \"minecraft:block/cube_all\",\n    \"textures\": {\n      \"all\": \"indrev:block/ore_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/ore_highlight.json",
    "content": "{\n    \"parent\": \"minecraft:block/cube_all\",\n    \"textures\": {\n      \"all\": \"indrev:block/ore_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/plank_block.json",
    "content": "{\n  \"parent\": \"minecraft:block/cube\",\n  \"textures\": {\n    \"up\": \"block/oak_planks\",\n    \"down\": \"block/oak_planks\",\n    \"north\": \"indrev:block/plank_side\",\n    \"south\": \"indrev:block/plank_side\",\n    \"east\": \"indrev:block/plank_side\",\n    \"west\": \"indrev:block/plank_side\",\n    \"particle\": \"indrev:block/plank_side\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/planks_height10.json",
    "content": "{\n  \"parent\": \"block/thin_block\",\n  \"textures\": {\n    \"particle\": \"indrev:block/plank_side\",\n    \"texture\": \"indrev:block/plank_side\",\n    \"top\": \"block/oak_planks\"\n  },\n  \"elements\": [\n    {\n     \"from\": [ 0, 0, 0 ],\n      \"to\": [ 16, 10, 16 ],\n      \"faces\": {\n        \"down\":  { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\", \"cullface\": \"down\" },\n        \"up\":    { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\" },\n        \"north\": { \"uv\": [ 0, 6, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n        \"south\": { \"uv\": [ 0, 6, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n        \"west\":  { \"uv\": [ 0, 6, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n        \"east\":  { \"uv\": [ 0, 6, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/planks_height12.json",
    "content": "{\n  \"parent\": \"block/thin_block\",\n  \"textures\": {\n    \"particle\": \"indrev:block/plank_side\",\n    \"texture\": \"indrev:block/plank_side\",\n    \"top\": \"block/oak_planks\"\n  },\n  \"elements\": [\n    {\n      \"from\": [ 0, 0, 0 ],\n      \"to\": [ 16, 12, 16 ],\n      \"faces\": {\n        \"down\":  { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\", \"cullface\": \"down\" },\n        \"up\":    { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\" },\n        \"north\": { \"uv\": [ 0, 4, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n        \"south\": { \"uv\": [ 0, 4, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n        \"west\":  { \"uv\": [ 0, 4, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n        \"east\":  { \"uv\": [ 0, 4, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/planks_height14.json",
    "content": "{\n  \"parent\": \"block/thin_block\",\n  \"textures\": {\n    \"particle\": \"indrev:block/plank_side\",\n    \"texture\": \"indrev:block/plank_side\",\n    \"top\": \"block/oak_planks\"\n  },\n  \"elements\": [\n    {\n      \"from\": [ 0, 0, 0 ],\n      \"to\": [ 16, 14, 16 ],\n      \"faces\": {\n        \"down\":  { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\", \"cullface\": \"down\" },\n        \"up\":    { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\" },\n        \"north\": { \"uv\": [ 0, 2, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n        \"south\": { \"uv\": [ 0, 2, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n        \"west\":  { \"uv\": [ 0, 2, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n        \"east\":  { \"uv\": [ 0, 2, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/planks_height2.json",
    "content": "{\n  \"parent\": \"block/thin_block\",\n  \"textures\": {\n    \"particle\": \"indrev:block/plank_side\",\n    \"texture\": \"indrev:block/plank_side\",\n    \"top\": \"block/oak_planks\"\n  },\n  \"elements\": [\n    {   \"from\": [ 0, 0, 0 ],\n      \"to\": [ 16, 2, 16 ],\n      \"faces\": {\n        \"down\":  { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\", \"cullface\": \"down\" },\n        \"up\":    { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\" },\n        \"north\": { \"uv\": [ 0, 14, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n        \"south\": { \"uv\": [ 0, 14, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n        \"west\":  { \"uv\": [ 0, 14, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n        \"east\":  { \"uv\": [ 0, 14, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/planks_height4.json",
    "content": "{\n  \"parent\": \"block/thin_block\",\n  \"textures\": {\n    \"particle\": \"indrev:block/plank_side\",\n    \"texture\": \"indrev:block/plank_side\",\n    \"top\": \"block/oak_planks\"\n  },\n  \"elements\": [\n    {\n      \"from\": [ 0, 0, 0 ],\n      \"to\": [ 16, 4, 16 ],\n      \"faces\": {\n        \"down\":  { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\", \"cullface\": \"down\" },\n        \"up\":    { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\" },\n        \"north\": { \"uv\": [ 0, 12, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n        \"south\": { \"uv\": [ 0, 12, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n        \"west\":  { \"uv\": [ 0, 12, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n        \"east\":  { \"uv\": [ 0, 12, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/planks_height6.json",
    "content": "{\n  \"parent\": \"block/thin_block\",\n  \"textures\": {\n    \"particle\": \"indrev:block/plank_side\",\n    \"texture\": \"indrev:block/plank_side\",\n    \"top\": \"block/oak_planks\"\n  },\n  \"elements\": [\n    {   \"from\": [ 0, 0, 0 ],\n      \"to\": [ 16, 6, 16 ],\n      \"faces\": {\n        \"down\":  { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\", \"cullface\": \"down\" },\n        \"up\":    { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\" },\n        \"north\": { \"uv\": [ 0, 10, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n        \"south\": { \"uv\": [ 0, 10, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n        \"west\":  { \"uv\": [ 0, 10, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n        \"east\":  { \"uv\": [ 0, 10, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/planks_height8.json",
    "content": "{\n  \"parent\": \"block/thin_block\",\n  \"textures\": {\n    \"particle\": \"indrev:block/plank_side\",\n    \"texture\": \"indrev:block/plank_side\",\n    \"top\": \"block/oak_planks\"\n  },\n  \"elements\": [\n    {\n      \"from\": [ 0, 0, 0 ],\n      \"to\": [ 16, 8, 16 ],\n      \"faces\": {\n        \"down\":  { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\", \"cullface\": \"down\" },\n        \"up\":    { \"uv\": [ 0, 0, 16, 16 ], \"texture\": \"#top\" },\n        \"north\": { \"uv\": [ 0, 8, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"north\" },\n        \"south\": { \"uv\": [ 0, 8, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"south\" },\n        \"west\":  { \"uv\": [ 0, 8, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"west\" },\n        \"east\":  { \"uv\": [ 0, 8, 16, 16 ], \"texture\": \"#texture\", \"cullface\": \"east\" }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/pump_mk1.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"block/block\",\n\t\"textures\": {\n\t\t\"particle\": \"indrev:block/pump\",\n\t\t\"texture\": \"indrev:block/pump\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"name\": \"body\",\n\t\t\t\"from\": [6, 0, 6],\n\t\t\t\"to\": [10, 11, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [4, 0, 6, 5.5], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [4, 0, 6, 5.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [4, 0, 6, 5.5], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [4, 0, 6, 5.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [3.999, 1.999, 2.001, 0.001], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4, 5.5, 2, 7.5], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"name\": \"motor\",\n\t\t\t\"from\": [5, 11, 5],\n\t\t\t\"to\": [11, 16, 11],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [6, 0, 9.5, 2.5], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [6, 0, 9.5, 2.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [6, 0, 9.5, 2.5], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [6, 0, 9.5, 2.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [9, 5.5, 6, 2.5], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [9, 5.5, 6, 8.5], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"name\": \"outlet\",\n\t\t\t\"from\": [10, 6, 6],\n\t\t\t\"to\": [15, 10, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [4.5, 8.5, 7, 10.5], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [5, 11.5, 7, 13.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [2, 8.5, 4.5, 10.5], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [7, 11.5, 9, 13.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [9.5, 10.5, 7, 8.5], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4.5, 10.5, 2, 12.5], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"name\": \"backplate\",\n\t\t\t\"from\": [15, 5, 5],\n\t\t\t\"to\": [16, 11, 11],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [9.5, 2.5, 10, 5.5], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [9, 5.5, 6, 2.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [9.5, 2.5, 10, 5.5], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [6, 5.5, 9, 8.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [10.5, 5.5, 10, 2.5], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [11, 2.5, 10.5, 5.5], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"name\": \"support\",\n\t\t\t\"from\": [10, 3, 8],\n\t\t\t\"to\": [12, 6, 8],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [9, 5.5, 10, 7], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 0, 1.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [10, 5.5, 11, 7], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 0, 1.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [1, 0, 0, 0], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [1, 0, 0, 0], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [3.30395, 4.79883, 6.99134],\n\t\t\t\"to\": [5.30395, 9.79883, 6.99134],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"z\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [3.30395, 9.79883, 6.99134],\n\t\t\t\"to\": [5.30395, 9.79883, 8.99134],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"z\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"up\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"rotation\": 90, \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"rotation\": 270, \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [3.30395, 4.89883, 6.99134],\n\t\t\t\"to\": [5.30395, 4.89883, 8.99134],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"z\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"up\": {\"uv\": [4.5, 2, 5, 2.5], \"rotation\": 90, \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"rotation\": 270, \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [5.30395, 4.79883, 6.99134],\n\t\t\t\"to\": [5.30395, 9.79883, 8.99134],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"z\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [3.30395, 4.79883, 6.99134],\n\t\t\t\"to\": [3.30395, 9.79883, 8.99134],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"z\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [3.30395, 4.79883, 8.99134],\n\t\t\t\"to\": [5.30395, 9.79883, 8.99134],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"z\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"name\": \"inner\",\n\t\t\t\"from\": [3.35395, 4.74883, 7.04134],\n\t\t\t\"to\": [5.25395, 9.64883, 8.94134],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"z\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [4.5, 10.5, 5.5, 11.5], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"rotation\": 270, \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [9, 4.92858, 3.3211],\n\t\t\t\"to\": [9, 9.92858, 5.3211],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7, 9.92858, 3.3211],\n\t\t\t\"to\": [9, 9.92858, 5.3211],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"up\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"rotation\": 180, \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"rotation\": 180, \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7, 5.02858, 3.3211],\n\t\t\t\"to\": [9, 5.02858, 5.3211],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"up\": {\"uv\": [4.5, 2, 5, 2.5], \"rotation\": 180, \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"rotation\": 180, \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7, 4.92858, 5.3211],\n\t\t\t\"to\": [9, 9.92858, 5.3211],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7, 4.92858, 3.3211],\n\t\t\t\"to\": [9, 9.92858, 3.3211],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7, 4.92858, 3.3211],\n\t\t\t\"to\": [7, 9.92858, 5.3211],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"name\": \"inner\",\n\t\t\t\"from\": [7.05, 4.97858, 3.3711],\n\t\t\t\"to\": [8.95, 9.87858, 5.2711],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [4.5, 10.5, 5.5, 11.5], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"rotation\": 180, \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7, 4.92637, 10.66202],\n\t\t\t\"to\": [7, 9.92637, 12.66202],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7, 9.92637, 10.66202],\n\t\t\t\"to\": [9, 9.92637, 12.66202],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"up\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7, 5.02637, 10.66202],\n\t\t\t\"to\": [9, 5.02637, 12.66202],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"up\": {\"uv\": [4.5, 2, 5, 2.5], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7, 4.92637, 10.66202],\n\t\t\t\"to\": [9, 9.92637, 10.66202],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7, 4.92637, 12.66202],\n\t\t\t\"to\": [9, 9.92637, 12.66202],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [9, 4.92637, 10.66202],\n\t\t\t\"to\": [9, 9.92637, 12.66202],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [5, 5.5, 6, 8], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [4, 5.5, 5, 8], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"name\": \"inner\",\n\t\t\t\"from\": [7.05, 4.97637, 10.71202],\n\t\t\t\"to\": [8.95, 9.87637, 12.61202],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [4.5, 10.5, 5.5, 12.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [4.5, 10.5, 5.5, 11.5], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4.5, 1.5, 5.5, 2.5], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.01, 2.98652, 10.38077],\n\t\t\t\"to\": [9.99, 4.96652, 12.76077],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [4, 3, 6, 4], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [1, 7.5, 2, 8.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [0, 6.5, 2, 7.5], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [0, 7.5, 1, 8.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [2, 6.5, 0, 5.5], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4, 7.5, 2, 8.5], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [3.2132, 2.85568, 6.01],\n\t\t\t\"to\": [5.5932, 4.83568, 9.99],\n\t\t\t\"rotation\": {\"angle\": 22.5, \"axis\": \"z\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 7.5, 1, 8.5], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [5, 13.5, 7, 14.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [1, 7.5, 2, 8.5], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [0, 6.5, 2, 7.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [1, 10.5, 0, 8.5], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [2, 8.5, 1, 10.5], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6.01, 2.98211, 3.23835],\n\t\t\t\"to\": [9.99, 4.96211, 5.61835],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"x\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 6.5, 2, 7.5], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [0, 7.5, 1, 8.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [14, 5, 16, 6], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [1, 7.5, 2, 8.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [2, 7, 0, 6], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [4, 7.5, 2, 8.5], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [10.36875, 3.11839, 6.01],\n\t\t\t\"to\": [12.74875, 5.09839, 9.99],\n\t\t\t\"rotation\": {\"angle\": -22.5, \"axis\": \"z\", \"origin\": [7.66385, 5.81363, 7.99423]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [1, 7.5, 2, 8.5], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [0, 6.5, 2, 7.5], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [0, 7.5, 1, 8.5], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [14, 10.5, 16, 11.5], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [1, 12.5, 0, 10.5], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [2, 8.5, 1, 10.5], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 135, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0],\n\t\t\t\"scale\": [0.8, 0.8, 0.8]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t},\n\t\"groups\": [0, 1, 2, 3, 4,\n\t\t{\n\t\t\t\"name\": \"cylinder\",\n\t\t\t\"origin\": [9, 5.5, 15],\n\t\t\t\"children\": [5, 6, 7, 8, 9, 10, 11]\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cylinder\",\n\t\t\t\"origin\": [9, 5.5, 15],\n\t\t\t\"children\": [12, 13, 14, 15, 16, 17, 18]\n\t\t},\n\t\t{\n\t\t\t\"name\": \"cylinder\",\n\t\t\t\"origin\": [9, 5.5, 15],\n\t\t\t\"children\": [19, 20, 21, 22, 23, 24, 25]\n\t\t},\n\t\t{\n\t\t\t\"name\": \"wings\",\n\t\t\t\"origin\": [9.6, 3.70711, 8.01421],\n\t\t\t\"children\": [26, 27, 28, 29]\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/pump_pipe.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"block/block\",\n\t\"texture_size\": [8, 8],\n\t\"textures\": {\n\t\t\"particle\": \"indrev:block/pump_pipe\",\n\t\t\"texture\": \"indrev:block/pump_pipe\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [6.1, 0, 6.1],\n\t\t\t\"to\": [9.9, 16, 9.9],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [16, 12, 14]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 4, 16], \"texture\": \"#texture\"},\n\t\t\t\t\"east\": {\"uv\": [4, 0, 8, 16], \"texture\": \"#texture\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 4, 16], \"texture\": \"#texture\"},\n\t\t\t\t\"west\": {\"uv\": [4, 0, 8, 16], \"texture\": \"#texture\"},\n\t\t\t\t\"up\": {\"uv\": [11, 3, 8, 0], \"texture\": \"#texture\"},\n\t\t\t\t\"down\": {\"uv\": [11, 0, 8, 3], \"texture\": \"#texture\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/raw_lead_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/raw_lead_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/raw_silver_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/raw_silver_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/raw_tin_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/raw_tin_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/raw_tungsten_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/raw_tungsten_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/servo_output.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/servo_output\",\n\t\t\"particle\": \"indrev:block/servo_output\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [4.9, -0.9, 4.9],\n\t\t\t\"to\": [11.1, 2.1, 11.1],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [12, 6, 6, 0], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [12, 0, 6, 6], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/servo_output_item.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/servo_output\",\n\t\t\"particle\": \"indrev:block/servo_output\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [5.75, 0, 5.75],\n\t\t\t\"to\": [10.25, 1.5, 10.25],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [12, 6, 6, 0], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [12, 0, 6, 6], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/servo_retriever.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/servo_retriever\",\n\t\t\"particle\": \"indrev:block/servo_retriever\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [4.9, -0.9, 4.9],\n\t\t\t\"to\": [11.1, 2.1, 11.1],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [12, 6, 6, 0], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [12, 0, 6, 6], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/servo_retriever_item.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/servo_retriever\",\n\t\t\"particle\": \"indrev:block/servo_retriever\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [5.75, 0, 5.75],\n\t\t\t\"to\": [10.25, 1.5, 10.25],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 8, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 6, 2], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [12, 6, 6, 0], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [12, 0, 6, 6], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {\n\t\t\"thirdperson_righthand\": {\n\t\t\t\"rotation\": [75, 45, 0],\n\t\t\t\"translation\": [0, 2.5, 0]\n\t\t},\n\t\t\"firstperson_righthand\": {\n\t\t\t\"rotation\": [0, 45, 0]\n\t\t},\n\t\t\"firstperson_lefthand\": {\n\t\t\t\"rotation\": [0, 225, 0]\n\t\t},\n\t\t\"ground\": {\n\t\t\t\"translation\": [0, 2, 0]\n\t\t},\n\t\t\"gui\": {\n\t\t\t\"rotation\": [30, 225, 0]\n\t\t},\n\t\t\"head\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"translation\": [0, 13, 7],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t},\n\t\t\"fixed\": {\n\t\t\t\"rotation\": [0, 180, 0],\n\t\t\t\"scale\": [2, 2, 2]\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/silo.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"minecraft:block/block\",\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/silo\",\n\t\t\"particle\": \"indrev:block/silo\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [0, 0, 0],\n\t\t\t\"to\": [16, 16, 16],\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 8, 8], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [8, 0, 16, 8], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 8, 8, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/silver_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/silver_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/silver_ore.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/silver_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/smelter_mk4.json",
    "content": "{\n  \"parent\": \"indrev:block/machine_mk4\",\n  \"textures\": {\n    \"texture\": \"indrev:block/smelter\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/solar_generator_mk1.json",
    "content": "{\n  \"parent\": \"indrev:block/machine_mk1\",\n  \"textures\": {\n    \"texture\": \"indrev:block/solar_generator\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/solar_generator_mk1_on.json",
    "content": "{\n  \"parent\": \"indrev:block/machine_mk1\",\n  \"textures\": {\n    \"texture\": \"indrev:block/solar_generator_on\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/solar_generator_mk3.json",
    "content": "{\n  \"parent\": \"indrev:block/machine_mk3\",\n  \"textures\": {\n    \"texture\": \"indrev:block/solar_generator\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/solar_generator_mk3_on.json",
    "content": "{\n  \"parent\": \"indrev:block/machine_mk3\",\n  \"textures\": {\n    \"texture\": \"indrev:block/solar_generator_on\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/steel_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/steel_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/stone_drill_head.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"texture_size\": [32, 32],\n\t\"textures\": {\n\t\t\"0\": \"indrev:item/stone_drill_head\",\n\t\t\"particle\": \"indrev:item/stone_drill_head\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [2.5, 6, 8.01],\n\t\t\t\"to\": [14.5, 18, 8.01],\n\t\t\t\"rotation\": {\"angle\": 45, \"axis\": \"z\", \"origin\": [8, 11.5, 8.01]},\n\t\t\t\"faces\": {\n\t\t\t\t\"south\": {\"uv\": [2, 3, 13, 14], \"rotation\": 270, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [2.5, 6, 7.99],\n\t\t\t\"to\": [14.5, 18, 7.99],\n\t\t\t\"rotation\": {\"angle\": 45, \"axis\": \"z\", \"origin\": [8, 11.5, 7.99]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [2, 3, 13, 14], \"rotation\": 180, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7.99, 6, 2.5],\n\t\t\t\"to\": [7.99, 18, 14.5],\n\t\t\t\"rotation\": {\"angle\": -45, \"axis\": \"x\", \"origin\": [7.99, 11.5, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"west\": {\"uv\": [2, 3, 13, 14], \"rotation\": 270, \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8.01, 6, 2.5],\n\t\t\t\"to\": [8.01, 18, 14.5],\n\t\t\t\"rotation\": {\"angle\": -45, \"axis\": \"x\", \"origin\": [8.01, 11.5, 8]},\n\t\t\t\"faces\": {\n\t\t\t\t\"east\": {\"uv\": [2, 3, 13, 14], \"rotation\": 180, \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/sulfur_crystal.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/sulfur_crystal\",\n\t\t\"particle\": \"indrev:block/sulfur_crystal\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [10, 0, 1],\n\t\t\t\"to\": [12, 1, 3],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [18, 8, 9]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [11, 0, 2],\n\t\t\t\"to\": [14, 4, 5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [19, 9, 10]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [11, 4, 2],\n\t\t\t\"to\": [13, 5, 4],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [19, 13, 10]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 2, 1], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 2, 1], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 2, 1], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 2, 1], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 2, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 2, 2], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [12, 5, 3],\n\t\t\t\"to\": [13, 6, 4],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [20, 14, 11]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [13, 0, 4],\n\t\t\t\"to\": [15, 2, 6],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [21, 9, 12]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 2, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [3, 0, 2],\n\t\t\t\"to\": [5, 1, 4],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [11, 8, 10]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [1, 0, 3],\n\t\t\t\"to\": [4, 4, 6],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 9, 11]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [2, 4, 4],\n\t\t\t\"to\": [4, 5, 6],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 13, 12]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 2, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [2, 5, 4],\n\t\t\t\"to\": [3, 6, 5],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 14, 12]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [0, 0, 5],\n\t\t\t\"to\": [2, 2, 7],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [8, 9, 13]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6, 0, 7],\n\t\t\t\"to\": [8, 2, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [14, 8, 15]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [7, 0, 8],\n\t\t\t\"to\": [12, 5, 13],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [15, 9, 16]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [8, 5, 9],\n\t\t\t\"to\": [11, 8, 12],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [16, 14, 17]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [9, 8, 10],\n\t\t\t\"to\": [10, 10, 11],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [17, 17, 18]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 3], \"texture\": \"#0\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [11, 0, 12],\n\t\t\t\"to\": [14, 3, 15],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [19, 9, 20]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 1, 1], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 16, 2], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [0, 0, 16, 16], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t],\n\t\"display\": {},\n\t\"groups\": [\n\t\t{\n\t\t\t\"name\": \"VoxelShapes\",\n\t\t\t\"origin\": [19, 9, 20],\n\t\t\t\"children\": [\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"group\",\n\t\t\t\t\t\"origin\": [19, 9, 20],\n\t\t\t\t\t\"children\": [0, 1, 2, 3, 4]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"group\",\n\t\t\t\t\t\"origin\": [19, 9, 20],\n\t\t\t\t\t\"children\": [5, 6, 7, 8, 9]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"name\": \"group\",\n\t\t\t\t\t\"origin\": [19, 9, 20],\n\t\t\t\t\t\"children\": [10, 11, 12, 13, 14]\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/tank.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"parent\": \"minecraft:block/block\",\n  \"textures\": {\n    \"3\": \"minecraft:block/glass\",\n    \"5\": \"indrev:block/machine_block\",\n    \"particle\": \"minecraft:block/glass\"\n  },\n  \"elements\": [\n    {\n      \"from\": [1, 14, 1],\n      \"to\": [15, 15, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [17, 20, 14]},\n      \"faces\": {\n        \"north\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"up\": {\"uv\": [5.333, 10.667, 10.666, 16], \"texture\": \"#5\"},\n        \"down\": {\"uv\": [5.333, 10.667, 10.666, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [1, 0, 1],\n      \"to\": [15, 1, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [17, 6, 14]},\n      \"faces\": {\n        \"north\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"up\": {\"uv\": [5.333, 10.667, 10.666, 16], \"texture\": \"#5\"},\n        \"down\": {\"uv\": [5.333, 10.667, 10.666, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [14, 1, 14],\n      \"to\": [15, 14, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 9, 23]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [1, 1, 14],\n      \"to\": [2, 14, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 9, 23]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [1, 1, 1],\n      \"to\": [2, 14, 2],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 9, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [14, 1, 1],\n      \"to\": [15, 14, 2],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 9, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [14.5, 1, 1.5],\n      \"to\": [14.7, 14, 14.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [23, 16, 11]},\n      \"faces\": {\n        \"east\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"west\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 1, 1.5],\n      \"to\": [1.7, 14, 14.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 16, 11]},\n      \"faces\": {\n        \"east\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"west\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 1, 1.5],\n      \"to\": [14.5, 14, 1.7],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 16, 11]},\n      \"faces\": {\n        \"north\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"south\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 1, 14.5],\n      \"to\": [14.5, 14, 14.7],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 16, 24]},\n      \"faces\": {\n        \"north\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"south\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    }\n  ],\n  \"groups\": [\n    {\n      \"name\": \"VoxelShapes\",\n      \"origin\": [17, 21, 14],\n      \"children\": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/tank_both.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"parent\": \"minecraft:block/block\",\n  \"textures\": {\n    \"3\": \"minecraft:block/glass\",\n    \"5\": \"indrev:block/machine_block\",\n    \"particle\": \"minecraft:block/glass\"\n  },\n  \"elements\": [\n    {\n      \"from\": [14, 0, 14],\n      \"to\": [15, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 8, 23]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [1, 0, 14],\n      \"to\": [2, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 8, 23]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [1, 0, 1],\n      \"to\": [2, 16, 2],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 8, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [14, 0, 1],\n      \"to\": [15, 16, 2],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 8, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [14.5, 0, 1.5],\n      \"to\": [14.7, 16, 14.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [23, 15, 11]},\n      \"faces\": {\n        \"east\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"west\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 0, 1.5],\n      \"to\": [1.7, 16, 14.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 15, 11]},\n      \"faces\": {\n        \"east\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"west\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 0, 1.5],\n      \"to\": [14.5, 16, 1.7],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 15, 11]},\n      \"faces\": {\n        \"north\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"south\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 0, 14.5],\n      \"to\": [14.5, 16, 14.7],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 15, 24]},\n      \"faces\": {\n        \"north\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"south\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    }\n  ],\n  \"groups\": [\n    {\n      \"name\": \"VoxelShapes\",\n      \"origin\": [17, 21, 14],\n      \"children\": [0, 1, 2, 3, 4, 5, 6, 7]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/tank_down.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"parent\": \"minecraft:block/block\",\n  \"textures\": {\n    \"3\": \"minecraft:block/glass\",\n    \"5\": \"indrev:block/machine_block\",\n    \"particle\": \"minecraft:block/glass\"\n  },\n  \"elements\": [\n    {\n      \"from\": [1, 14, 1],\n      \"to\": [15, 15, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [17, 21, 14]},\n      \"faces\": {\n        \"north\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"up\": {\"uv\": [5.333, 10.667, 10.666, 16], \"texture\": \"#5\"},\n        \"down\": {\"uv\": [5.333, 10.667, 10.666, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [14, 0, 14],\n      \"to\": [15, 15, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 8, 23]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [1, 0, 14],\n      \"to\": [2, 15, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 8, 23]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [1, 0, 1],\n      \"to\": [2, 15, 2],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 8, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [14, 0, 1],\n      \"to\": [15, 15, 2],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 8, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [14.5, 0, 1.5],\n      \"to\": [14.7, 15, 14.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [23, 15, 11]},\n      \"faces\": {\n        \"east\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"west\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 0, 1.5],\n      \"to\": [1.7, 15, 14.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 15, 11]},\n      \"faces\": {\n        \"east\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"west\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 0, 1.5],\n      \"to\": [14.5, 15, 1.7],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 15, 11]},\n      \"faces\": {\n        \"north\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"south\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 0, 14.5],\n      \"to\": [14.5, 15, 14.7],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 15, 24]},\n      \"faces\": {\n        \"north\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"south\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    }\n  ],\n  \"groups\": [\n    {\n      \"name\": \"VoxelShapes\",\n      \"origin\": [17, 21, 14],\n      \"children\": [0, 1, 2, 3, 4, 5, 6, 7, 8]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/tank_up.json",
    "content": "{\n  \"credit\": \"Made with Blockbench\",\n  \"parent\": \"minecraft:block/block\",\n  \"textures\": {\n    \"3\": \"minecraft:block/glass\",\n    \"5\": \"indrev:block/machine_block\",\n    \"particle\": \"minecraft:block/glass\"\n  },\n  \"elements\": [\n    {\n      \"from\": [1, 0, 1],\n      \"to\": [15, 1, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [17, 6, 14]},\n      \"faces\": {\n        \"north\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [5.667, 10.333, 16, 11], \"texture\": \"#5\"},\n        \"up\": {\"uv\": [5.333, 10.667, 10.666, 16], \"texture\": \"#5\"},\n        \"down\": {\"uv\": [5.333, 10.667, 10.666, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [14, 1, 14],\n      \"to\": [15, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 9, 23]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [1, 1, 14],\n      \"to\": [2, 16, 15],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 9, 23]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [1, 1, 1],\n      \"to\": [2, 16, 2],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [9, 9, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [14, 1, 1],\n      \"to\": [15, 16, 2],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [22, 9, 10]},\n      \"faces\": {\n        \"north\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"east\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"south\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"},\n        \"west\": {\"uv\": [10.333, 5.333, 11, 16], \"texture\": \"#5\"}\n      }\n    },\n    {\n      \"from\": [14.5, 1, 1.5],\n      \"to\": [14.7, 16, 14.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [23, 16, 11]},\n      \"faces\": {\n        \"east\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"west\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 1, 1.5],\n      \"to\": [1.7, 16, 14.5],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 16, 11]},\n      \"faces\": {\n        \"east\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"west\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 1, 1.5],\n      \"to\": [14.5, 16, 1.7],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 16, 11]},\n      \"faces\": {\n        \"north\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"south\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    },\n    {\n      \"from\": [1.5, 1, 14.5],\n      \"to\": [14.5, 16, 14.7],\n      \"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [10, 16, 24]},\n      \"faces\": {\n        \"north\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"},\n        \"south\": {\"uv\": [1, 1, 14, 15], \"texture\": \"#3\"}\n      }\n    }\n  ],\n  \"groups\": [\n    {\n      \"name\": \"VoxelShapes\",\n      \"origin\": [17, 21, 14],\n      \"children\": [0, 1, 2, 3, 4, 5, 6, 7, 8]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/tin_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/tin_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/tin_ore.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/tin_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/tungsten_block.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/tungsten_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/tungsten_ore.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/tungsten_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/warning_strobe.json",
    "content": "{\n\t\"credit\": \"Made with Blockbench\",\n\t\"parent\": \"block/block\",\n\t\"textures\": {\n\t\t\"0\": \"indrev:block/strobe-light\",\n\t\t\"1\": \"indrev:block/strobe-base\",\n\t\t\"particle\": \"indrev:block/strobe-light\"\n\t},\n\t\"elements\": [\n\t\t{\n\t\t\t\"from\": [5, 0, 5],\n\t\t\t\"to\": [11, 1, 11],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [12, 8, 12]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [6, 0, 12, 1], \"texture\": \"#1\"},\n\t\t\t\t\"east\": {\"uv\": [6, 0, 12, 1], \"texture\": \"#1\"},\n\t\t\t\t\"south\": {\"uv\": [6, 0, 12, 1], \"texture\": \"#1\"},\n\t\t\t\t\"west\": {\"uv\": [6, 0, 12, 1], \"texture\": \"#1\"},\n\t\t\t\t\"up\": {\"uv\": [0, 0, 6, 6], \"texture\": \"#1\"},\n\t\t\t\t\"down\": {\"uv\": [6, 0, 0, 6], \"texture\": \"#1\"}\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"from\": [6, 1, 6],\n\t\t\t\"to\": [10, 6, 10],\n\t\t\t\"rotation\": {\"angle\": 0, \"axis\": \"y\", \"origin\": [13, 9, 13]},\n\t\t\t\"faces\": {\n\t\t\t\t\"north\": {\"uv\": [0, 0, 4, 5], \"texture\": \"#0\"},\n\t\t\t\t\"east\": {\"uv\": [0, 0, 4, 5], \"texture\": \"#0\"},\n\t\t\t\t\"south\": {\"uv\": [0, 0, 4, 5], \"texture\": \"#0\"},\n\t\t\t\t\"west\": {\"uv\": [0, 0, 4, 5], \"texture\": \"#0\"},\n\t\t\t\t\"up\": {\"uv\": [8, 4, 4, 0], \"texture\": \"#0\"},\n\t\t\t\t\"down\": {\"uv\": [8, 4, 4, 0], \"texture\": \"#0\"}\n\t\t\t}\n\t\t}\n\t]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/block/wither_proof_obsidian.json",
    "content": "{\n  \"parent\": \"block/cube_all\",\n  \"textures\": {\n    \"all\": \"indrev:block/wither_proof_obsidian\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/axe_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/axe_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/axe_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/axe_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/axe_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/axe_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/battery.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/battery\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/biomass.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/biomass\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/blast_furnace_enhancer.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/blast_furnace_upgrade\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/block_base.json",
    "content": "{\n    \"parent\": \"indrev:block/block_base\"\n\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/block_highlight.json",
    "content": "{\n    \"parent\": \"indrev:block/block_highlight\"\n\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/boots_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/boots_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/boots_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/boots_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/boots_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/boots_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/broken_reinforced_elytra.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/broken_reinforced_elytra\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_axe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_block.json",
    "content": "{\n  \"parent\": \"indrev:block/bronze_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_boots.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_chestplate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_helmet.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_hoe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_ingot.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_leggings.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_nugget.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_nugget\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_pickaxe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_plate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_shovel.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/bronze_sword.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/bronze_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/buffer_enhancer.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/buffer_upgrade\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/cabinet.json",
    "content": "{\n  \"parent\": \"indrev:block/cabinet\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/cable_mk1.json",
    "content": "{\n  \"parent\": \"indrev:block/cable_mk1\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/cable_mk2.json",
    "content": "{\n  \"parent\": \"indrev:block/cable_mk2\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/cable_mk3.json",
    "content": "{\n  \"parent\": \"indrev:block/cable_mk3\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/cable_mk4.json",
    "content": "{\n  \"parent\": \"indrev:block/cable_mk4\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/capsule.json",
    "content": "{\n  \"parent\": \"indrev:block/capsule\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/charge_pad_mk4.json",
    "content": "{\n  \"parent\": \"indrev:block/charge_pad_mk4\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/chestplate_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/chestplate_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/chestplate_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/chestplate_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/chestplate_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/chestplate_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/chunk_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/chunk_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/chunk_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/chunk_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/chunk_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/chunk_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/chunk_scanner.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/chunk_scanner\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/circuit_mk1.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/circuit_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/circuit_mk2.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/circuit_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/circuit_mk3.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/circuit_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/circuit_mk4.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/circuit_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/coal_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/coal_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/controller.json",
    "content": "{\n  \"parent\": \"indrev:block/controller\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/coolant_bucket.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/coolant_bucket\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/cooler_cell.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/cooler_cell\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_axe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_block.json",
    "content": "{\n  \"parent\": \"indrev:block/copper_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_boots.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_chestplate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_chunk.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_gear.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_gear\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_helmet.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_hoe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_leggings.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_nether_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/copper_nether_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_nugget.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_nugget\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/copper_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_pickaxe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_plate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_purified_ore.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_purified_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_shovel.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/copper_sword.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/copper_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/damage_enhancer.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/damage_enhancer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/deepslate_lead_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/deepslate_lead_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/deepslate_nikolite_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/deepslate_nikolite_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/deepslate_silver_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/deepslate_silver_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/deepslate_tin_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/deepslate_tin_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/deepslate_tungsten_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/deepslate_tungsten_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/diamond_drill_head.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/diamond_drill_head\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/diamond_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/diamond_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/drain_mk1.json",
    "content": "{\n  \"parent\": \"indrev:block/drain_mk1\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/drill_bottom.json",
    "content": "{\n  \"parent\": \"indrev:block/drill\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/duct.json",
    "content": "{\n  \"parent\": \"indrev:block/duct\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/dust_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/dust_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/dust_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/dust_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/dust_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/dust_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/electrum_block.json",
    "content": "{\n  \"parent\": \"indrev:block/electrum_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/electrum_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/electrum_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/electrum_ingot.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/electrum_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/electrum_nugget.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/electrum_nugget\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/electrum_plate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/electrum_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/empty_enhancer.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/empty_upgrade\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/energy_enhancer.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/energy_upgrade\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/energy_reader.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/energy_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/enriched_nikolite_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/enriched_nikolite_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/enriched_nikolite_ingot.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/enriched_nikolite_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/fan.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/fan\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/fisher_mk2.json",
    "content": "{\n  \"parent\": \"indrev:block/fishing_farm_mk2\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/fisher_mk3.json",
    "content": "{\n  \"parent\": \"indrev:block/fishing_farm_mk3\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/fisher_mk4.json",
    "content": "{\n  \"parent\": \"indrev:block/fishing_farm_mk4\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/fluid_pipe_mk1.json",
    "content": "{\n\t\"parent\": \"indrev:block/fluid_pipe_mk1\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/fluid_pipe_mk2.json",
    "content": "{\n\t\"parent\": \"indrev:block/fluid_pipe_mk2\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/fluid_pipe_mk3.json",
    "content": "{\n\t\"parent\": \"indrev:block/fluid_pipe_mk3\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/fluid_pipe_mk4.json",
    "content": "{\n\t\"parent\": \"indrev:block/fluid_pipe_mk4\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/frame.json",
    "content": "{\n  \"parent\": \"indrev:block/frame\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gamer_axe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gamer_axe\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        30\n      ],\n      \"translation\": [\n        0,\n        7.0,\n        2.0\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"thirdperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -55\n      ],\n      \"translation\": [\n        0,\n        4.0,\n        0.5\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    }\n  },\n  \"overrides\": [\n    {\n      \"predicate\": {\n        \"indrev:activate\": 0.0\n      },\n      \"model\": \"indrev:item/gamer_axe10\"\n    },\n    {\n      \"predicate\": {\n        \"indrev:activate\": 0.1\n      },\n      \"model\": \"indrev:item/gamer_axe9\"\n    },\n    {\n      \"predicate\": {\n        \"indrev:activate\": 0.2\n      },\n      \"model\": \"indrev:item/gamer_axe8\"\n    },\n    {\n      \"predicate\": {\n        \"indrev:activate\": 0.3\n      },\n      \"model\": \"indrev:item/gamer_axe7\"\n    },\n    {\n      \"predicate\": {\n        \"indrev:activate\": 0.4\n      },\n      \"model\": \"indrev:item/gamer_axe6\"\n    },\n    {\n      \"predicate\": {\n        \"indrev:activate\": 0.5\n      },\n      \"model\": \"indrev:item/gamer_axe5\"\n    },\n    {\n      \"predicate\": {\n        \"indrev:activate\": 0.6\n      },\n      \"model\": \"indrev:item/gamer_axe4\"\n    },\n    {\n      \"predicate\": {\n        \"indrev:activate\": 0.7\n      },\n      \"model\": \"indrev:item/gamer_axe3\"\n    },\n    {\n      \"predicate\": {\n        \"indrev:activate\": 0.8\n      },\n      \"model\": \"indrev:item/gamer_axe2\"\n    },\n    {\n      \"predicate\": {\n        \"indrev:activate\": 0.9\n      },\n      \"model\": \"indrev:item/gamer_axe1\"\n    },\n    {\n      \"predicate\": {\n        \"indrev:activate\": 1.0\n      },\n      \"model\": \"indrev:item/gamer_axe\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gamer_axe1.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gamer_axe1\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        30\n      ],\n      \"translation\": [\n        0,\n        7.0,\n        2.0\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"thirdperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -55\n      ],\n      \"translation\": [\n        0,\n        4.0,\n        0.5\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gamer_axe10.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gamer_axe10\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        30\n      ],\n      \"translation\": [\n        0,\n        7.0,\n        2.0\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"thirdperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -55\n      ],\n      \"translation\": [\n        0,\n        4.0,\n        0.5\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gamer_axe2.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gamer_axe2\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        30\n      ],\n      \"translation\": [\n        0,\n        7.0,\n        2.0\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"thirdperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -55\n      ],\n      \"translation\": [\n        0,\n        4.0,\n        0.5\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gamer_axe3.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gamer_axe3\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        30\n      ],\n      \"translation\": [\n        0,\n        7.0,\n        2.0\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"thirdperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -55\n      ],\n      \"translation\": [\n        0,\n        4.0,\n        0.5\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gamer_axe4.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gamer_axe4\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        30\n      ],\n      \"translation\": [\n        0,\n        7.0,\n        2.0\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"thirdperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -55\n      ],\n      \"translation\": [\n        0,\n        4.0,\n        0.5\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gamer_axe5.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gamer_axe5\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        30\n      ],\n      \"translation\": [\n        0,\n        7.0,\n        2.0\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"thirdperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -55\n      ],\n      \"translation\": [\n        0,\n        4.0,\n        0.5\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gamer_axe6.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gamer_axe6\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        30\n      ],\n      \"translation\": [\n        0,\n        7.0,\n        2.0\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"thirdperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -55\n      ],\n      \"translation\": [\n        0,\n        4.0,\n        0.5\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gamer_axe7.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gamer_axe7\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        30\n      ],\n      \"translation\": [\n        0,\n        7.0,\n        2.0\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"thirdperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -55\n      ],\n      \"translation\": [\n        0,\n        4.0,\n        0.5\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gamer_axe8.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gamer_axe8\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        30\n      ],\n      \"translation\": [\n        0,\n        7.0,\n        2.0\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"thirdperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -55\n      ],\n      \"translation\": [\n        0,\n        4.0,\n        0.5\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gamer_axe9.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gamer_axe9\"\n  },\n  \"display\": {\n    \"thirdperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        30\n      ],\n      \"translation\": [\n        0,\n        7.0,\n        2.0\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"thirdperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -55\n      ],\n      \"translation\": [\n        0,\n        4.0,\n        0.5\n      ],\n      \"scale\": [\n        1.7,\n        1.7,\n        1.7\n      ]\n    },\n    \"firstperson_righthand\": {\n      \"rotation\": [\n        0,\n        -90,\n        25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    },\n    \"firstperson_lefthand\": {\n      \"rotation\": [\n        0,\n        90,\n        -25\n      ],\n      \"translation\": [\n        1.13,\n        3.2,\n        1.13\n      ],\n      \"scale\": [\n        1.36,\n        1.36,\n        1.36\n      ]\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gold_chunk.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gold_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gold_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gold_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gold_plate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gold_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/gold_purified_ore.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/gold_purified_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/guide_book.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/guide_book\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/hammer.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/hammer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/heat_coil.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/heat_coil\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/heat_generator_mk4.json",
    "content": "{\n  \"parent\": \"indrev:block/heat_generator_mk4\"\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/heatsink.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/heatsink\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/helmet_base.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/helmet_base\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/helmet_highlight.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/helmet_highlight\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/helmet_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/helmet_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/hoe_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/hoe_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/hoe_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/hoe_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/hoe_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/hoe_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/ingot_base.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/ingot_base\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/ingot_highlight.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/ingot_highlight\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/ingot_outline.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/ingot_outline\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/intake.json",
    "content": "{\n  \"parent\": \"indrev:block/intake\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/iron_chunk.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/iron_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/iron_drill_head.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/iron_drill_head\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/iron_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/iron_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/iron_gear.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/iron_gear\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/iron_plate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/iron_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/iron_purified_ore.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/iron_purified_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/jetpack_mk1.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/jetpack_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/jetpack_mk2.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/jetpack_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/jetpack_mk3.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/jetpack_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/jetpack_mk4.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/jetpack_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/laser_emitter_mk4.json",
    "content": "{\n  \"parent\": \"indrev:block/laser\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lazuli_flux_container_creative.json",
    "content": "{\n  \"parent\": \"indrev:block/lazuli_flux_container_mk1\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lazuli_flux_container_mk1.json",
    "content": "{\n  \"parent\": \"indrev:block/lazuli_flux_container_mk1\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lazuli_flux_container_mk2.json",
    "content": "{\n  \"parent\": \"indrev:block/lazuli_flux_container_mk2\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lazuli_flux_container_mk3.json",
    "content": "{\n  \"parent\": \"indrev:block/lazuli_flux_container_mk3\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lazuli_flux_container_mk4.json",
    "content": "{\n  \"parent\": \"indrev:block/lazuli_flux_container_mk4\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_axe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_block.json",
    "content": "{\n  \"parent\": \"indrev:block/lead_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_boots.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_chestplate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_chunk.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_helmet.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_hoe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_ingot.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_leggings.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_nugget.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_nugget\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/lead_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_pickaxe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_plate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_purified_ore.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_purified_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_shovel.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/lead_sword.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/lead_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/leggings_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/leggings_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/leggings_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/leggings_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/leggings_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/leggings_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/machine_block.json",
    "content": "{\n  \"parent\": \"indrev:block/machine_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/miner_mk4.json",
    "content": "{\n  \"parent\": \"indrev:block/miner_mk4\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/mining_drill_mk1.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/mining_drill_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/mining_drill_mk2.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/mining_drill_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/mining_drill_mk3.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/mining_drill_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/mining_drill_mk4.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/mining_drill_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/modular_armor_boots.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/modular_armor_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/modular_armor_chest.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/modular_armor_chest\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/modular_armor_helmet.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/modular_armor_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/modular_armor_legs.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/modular_armor_legs\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/modular_core.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/modular_core\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/modular_core_activated.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/modular_core_activated\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/modular_workbench_mk4.json",
    "content": "{\n  \"parent\": \"indrev:block/modular_workbench_mk4\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_auto_feeder.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_auto_feeder\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_breathing.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_breathing\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_charger.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_charger\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_color_black.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_color_black\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_color_blue.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_color_blue\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_color_brown.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_color_brown\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_color_cyan.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_color_cyan\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_color_green.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_color_green\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_color_orange.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_color_orange\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_color_pink.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_color_pink\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_color_purple.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_color_purple\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_color_red.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_color_red\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_color_yellow.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_color_yellow\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_efficiency.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_efficiency\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_elytra.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_elytra\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_feather_falling.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_feather_falling\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_fire_aspect.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_fire_aspect\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_fire_resistance.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_fire_resistance\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_fortune.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_fortune\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_jetpack.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_jetpack\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_jump_boost.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_jump_boost\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_looting.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_looting\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_magnet.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_magnet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_night_vision.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_night_vision\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_piglin_tricker.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_piglin_tricker\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_protection.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_protection\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_range.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_range\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_reach.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_reach\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_sharpness.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_sharpness\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_silk_touch.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_silk_touch\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_solar_panel.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_solar_panel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_speed.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_speed\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/module_water_affinity.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/module_water_affinity\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/molten_bucket_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/molten_bucket_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/molten_bucket_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/molten_bucket_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/molten_bucket_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/molten_bucket_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/molten_copper_bucket.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/molten_copper_bucket\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/molten_gold_bucket.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/molten_gold_bucket\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/molten_iron_bucket.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/molten_iron_bucket\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/molten_lead_bucket.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/molten_lead_bucket\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/molten_netherite_bucket.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/molten_netherite_bucket\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/molten_silver_bucket.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/molten_silver_bucket\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/molten_tin_bucket.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/molten_tin_bucket\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/netherite_drill_head.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/netherite_drill_head\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/netherite_scrap_chunk.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/netherite_scrap_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/netherite_scrap_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/netherite_scrap_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/netherite_scrap_purified_ore.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/netherite_scrap_purified_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/nikolite_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/nikolite_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/nikolite_ingot.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/nikolite_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/nikolite_nether_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/nikolite_nether_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/nikolite_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/nikolite_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/nugget_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/nugget_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/nugget_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/nugget_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/nugget_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/nugget_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/ore_base.json",
    "content": "{\n    \"parent\": \"indrev:block/ore_base\"\n\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/ore_data_card.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/ore_data_card_empty\"\n  },\n  \"overrides\": [\n    {\n      \"predicate\": {\n        \"indrev:empty\": 1\n      },\n      \"model\": \"indrev:item/ore_data_card_not_empty\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/ore_data_card_not_empty.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/ore_data_card_not_empty\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/ore_highlight.json",
    "content": "{\n    \"parent\": \"indrev:block/ore_highlight\"\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/pickaxe_base.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/pickaxe_base\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/pickaxe_highlight.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/pickaxe_highlight\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/pickaxe_outline.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/pickaxe_outline\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/plank_block.json",
    "content": "{\n  \"parent\": \"indrev:block/plank_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/planks.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/planks_item\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/plate_base.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/plate_base\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/plate_highlight.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/plate_highlight\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/plate_outline.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/plate_outline\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/portable_charger.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/portable_charger\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/pump_mk1.json",
    "content": "{\n  \"parent\": \"indrev:block/pump_mk1\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/purified_ore_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/purified_ore_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/purified_ore_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/purified_ore_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/purified_ore_outline.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/purified_ore_outline\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/raw_lead.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/raw_lead\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/raw_lead_block.json",
    "content": "{\n  \"parent\": \"indrev:block/raw_lead_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/raw_silver.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/raw_silver\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/raw_silver_block.json",
    "content": "{\n  \"parent\": \"indrev:block/raw_silver_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/raw_tin.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/raw_tin\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/raw_tin_block.json",
    "content": "{\n  \"parent\": \"indrev:block/raw_tin_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/raw_tungsten.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/raw_tungsten\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/raw_tungsten_block.json",
    "content": "{\n  \"parent\": \"indrev:block/raw_tungsten_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/reinforced_elytra.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/reinforced_elytra\"\n  },\n  \"overrides\": [\n    {\n      \"predicate\": {\n        \"indrev:broken\": 1\n      },\n      \"model\": \"indrev:item/broken_reinforced_elytra\"\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/sawdust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/sawdust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/scan_output.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/scan_output\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/screwdriver.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/screwdriver\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/servo_output.json",
    "content": "{\n\t\"parent\": \"item/generated\",\n\t\"textures\": {\n\t  \"layer0\": \"indrev:item/servo_output\"\n\t}\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/servo_retriever.json",
    "content": "{\n\t\"parent\": \"item/generated\",\n\t\"textures\": {\n\t  \"layer0\": \"indrev:item/servo_retriever\"\n\t}\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/shovel_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/shovel_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/shovel_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/shovel_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/shovel_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/shovel_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silo.json",
    "content": "{\n  \"parent\": \"indrev:block/silo\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_axe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_block.json",
    "content": "{\n  \"parent\": \"indrev:block/silver_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_boots.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_chestplate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_chunk.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_helmet.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_hoe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_ingot.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_leggings.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_nugget.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_nugget\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/silver_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_pickaxe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_plate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_purified_ore.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_purified_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_shovel.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/silver_sword.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/silver_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/smelter_mk4.json",
    "content": "{\n  \"parent\": \"indrev:block/smelter_mk4\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/smoker_enhancer.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/smoker_upgrade\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/soot.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/soot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/speed_enhancer.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/speed_upgrade\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_axe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_block.json",
    "content": "{\n  \"parent\": \"indrev:block/steel_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_boots.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_chestplate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_gear.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_gear\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_helmet.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_hoe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_ingot.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_leggings.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_nugget.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_nugget\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_pickaxe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_plate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_shovel.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/steel_sword.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/steel_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/stone_drill_head.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/stone_drill_head\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/sulfur_crystal.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/sulfur_crystal\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/sulfur_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/sulfur_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/sulfuric_acid_bucket.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/sulfuric_acid_bucket\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/sword_base.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/sword_base\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/sword_highlight.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/sword_highlight\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/sword_outline.json",
    "content": "{\n    \"parent\": \"item/generated\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/sword_outline\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tank.json",
    "content": "{\n  \"parent\": \"indrev:block/tank\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tech_soup.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tech_soup\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tier_upgrade_mk2.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tier_upgrade_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tier_upgrade_mk3.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tier_upgrade_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tier_upgrade_mk4.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tier_upgrade_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_axe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_block.json",
    "content": "{\n  \"parent\": \"indrev:block/tin_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_boots.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_chestplate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_chunk.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_gear.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_gear\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_helmet.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_hoe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_ingot.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_leggings.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_nugget.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_nugget\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/tin_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_pickaxe.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_plate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_purified_ore.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_purified_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_shovel.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tin_sword.json",
    "content": "{\n  \"parent\": \"item/handheld\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tin_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tool_stick.json",
    "content": "{\n    \"parent\": \"item/handheld\",\n    \"textures\": {\n      \"layer0\": \"indrev:item/tool_stick\"\n    }\n  }\n"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/toxic_mud_bucket.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/toxic_mud_bucket\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tungsten_block.json",
    "content": "{\n  \"parent\": \"indrev:block/tungsten_block\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tungsten_chunk.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tungsten_chunk\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tungsten_dust.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tungsten_dust\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tungsten_ingot.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tungsten_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tungsten_nugget.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tungsten_nugget\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tungsten_ore.json",
    "content": "{\n  \"parent\": \"indrev:block/tungsten_ore\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tungsten_plate.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tungsten_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/tungsten_purified_ore.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/tungsten_purified_ore\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/untanned_leather.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/untanned_leather\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/warning_strobe.json",
    "content": "{\n  \"parent\": \"indrev:block/warning_strobe\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/wither_proof_obsidian.json",
    "content": "{\n  \"parent\": \"indrev:block/wither_proof_obsidian\"\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/models/item/wrench.json",
    "content": "{\n  \"parent\": \"item/generated\",\n  \"textures\": {\n    \"layer0\": \"indrev:item/wrench\"\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/particles/laser_particle.json",
    "content": "{\n  \"textures\": [\n    \"indrev:laser_particle_1\",\n    \"indrev:laser_particle_2\",\n    \"indrev:laser_particle_3\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/sounds.json",
    "content": "{\n  \"laser\": {\n    \"subtitle\": \"subtitles.indrev.laser\",\n    \"sounds\": [\n      \"indrev:laser\"\n    ]\n  }\n}\n\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/biomass_generator_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 2,\n    \"frames\": [2, 3, 4, 1, 2, 0, 3, 2, 0, 1, 2, 4, 3]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/cable_center_emissive_mk1.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 1\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/cable_center_emissive_mk2.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 1\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/cable_center_emissive_mk3.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 1\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/cable_center_emissive_mk4.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 1\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/cable_wire_emissive_mk1.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 1\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/cable_wire_emissive_mk2.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 1\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/cable_wire_emissive_mk3.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 1\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/cable_wire_emissive_mk4.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 1\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/chopper_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 6\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/coal_generator_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"frametime\": 6,\n    \"frames\": [3, 2, 0, 1, 3, 1, 0, 2, 3, 1, 0, 2, 1, 3, 3, 2, 1]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/compressor_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"frametime\": 6,\n    \"frames\": [0, 1, 2, 3, 4, 4, 4, 4, 3, 2, 1]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/condenser_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 5\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/data_card_writer_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 1,\n\t\"frames\": [0,1,2,3,4,5,6,7,6,5,4,3,2,1]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/drill_top_emissive_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 4\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/drill_top_tracer_emissive_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 1\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/electric_furnace_emissive_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 4\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/fluid_infuser_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 5\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/gray_lava_flow.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"frametime\": 3\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/gray_lava_still.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"frametime\": 2,\n    \"frames\": [\n      0,\n      1,\n      2,\n      3,\n      4,\n      5,\n      6,\n      7,\n      8,\n      9,\n      10,\n      11,\n      12,\n      13,\n      14,\n      15,\n      16,\n      17,\n      18,\n      19,\n      18,\n      17,\n      16,\n      15,\n      14,\n      13,\n      12,\n      11,\n      10,\n      9,\n      8,\n      7,\n      6,\n      5,\n      4,\n      3,\n      2,\n      1\n    ]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/mining_rig_screen_emissive.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 3\n  }\n}\n"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/pulverizer_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 2\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/rancher_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 5\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/recycler_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 10,\n    \"frames\": [0, 1, 2, 3, 2, 1]\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/sawmill_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 1\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/block/solid_infuser_emissive_on.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": false,\n    \"frametime\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/entity/laser.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 1\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/gui/hud_damaged.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 1\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/gui/hud_regenerating.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 1\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/gui/hud_warning.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 1\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/item/enriched_nikolite_dust.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 1\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/item/enriched_nikolite_ingot.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 1\n  }\n}"
  },
  {
    "path": "src/main/resources/assets/indrev/textures/item/gamer_axe.png.mcmeta",
    "content": "{\n  \"animation\": {\n    \"interpolate\": true,\n    \"frametime\": 1\n  }\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/blocks/bronze_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:bronze_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/blocks/copper_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:copper_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/blocks/electrum_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:electrum_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/blocks/lead_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:lead_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/blocks/lead_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:lead_ore\",\n    \"indrev:deepslate_lead_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/blocks/silver_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:silver_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/blocks/silver_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:silver_ore\",\n    \"indrev:deepslate_silver_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/blocks/tin_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tin_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/blocks/tin_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tin_ore\",\n    \"indrev:deepslate_tin_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/blocks/tungsten_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tungsten_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/blocks/tungsten_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tungsten_ore\",\n    \"indrev:deepslate_tungsten_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/ancient_debris_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:ancient_debris\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/bronze_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:bronze_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/bronze_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:bronze_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/bronze_ingots.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:bronze_ingot\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/bronze_nuggets.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:bronze_nugget\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/bronze_plates.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:bronze_plate\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/coal_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:coal_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/coal_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:coal_ore\",\n    \"minecraft:deepslate_coal_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/copper_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:copper_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/copper_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:copper_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/copper_ingots.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:copper_ingot\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/copper_nuggets.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:copper_nugget\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/copper_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:copper_ore\",\n    \"minecraft:deepslate_copper_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/copper_plates.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:copper_plate\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/diamond_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:diamond_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/diamond_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:diamond_ore\",\n    \"minecraft:deepslate_diamond_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/electrum_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:electrum_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/electrum_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:electrum_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/electrum_ingots.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:electrum_ingot\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/electrum_nuggets.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:electrum_nugget\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/electrum_plates.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:electrum_plate\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/emerald_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:emerald_ore\",\n    \"minecraft:deepslate_emerald_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/gold_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:gold_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/gold_ingots.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:gold_ingot\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/gold_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:gold_ore\",\n    \"minecraft:deepslate_gold_ore\",\n    \"minecraft:nether_gold_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/gold_plates.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:gold_plate\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/iron_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:iron_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/iron_ingots.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:iron_ingot\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/iron_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:iron_ore\",\n    \"minecraft:deepslate_iron_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/iron_plates.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:iron_plate\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/lead_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:lead_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/lead_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:lead_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/lead_ingots.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:lead_ingot\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/lead_nuggets.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:lead_nugget\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/lead_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:lead_ore\",\n    \"indrev:deepslate_lead_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/lead_plates.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:lead_plate\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/netherite_scrap_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:netherite_scrap_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/nikolite_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:nikolite_ore\",\n    \"indrev:deepslate_nikolite_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/raw_copper_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:raw_copper\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/raw_gold_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:raw_gold\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/raw_iron_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:raw_iron\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/raw_lead_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:raw_lead\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/raw_silver_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:raw_silver\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/raw_tin_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:raw_tin\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/raw_tungsten_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:raw_tungsten\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/redstone_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"minecraft:redstone_ore\",\n    \"minecraft:deepslate_redstone_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/screwdrivers.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:screwdriver\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/silver_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:silver_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/silver_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:silver_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/silver_ingots.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:silver_ingot\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/silver_nuggets.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:silver_nugget\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/silver_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:silver_ore\",\n    \"indrev:deepslate_silver_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/silver_plates.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:silver_plate\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/steel_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:steel_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/steel_boots.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:steel_boots\",\n    {\n      \"id\": \"astromine:steel_boots\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/steel_chestplates.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:steel_chestplate\",\n    {\n      \"id\": \"astromine:steel_chestplate\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/steel_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:steel_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/steel_helmets.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:steel_helmet\",\n    {\n      \"id\": \"astromine:steel_helmet\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/steel_ingots.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:steel_ingot\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/steel_leggings.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:steel_leggings\",\n    {\n      \"id\": \"astromine:steel_leggings\",\n      \"required\": false\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/steel_nuggets.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:steel_nugget\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/steel_plates.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:steel_plate\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/sulfur_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:sulfur_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/sulfurs.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:sulfur_crystal\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tin_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tin_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tin_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tin_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tin_ingots.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tin_ingot\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tin_nuggets.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tin_nugget\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tin_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tin_ore\",\n    \"indrev:deepslate_tin_ore\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tin_plates.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tin_plate\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tungsten_blocks.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tungsten_block\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tungsten_dusts.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tungsten_dust\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tungsten_ingots.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tungsten_ingot\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tungsten_nuggets.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tungsten_nugget\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tungsten_ores.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tungsten_ore\",\n    \"indrev:deepslate_tungsten_ore\"\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/data/c/tags/items/tungsten_plates.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tungsten_plate\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/c/tags/items/wrenches.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:wrench\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/fabric/tags/items/axes.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:mining_drill_mk1\",\n    \"indrev:mining_drill_mk2\",\n    \"indrev:mining_drill_mk3\",\n    \"indrev:mining_drill_mk4\",\n    \"indrev:tin_axe\",\n    \"indrev:copper_axe\",\n    \"indrev:steel_axe\",\n    \"indrev:gamer_axe\",\n    \"indrev:lead_axe\",\n    \"indrev:silver_axe\",\n    \"indrev:bronze_axe\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/fabric/tags/items/hoes.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tin_hoe\",\n    \"indrev:copper_hoe\",\n    \"indrev:steel_hoe\",\n    \"indrev:lead_hoe\",\n    \"indrev:silver_hoe\",\n    \"indrev:bronze_hoe\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/fabric/tags/items/pickaxes.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:mining_drill_mk1\",\n    \"indrev:mining_drill_mk2\",\n    \"indrev:mining_drill_mk3\",\n    \"indrev:mining_drill_mk4\",\n    \"indrev:tin_pickaxe\",\n    \"indrev:copper_pickaxe\",\n    \"indrev:steel_pickaxe\",\n    \"indrev:lead_pickaxe\",\n    \"indrev:silver_pickaxe\",\n    \"indrev:bronze_pickaxe\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/fabric/tags/items/shovels.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:mining_drill_mk1\",\n    \"indrev:mining_drill_mk2\",\n    \"indrev:mining_drill_mk3\",\n    \"indrev:mining_drill_mk4\",\n    \"indrev:tin_shovel\",\n    \"indrev:copper_shovel\",\n    \"indrev:steel_shovel\",\n    \"indrev:lead_shovel\",\n    \"indrev:silver_shovel\",\n    \"indrev:bronze_shovel\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/fabric/tags/items/swords.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:tin_sword\",\n    \"indrev:copper_sword\",\n    \"indrev:steel_sword\",\n    \"indrev:lead_sword\",\n    \"indrev:silver_sword\",\n    \"indrev:bronze_sword\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/advanced_solar_generator.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:solar_generator_mk3\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.advanced_solar_generator\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.advanced_solar_generator.description\"\n    }\n  },\n  \"parent\": \"indrev:basic_solar_generator\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:solar_generator_mk3\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/basic_solar_generator.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:solar_generator_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.basic_solar_generator\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.basic_solar_generator.description\"\n    }\n  },\n  \"parent\": \"indrev:coal_generator\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:solar_generator_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/biomass.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:biomass\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.biomass\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.biomass.description\"\n    }\n  },\n  \"parent\": \"indrev:recycler\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:biomass\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/biomass_generator.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:biomass_generator_mk3\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.biomass_generator\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.biomass_generator.description\"\n    }\n  },\n  \"parent\": \"indrev:biomass\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:biomass_generator_mk3\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/blast_furnace_enhancer.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:blast_furnace_enhancer\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.blast_furnace_enhancer\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.blast_furnace_enhancer.description\"\n    }\n  },\n  \"parent\": \"indrev:empty_enhancer\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:blast_furnace_enhancer\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/buffer_enhancer.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:buffer_enhancer\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.buffer_enhancer\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.buffer_enhancer.description\"\n    }\n  },\n  \"parent\": \"indrev:empty_enhancer\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:buffer_enhancer\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/cable_mk1.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:cable_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.cable_mk1\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.cable_mk1.description\"\n    }\n  },\n  \"parent\": \"indrev:nikolite\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:cable_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/cable_mk2.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:cable_mk2\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.cable_mk2\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.cable_mk2.description\"\n    }\n  },\n  \"parent\": \"indrev:cable_mk1\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:cable_mk2\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/cable_mk3.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:cable_mk3\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.cable_mk3\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.cable_mk3.description\"\n    }\n  },\n  \"parent\": \"indrev:cable_mk2\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:cable_mk3\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/cable_mk4.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:cable_mk4\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.cable_mk4\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.cable_mk4.description\"\n    }\n  },\n  \"parent\": \"indrev:cable_mk3\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:cable_mk4\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/chopper_mk4.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:chopper_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.chopper_mk4\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.chopper_mk4.description\"\n    }\n  },\n  \"parent\": \"indrev:machine_block\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:chopper_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/circuit_mk1.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.circuit_mk1\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.circuit_mk1.description\"\n    }\n  },\n  \"hidden\": true,\n  \"parent\": \"indrev:nikolite\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:circuit_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/circuit_mk2.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:circuit_mk2\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.circuit_mk2\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.circuit_mk2.description\"\n    }\n  },\n  \"hidden\": true,\n  \"parent\": \"indrev:circuit_mk1\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:circuit_mk2\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/circuit_mk3.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:circuit_mk3\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.circuit_mk3\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.circuit_mk3.description\"\n    }\n  },\n  \"hidden\": true,\n  \"parent\": \"indrev:circuit_mk2\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:circuit_mk3\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/circuit_mk4.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.circuit_mk4\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.circuit_mk4.description\"\n    }\n  },\n  \"hidden\": true,\n  \"parent\": \"indrev:circuit_mk3\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:circuit_mk4\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/coal_generator.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:coal_generator_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.coal_generator\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.coal_generator.description\"\n    }\n  },\n  \"parent\": \"indrev:machine_block\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:coal_generator_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/compressor.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:compressor_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.compressor\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.compressor.description\"\n    }\n  },\n  \"parent\": \"indrev:machine_block\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:compressor_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/condenser.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:condenser_mk4\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.condenser\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.condenser\"\n    }\n  },\n  \"parent\": \"indrev:circuit_mk4\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:condenser_mk4\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/electric_furnace.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:electric_furnace_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.electric_furnace\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.electric_furnace.description\"\n    }\n  },\n  \"parent\": \"indrev:machine_block\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:electric_furnace_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/empty_enhancer.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:empty_enhancer\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.empty_enhancer\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.empty_enhancer.description\"\n    }\n  },\n  \"parent\": \"indrev:nikolite\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:empty_enhancer\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/enriched_nikolite_dust.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.enriched_nikolite_dust\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.enriched_nikolite_dust.description\"\n    }\n  },\n  \"parent\": \"indrev:nikolite_ingot\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:enriched_nikolite_dust\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/enriched_nikolite_ingot.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.enriched_nikolite_ingot\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.enriched_nikolite_ingot.description\"\n    }\n  },\n  \"parent\": \"indrev:enriched_nikolite_dust\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:enriched_nikolite_ingot\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/farmer_mk1.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:farmer_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.farmer_mk1\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.farmer_mk1.description\"\n    }\n  },\n  \"parent\": \"indrev:machine_block\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:farmer_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/gamer_axe.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:gamer_axe\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.gamer_axe\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.gamer_axe.description\"\n    },\n    \"frame\": \"challenge\"\n  },\n  \"parent\": \"indrev:nikolite\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:gamer_axe\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/heat_generator.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:heat_generator_mk4\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.heat_generator\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.heat_generator.description\"\n    }\n  },\n  \"parent\": \"indrev:coal_generator\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:heat_generator_mk4\"]\n          }\n        ]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/industrial_smelter.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:smelter_mk4\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.industrial_smelter\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.industrial_smelter\"\n    }\n  },\n  \"parent\": \"indrev:circuit_mk4\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:smelter_mk4\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/lazuli_flux_container_mk1.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:lazuli_flux_container_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.lazuli_flux_container_mk1\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.lazuli_flux_container_mk1.description\"\n    }\n  },\n  \"parent\": \"indrev:circuit_mk1\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:lazuli_flux_container_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/lazuli_flux_container_mk2.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:lazuli_flux_container_mk2\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.lazuli_flux_container_mk2\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.lazuli_flux_container_mk2.description\"\n    }\n  },\n  \"parent\": \"indrev:circuit_mk2\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:lazuli_flux_container_mk2\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/lazuli_flux_container_mk3.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:lazuli_flux_container_mk3\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.lazuli_flux_container_mk3\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.lazuli_flux_container_mk3.description\"\n    }\n  },\n  \"parent\": \"indrev:circuit_mk3\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:lazuli_flux_container_mk3\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/lazuli_flux_container_mk4.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:lazuli_flux_container_mk4\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.lazuli_flux_container_mk4\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.lazuli_flux_container_mk4.description\"\n    }\n  },\n  \"parent\": \"indrev:circuit_mk4\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\n              \"indrev:lazuli_flux_container_mk4\"\n            ]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/machine_block.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.machine_block\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.machine_block.description\"\n    }\n  },\n  \"parent\": \"indrev:nikolite\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:machine_block\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/mk2_upgrade.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.mk2_upgrade\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.mk2_upgrade.description\"\n    }\n  },\n  \"parent\": \"indrev:circuit_mk2\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:item_used_on_block\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"indrev:tier_upgrade_mk2\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/mk3_upgrade.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.mk3_upgrade\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.mk3_upgrade.description\"\n    }\n  },\n  \"parent\": \"indrev:circuit_mk3\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:item_used_on_block\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"indrev:tier_upgrade_mk3\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/mk4_upgrade.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.mk4_upgrade\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.mk4_upgrade.description\"\n    }\n  },\n  \"parent\": \"indrev:circuit_mk4\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:item_used_on_block\",\n      \"conditions\": {\n        \"item\": {\n          \"items\": [\"indrev:tier_upgrade_mk4\"]\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/modular_armor.json",
    "content": "{\n  \"parent\": \"indrev:modular_workbench\",\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:modular_armor_chest\",\n      \"nbt\": \"{Damage:0}\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.modular_armor\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.modular_armor.description\"\n    },\n    \"frame\": \"challenge\"\n  },\n  \"rewards\": {\n    \"experience\": 100\n  },\n  \"criteria\": {\n    \"netherite_armor\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:modular_armor_helmet\",\"indrev:modular_armor_chest\", \"indrev:modular_armor_legs\", \"indrev:modular_armor_boots\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/modular_workbench.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:modular_workbench_mk4\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.modular_workbench\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.modular_workbench.description\"\n    }\n  },\n  \"parent\": \"indrev:machine_block\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:modular_workbench_mk4\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/nikolite.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.nikolite\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.nikolite.description\"\n    },\n    \"background\": \"minecraft:textures/gui/advancements/backgrounds/adventure.png\"\n  },\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:nikolite_dust\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/nikolite_ingot.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:nikolite_ingot\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.nikolite_ingot\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.nikolite_ingot.description\"\n    }\n  },\n  \"parent\": \"indrev:solid_infuser\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:nikolite_ingot\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/pulverizer.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:pulverizer_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.pulverizer\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.pulverizer.description\"\n    }\n  },\n  \"parent\": \"indrev:machine_block\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:pulverizer_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/rancher_mk4.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:rancher_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.rancher_mk4\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.rancher_mk4.description\"\n    }\n  },\n  \"parent\": \"indrev:machine_block\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:rancher_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/recycler.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:recycler_mk2\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.recycler\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.recycler.description\"\n    }\n  },\n  \"parent\": \"indrev:machine_block\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:recycler_mk2\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/slaughter_mk1.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:slaughter_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.slaughter_mk1\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.slaughter_mk1.description\"\n    }\n  },\n  \"parent\": \"indrev:machine_block\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:slaughter_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/smoker_enhancer.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:smoker_enhancer\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.smoker_enhancer\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.smoker_enhancer.description\"\n    }\n  },\n  \"parent\": \"indrev:empty_enhancer\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:smoker_enhancer\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/solid_infuser.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:solid_infuser_mk1\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.solid_infuser\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.solid_infuser.description\"\n    }\n  },\n  \"parent\": \"indrev:machine_block\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:solid_infuser_mk1\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/advancements/speed_enhancer.json",
    "content": "{\n  \"display\": {\n    \"icon\": {\n      \"item\": \"indrev:speed_enhancer\"\n    },\n    \"title\": {\n      \"translate\": \"advancements.indrev.speed_enhancer\"\n    },\n    \"description\": {\n      \"translate\": \"advancements.indrev.speed_enhancer.description\"\n    }\n  },\n  \"parent\": \"indrev:empty_enhancer\",\n  \"criteria\": {\n    \"checkInv\": {\n      \"trigger\": \"minecraft:inventory_changed\",\n      \"conditions\": {\n        \"items\": [\n          {\n            \"items\": [\"indrev:speed_enhancer\"]\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/biomass_generator_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:biomass_generator_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/bronze_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:bronze_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/cabinet.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:cabinet\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/cable_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:cable_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/cable_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:cable_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/cable_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:cable_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/cable_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:cable_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/capsule.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:capsule\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/charge_pad_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:charge_pad_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/chopper_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:chopper_creative\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/chopper_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:chopper_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/chopper_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:chopper_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/chopper_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:chopper_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/chopper_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:chopper_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/coal_generator_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:coal_generator_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/compressor_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:compressor_creative\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/compressor_factory_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:compressor_factory_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/compressor_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:compressor_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/compressor_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:compressor_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/compressor_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:compressor_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/compressor_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:compressor_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/condenser_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:condenser_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/controller.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:controller\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/deepslate_lead_ore.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"enchantments\": [\n                      {\n                        \"enchantment\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              ],\n              \"name\": \"indrev:deepslate_lead_ore\"\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"formula\": \"minecraft:ore_drops\"\n                },\n                {\n                  \"function\": \"minecraft:explosion_decay\"\n                }\n              ],\n              \"name\": \"indrev:raw_lead\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/deepslate_nikolite_ore.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"enchantments\": [\n                      {\n                        \"enchantment\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              ],\n              \"name\": \"indrev:deepslate_nikolite_ore\"\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:set_count\",\n                  \"count\": {\n                    \"min\": 4.0,\n                    \"max\": 5.0,\n                    \"type\": \"minecraft:uniform\"\n                  }\n                },\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"formula\": \"minecraft:uniform_bonus_count\",\n                  \"parameters\": {\n                    \"bonusMultiplier\": 1\n                  }\n                },\n                {\n                  \"function\": \"minecraft:explosion_decay\"\n                }\n              ],\n              \"name\": \"indrev:nikolite_dust\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/deepslate_silver_ore.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"enchantments\": [\n                      {\n                        \"enchantment\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              ],\n              \"name\": \"indrev:deepslate_silver_ore\"\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"formula\": \"minecraft:ore_drops\"\n                },\n                {\n                  \"function\": \"minecraft:explosion_decay\"\n                }\n              ],\n              \"name\": \"indrev:raw_silver\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/deepslate_tin_ore.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"enchantments\": [\n                      {\n                        \"enchantment\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              ],\n              \"name\": \"indrev:deepslate_tin_ore\"\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"formula\": \"minecraft:ore_drops\"\n                },\n                {\n                  \"function\": \"minecraft:explosion_decay\"\n                }\n              ],\n              \"name\": \"indrev:raw_tin\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/deepslate_tungsten_ore.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"enchantments\": [\n                      {\n                        \"enchantment\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              ],\n              \"name\": \"indrev:deepslate_tungsten_ore\"\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"formula\": \"minecraft:ore_drops\"\n                },\n                {\n                  \"function\": \"minecraft:explosion_decay\"\n                }\n              ],\n              \"name\": \"indrev:raw_tungsten\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/drain_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:drain_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/drill_bottom.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:drill_bottom\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/duct.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:duct\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:electric_furnace_creative\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_factory_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:electric_furnace_factory_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:electric_furnace_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:electric_furnace_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:electric_furnace_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/electric_furnace_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:electric_furnace_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/electrum_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:electrum_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/farmer_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:farmer_creative\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/farmer_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:farmer_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/farmer_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:farmer_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/farmer_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:farmer_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/farmer_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:farmer_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fisher_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fisher_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fisher_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fisher_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fisher_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fisher_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fluid_infuser_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fluid_infuser_creative\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fluid_infuser_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fluid_infuser_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fluid_infuser_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fluid_infuser_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fluid_infuser_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fluid_infuser_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fluid_infuser_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fluid_infuser_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fluid_pipe_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fluid_pipe_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fluid_pipe_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fluid_pipe_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fluid_pipe_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fluid_pipe_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/fluid_pipe_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:fluid_pipe_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/frame.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:frame\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/heat_generator_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:heat_generator_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/intake.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:intake\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/item_pipe_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:item_pipe_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/item_pipe_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:item_pipe_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/item_pipe_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:item_pipe_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/item_pipe_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:item_pipe_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/laser_emitter_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:laser_emitter_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/lazuli_flux_container_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:lazuli_flux_container_creative\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/lazuli_flux_container_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:lazuli_flux_container_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/lazuli_flux_container_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:lazuli_flux_container_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/lazuli_flux_container_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:lazuli_flux_container_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/lazuli_flux_container_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:lazuli_flux_container_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/lead_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:lead_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/lead_ore.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"enchantments\": [\n                      {\n                        \"enchantment\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              ],\n              \"name\": \"indrev:lead_ore\"\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"formula\": \"minecraft:ore_drops\"\n                },\n                {\n                  \"function\": \"minecraft:explosion_decay\"\n                }\n              ],\n              \"name\": \"indrev:raw_lead\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/machine_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:machine_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/mining_rig_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:mining_rig_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/modular_workbench_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:modular_workbench_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/nikolite_ore.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"enchantments\": [\n                      {\n                        \"enchantment\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              ],\n              \"name\": \"indrev:nikolite_ore\"\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:set_count\",\n                  \"count\": {\n                    \"min\": 4.0,\n                    \"max\": 5.0,\n                    \"type\": \"minecraft:uniform\"\n                  }\n                },\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"formula\": \"minecraft:uniform_bonus_count\",\n                  \"parameters\": {\n                    \"bonusMultiplier\": 1\n                  }\n                },\n                {\n                  \"function\": \"minecraft:explosion_decay\"\n                }\n              ],\n              \"name\": \"indrev:nikolite_dust\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/plank_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:plank_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/planks.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:alternatives\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:inverted\",\n                  \"term\": {\n                    \"condition\": \"minecraft:match_tool\",\n                    \"predicate\": {\n                      \"enchantments\": [\n                        {\n                          \"enchantment\": \"minecraft:silk_touch\",\n                          \"levels\": {\n                            \"min\": 1\n                          }\n                        }\n                      ]\n                    }\n                  }\n                }\n              ],\n              \"children\": [\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"1\"\n                      }\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"2\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 2\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"3\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 3\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"4\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 4\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"5\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 5\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"6\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 6\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"7\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 7\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"name\": \"indrev:plank_block\"\n                }\n              ]\n            },\n            {\n              \"type\": \"minecraft:alternatives\",\n              \"children\": [\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"1\"\n                      }\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"2\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 2\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"3\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 3\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"4\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 4\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"5\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 5\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"6\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 6\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"conditions\": [\n                    {\n                      \"condition\": \"minecraft:block_state_property\",\n                      \"block\": \"indrev:planks\",\n                      \"properties\": {\n                        \"layers\": \"7\"\n                      }\n                    }\n                  ],\n                  \"functions\": [\n                    {\n                      \"function\": \"minecraft:set_count\",\n                      \"count\": 7\n                    }\n                  ],\n                  \"name\": \"indrev:planks\"\n                },\n                {\n                  \"type\": \"minecraft:item\",\n                  \"name\": \"indrev:plank_block\"\n                }\n              ]\n            }\n          ]\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:entity_properties\",\n          \"predicate\": {},\n          \"entity\": \"this\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/pulverizer_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:pulverizer_creative\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/pulverizer_factory_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:pulverizer_factory_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/pulverizer_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:pulverizer_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/pulverizer_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:pulverizer_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/pulverizer_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:pulverizer_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/pulverizer_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:pulverizer_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/pump_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:pump_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/rancher_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:rancher_creative\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/rancher_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:rancher_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/rancher_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:rancher_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/rancher_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:rancher_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/rancher_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:rancher_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/raw_lead_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:raw_lead_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/raw_silver_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:raw_silver_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/raw_tin_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:raw_tin_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/raw_tungsten_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:raw_tungsten_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/recycler_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:recycler_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/sawmill_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:sawmill_creative\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/sawmill_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:sawmill_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/sawmill_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:sawmill_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/sawmill_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:sawmill_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/sawmill_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:sawmill_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/silo.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:silo\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/silver_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:silver_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/silver_ore.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"enchantments\": [\n                      {\n                        \"enchantment\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              ],\n              \"name\": \"indrev:silver_ore\"\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"formula\": \"minecraft:ore_drops\"\n                },\n                {\n                  \"function\": \"minecraft:explosion_decay\"\n                }\n              ],\n              \"name\": \"indrev:raw_silver\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/slaughter_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:slaughter_creative\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/slaughter_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:slaughter_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/slaughter_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:slaughter_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/slaughter_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:slaughter_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/slaughter_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:slaughter_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/smelter_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:smelter_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/solar_generator_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:solar_generator_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/solar_generator_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:solar_generator_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_creative.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:solid_infuser_creative\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_factory_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:solid_infuser_factory_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_mk1.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:solid_infuser_mk1\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_mk2.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:solid_infuser_mk2\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_mk3.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:solid_infuser_mk3\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/solid_infuser_mk4.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:solid_infuser_mk4\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/steel_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:steel_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/sulfur_crystal.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:set_count\",\n                  \"count\": {\n                    \"min\": 1.0,\n                    \"max\": 3.0,\n                    \"type\": \"minecraft:uniform\"\n                  }\n                },\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"formula\": \"minecraft:uniform_bonus_count\",\n                  \"parameters\": {\n                    \"bonusMultiplier\": 2\n                  }\n                },\n                {\n                  \"function\": \"minecraft:explosion_decay\"\n                }\n              ],\n              \"name\": \"indrev:sulfur_crystal\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/tank.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:tank\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/tin_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:tin_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/tin_ore.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"enchantments\": [\n                      {\n                        \"enchantment\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              ],\n              \"name\": \"indrev:tin_ore\"\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"formula\": \"minecraft:ore_drops\"\n                },\n                {\n                  \"function\": \"minecraft:explosion_decay\"\n                }\n              ],\n              \"name\": \"indrev:raw_tin\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/tungsten_block.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:tungsten_block\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/tungsten_ore.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1.0,\n      \"bonus_rolls\": 0.0,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:alternatives\",\n          \"children\": [\n            {\n              \"type\": \"minecraft:item\",\n              \"conditions\": [\n                {\n                  \"condition\": \"minecraft:match_tool\",\n                  \"predicate\": {\n                    \"enchantments\": [\n                      {\n                        \"enchantment\": \"minecraft:silk_touch\",\n                        \"levels\": {\n                          \"min\": 1\n                        }\n                      }\n                    ]\n                  }\n                }\n              ],\n              \"name\": \"indrev:tungsten_ore\"\n            },\n            {\n              \"type\": \"minecraft:item\",\n              \"functions\": [\n                {\n                  \"function\": \"minecraft:apply_bonus\",\n                  \"enchantment\": \"minecraft:fortune\",\n                  \"formula\": \"minecraft:ore_drops\"\n                },\n                {\n                  \"function\": \"minecraft:explosion_decay\"\n                }\n              ],\n              \"name\": \"indrev:raw_tungsten\"\n            }\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/warning_strobe.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:warning_strobe\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/loot_tables/blocks/wither_proof_obsidian.json",
    "content": "{\n  \"type\": \"minecraft:block\",\n  \"pools\": [\n    {\n      \"rolls\": 1,\n      \"entries\": [\n        {\n          \"type\": \"minecraft:item\",\n          \"name\": \"indrev:wither_proof_obsidian\"\n        }\n      ],\n      \"conditions\": [\n        {\n          \"condition\": \"minecraft:survives_explosion\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/book.json",
    "content": "{\n  \"name\": \"item.patchouli.industrial_revolution_book.name\",\n  \"landing_text\": \"item.patchouli.industrial_revolution_book.landing\",\n  \"model\": \"indrev:guide_book\",\n  \"version\": 1,\n  \"subtitle\": \"Everything you, $(playername), need to know about $(l)$(o)Industrial Revolution\",\n  \"creative_tab\": \"indrev.indrev_group\",\n  \"show_progress\": false,\n  \"pause_game\": false\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/enhanced_ore_processing.json",
    "content": "{\n  \"name\": \"Enhanced Ore Processing\",\n  \"sortnum\": 6,\n  \"description\": \"One of the many features presented by Industrial Revolution is it's capability for even quadrupling ores.\",\n  \"icon\": \"indrev:copper_chunk\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/machines.json",
    "content": "{\n  \"name\": \"Machines\",\n  \"sortnum\": 1,\n  \"description\": \"Machines are the main focus for this mod. The following pages will show you their recipes and provide a little more information about them.\",\n  \"icon\": \"indrev:electric_furnace_mk1\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/natural_resources.json",
    "content": "{\n  \"name\": \"Natural Resources\",\n  \"sortnum\": 0,\n  \"description\": \"Information about needed natural resources to explore the Industrial Revolution.\",\n  \"icon\": \"indrev:tin_ore\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/temperature.json",
    "content": "{\n  \"name\": \"Temperature\",\n  \"sortnum\": 4,\n  \"description\": \"Some machines will present features like temperature, which can be checked inside it's interface. This section will talk more about it.\",\n  \"icon\": \"indrev:fan\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/tools.json",
    "content": "{\n  \"name\": \"Tools\",\n  \"sortnum\": 5,\n  \"description\": \"Tools are also important in this mod.\",\n  \"icon\": \"indrev:hammer\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/transportation.json",
    "content": "{\n  \"name\": \"Transportation\",\n  \"sortnum\": 2,\n  \"description\": \"Industrial Revolution also provides you ways of transporting energy, items and fluids through your world!\",\n  \"icon\": \"indrev:cable_mk1\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/categories/upgrades.json",
    "content": "{\n  \"name\": \"Upgrades\",\n  \"sortnum\": 3,\n  \"description\": \"Upgrades can boost machines stats.\",\n  \"icon\": \"indrev:speed_enhancer\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/enhanced_ore_processing/doubling.json",
    "content": "{\n  \"name\": \"Doubling Ores\",\n  \"category\": \"indrev:enhanced_ore_processing\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:pulverizer_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Ore doubling is the first step for enhanced ore processing.$(br)To get started, you'll need a $(l:machines/basic_machines#pulverizer)$(t:Go to Pulverizer)$(l)$(o)Pulverizer$().$(br)The Pulverizer will turn 1 ore into 2 dusts which can then be smelted into 2 ingots.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/enhanced_ore_processing/quadrupling.json",
    "content": "{\n  \"name\": \"Quadrupling Ores\",\n  \"category\": \"indrev:enhanced_ore_processing\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:fluid_infuser_mk4\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Ore quadrupling is the last step for enhanced ore processing. For this, you'll need a $(l:machines/basic_machines#fluid_infuser)$(t:Go to Fluid Infuser)$(l)$(o)Fluid Infuser$(), a $(l:machines/basic_machines#smelter)$(t:Go to Industrial Smelter)$(l)$(o)Industrial Smelter$(), a $(l:machines/basic_machines#condenser)$(t:Go to Condenser)$(l)$(o)Condenser$(), a $(l:machines/basic_machines#pulverizer)$(t:Go to Pulverizer)$(l)$(o)Pulverizer$() and plenty of Sulfuric Acid.$(br)$(#3ead36)Sulfuric Acid$() can be found in lake forms at Swamps and can be crafted from $(#b3b332)Sulfur Dust$() + $(#88adf2)Water$() inside a Fluid Infuser.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)1)$() Insert your ore in a Fluid Infuser with Sulfuric Acid. It will purify them and give you the purified ore form. $(br)$(l)2)$() Insert your purified ores into an Industrial Smelter. It will smelt them into their fluid forms.$(br)$(l)3)$() Move your molten ore into a Condenser.$(br)$(l)4)$() Pulverize your Ore Chunk, which will then give you a dust.$(br)$(l)5)$() Smelt your dust.$(br2)Done! Now you've transformed 1 ore into 4 ingots!\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/enhanced_ore_processing/tripling.json",
    "content": "{\n  \"name\": \"Tripling Ores\",\n  \"category\": \"indrev:enhanced_ore_processing\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:smelter_mk4\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Ore tripling is the next step for enhanced ore processing. For this, you'll need a $(l:machines/basic_machines#smelter)$(t:Go to Industrial Smelter)$(l)$(o)Industrial Smelter$(), a $(l:machines/basic_machines#condenser)$(t:Go to Condenser)$(l)$(o)Condenser$() and a  $(l:machines/basic_machines#pulverizer)$(t:Go to Pulverizer)$(l)$(o)Pulverizer$().\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)1)$() Insert your ores into an Industrial Smelter. It will smelt them into their fluid forms.$(br)$(l)2)$() Move your molten ore into a Condenser. It will solidify them into a Ore Chunk.$(br)$(l)3)$() Pulverize your Ore Chunk, which will then give you a dust.$(br)$(l)4)$() Smelt your dust.$(br2)Done! Now you've transformed 1 ore into 3 ingots!\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/basic_machines.json",
    "content": "{\n  \"name\": \"Basic Machines\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:electric_furnace_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"These machines are used to transform resources into other more complex resources.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"electric_furnace\",\n      \"recipe\": \"indrev:shaped/electric_furnace_mk1\",\n      \"text\": \"The $(l)$(o)Electric Furnace $()does everything a normal furnace does but using Lazuli Flux instead of coal.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"pulverizer\",\n      \"recipe\": \"indrev:shaped/pulverizer_mk1\",\n      \"text\": \"The $(l)$(o)Pulverizer $()is mainly used for processing ores into two dusts.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"compressor\",\n      \"recipe\": \"indrev:shaped/compressor_mk1\",\n      \"text\": \"The $(l)$(o)Compressor $()is mainly used for compressing ingots into plates.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"infuser\",\n      \"recipe\": \"indrev:shaped/solid_infuser_mk1\",\n      \"text\": \"The $(l)$(o)Solid Infuser $()has the capability of mixin two ingredients into a different one.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"fluid_infuser\",\n      \"recipe\": \"indrev:shaped/fluid_infuser_mk1\",\n      \"text\": \"The $(l)$(o)Fluid Infuser $()is used for advanced infusing with fluids. It's main current use is for ore quadrupling.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"condenser\",\n      \"recipe\": \"indrev:shaped/condenser_mk4\",\n      \"text\": \"The $(l)$(o)Condenser $()is used for transforming molten ores into chunks which can be pulverized and then smelted into ingots.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"smelter\",\n      \"recipe\": \"indrev:shaped/smelter_mk4\",\n      \"text\": \"The $(l)$(o)Industrial Smelter $()is mainly used to smelt ores into liquid. It's mostly used for enhanced ore processing.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"recycler\",\n      \"recipe\": \"indrev:shaped/recycler_mk2\",\n      \"text\": \"The $(l)$(o)Recycler $()can process organic resources into biomass which can be used for energy generation.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/batteries.json",
    "content": "{\n  \"name\": \"Lazuli Flux Containers\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 3,\n  \"icon\": \"indrev:lazuli_flux_container_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"These machines are used to store Lazuli Flux.$(br)You can see blue and orange arrows on each side. Blue arrows represent energy input and orange arrows represent energy output. You can configure these arrows using a Screwdriver.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/lazuli_flux_container_mk1\",\n      \"recipe2\": \"indrev:shaped/lazuli_flux_container_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/lazuli_flux_container_mk3\",\n      \"recipe2\": \"indrev:shaped/lazuli_flux_container_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/cables.json",
    "content": "{\n  \"name\": \"Cables\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:cable_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"These machines are used to transfer Lazuli Flux to great distances.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cable_mk1\",\n      \"recipe2\": \"indrev:shaped/cable_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cable_mk3\",\n      \"recipe2\": \"indrev:shaped/cable_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/chopper.json",
    "content": "{\n  \"name\": \"Chopper\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 5,\n  \"icon\": \"indrev:chopper_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/chopper_mk1\",\n      \"text\": \"The $(l)Chopper$() is a machine focused in automating wood farming, and it also supports enhancement upgrades.$(br)Note that it requires certain items for it to function: (put them in the 2x2 square)\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Axe:$() if present in the inventory, will retrieve any wood and leaves in range (leaves will not consume durability)$(br2)$(l)Sapling:$() if present in the inventory, will be planted in every block possible inside it's range.$(br2)$(l)Bone meal:$() will be applied to any saplings inside it's range.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/factories.json",
    "content": "{\n  \"name\": \"Factories\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 9,\n  \"icon\": \"indrev:electric_furnace_factory_mk4\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Factories$() are improved versions of machines and they can process 5 items at once. They are also smart enough to split your items if you'd like.$(br2)Given their powerful state, it's not possible to insert this much power into a single block, which means you'll need to build a structure.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Required blocks$()$(br2)$(li)$(l)9x$() Frames$(br)$(li)$(l)6x$() Silos$(br)$(li)$(l)3x$() Intakes$(br)$(li)$(l)3x$() Ducts$(br)$(li)$(l)2x$() Cabinets$(br)$(li)$(l)1x$() Warning Strobe$(br)$(li)$(l)1x$() Controller$()$(br2)You'll be able to visualize the block positions by right clicking a factory.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/frame\",\n      \"recipe2\": \"indrev:shaped/silo\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/intake\",\n      \"recipe2\": \"indrev:shaped/duct\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cabinet\",\n      \"recipe2\": \"indrev:shaped/warning_strobe\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/controller\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/farmer.json",
    "content": "{\n  \"name\": \"Farmer\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 7,\n  \"icon\": \"indrev:farmer_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/farmer_mk1\",\n      \"text\": \"The $(l)Farmer$() is a machine focused in crop farming, and it also supports enhancement upgrades.$(br)Note that it requires some items for it to function: (put them in the 2x2 square)\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Seeds:$() if present will be planted in the farmer's range. Note that it will try to match a crop and its seeds so you can use multiple crops.$(br2)$(l)Bone meal:$() if present, will fertilize any crops possible within range.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/fisher.json",
    "content": "{\n  \"name\": \"Fisher\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 8,\n  \"icon\": \"indrev:fisher_mk2\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fisher_mk2\",\n      \"text\": \"The $(l)Fisher$() is a machine focused in fish farming, and it also supports enhancement upgrades.$(br)It's only requirement is a Fishing Rod. Note that enchantments are supported.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/generators.json",
    "content": "{\n  \"name\": \"Generators\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:coal_generator_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)$(o)Generators $()use other resources to create energy known as Lazuli Flux for your machines\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/coal_generator_mk1\",\n      \"text\": \"The $(l)$(o)Coal Generator $()can use, not only coal, but anything a furnace can to generate energy.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/solar_generator_mk1\",\n      \"recipe2\": \"indrev:shaped/solar_generator_mk3\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"The $(l)$(o)Solar Generator $()will generate energy as long as the sunlight can reach it.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/biomass_generator_mk3\",\n      \"text\": \"The $(l)$(o)Biomass Generator $()will burn $(#00ff00)biomass $()to generate energy.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/heat_generator\",\n      \"text\": \"The $(l)$(o)Heat Generator $()will consume lava inside it to generate energy. Pairs up well with the pump.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/miner.json",
    "content": "{\n  \"name\": \"Mining Rig Computer\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 4,\n  \"icon\": \"indrev:mining_rig_mk4\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/mining_rig_mk4\",\n      \"text\": \"The Mining Rig Computer has been developed to create virtual dimensions based on ore data cards, allowing you to harvest ores without modifying your world.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/drill_bottom\",\n      \"text\": \"Mining Rig Drills are required for the Mining Rig Computer to work. They have to be placed next to the Computer and require drill heads to work. Each Drill increases the mining speed.$(br)Screenshot of a functional setup:\"\n    },\n    {\n      \"type\": \"image\",\n      \"title\": \"Example\",\n      \"images\": [\"indrev:textures/gui/mrc_setup.png\"],\n      \"text\": \"Note that the Drills can be placed connected or diagonally to the Mining Rig Computer.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/ore_data_card\",\n      \"text\": \"Ore Data Cards store the data necessary for the Mining Rig Computer to virtualize a dimension based on the data written on it where it mines the ores.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/data_card_writer_mk4\",\n      \"text\": \"The Data Card Encoder will encode all the ores and modifiers into your Ore Data Card. You can write multiple times in a card to encode more data.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"When looking inside a Data Card Encoder, the ores and modifiers it can encode will be highlighted in your inventory.$(br2)When encoding, some attributes will change in your card, like richness or size.$(br2)$(br)Tip: search \\\"#indrev:mining_rig_allowed\\\" on REI to see all allowed ores!\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Attributes$() can be observed on your data card tooltip.$(br)These attributes can then be improved by modifiers.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Storage size$() can be seen by the circle UI in the tooltip. The blue circle represents how many cycles the Mining Rig Computer has left to search on the virtual dimension, which will then turn the circle red. At the end of each cycle, the ores found will be added to a connected inventory.$(br2)Each data slot on the Data Card Encoder will increase the total cycles on the card. It can also be increased by adding Stone as a modifier.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Types$() represents the ores that can be generated from this card. Note that the chance of generating each ore is proportional to the amount of the respective ore that was encoded in the card.$(br2)$(#FF1111)Be careful$(), encoding too many types can cause your data card to overflow and be unusable.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Richness$() represents the maximum amount of ores that can be generated from each cycle. $(br)This can be increased by adding $(#5300de)Enriched Nikolite Dust$() to the modifier slots.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Cycles$() information can be seen on the tooltip. After each cycle the ores will be inserted into a nearby inventory.$(br2)You can see the cycle progress on the Mining Rig Computer screen.$(br)Better Drill Heads can complete cycles faster.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/pump.json",
    "content": "{\n  \"name\": \"Pump\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 10,\n  \"icon\": \"indrev:pump_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/pump_mk1\",\n      \"text\": \"The $(l)Pump$() is used to pump fluids placed on the world into fluid pipes and tanks.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Usage:$() You must input energy from the top and take the fluid from the side it is facing. After receiving energy, it will drop down a pipe looking for fluids to pump.$(br2)There is no hard limit, except for the fluid's physics. As long as there is fluid touching the pump's pipe, source or not, it will look for a source block to pump. This means it can be used to drain lava lakes from the Nether.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/machines/rancher.json",
    "content": "{\n  \"name\": \"Rancher\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 6,\n  \"icon\": \"indrev:rancher_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/rancher_mk1\",\n      \"text\": \"The $(l)Rancher$() is a machine focused in automating animal farming, and it also supports enhancement upgrades.$(br)Note that it requires some items for it to function: (put them in the 2x2 square)\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Sword:$() if present in the inventory, will kill animals if there are more than 7 in its range.$(br2)$(l)Wheat/Seed/Carrot:$() if present in the inventory, will be fed to the respective animals in it's range.$(br2)$(l)Bucket:$() if present in the inventory, will collect milk from cows.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/lead_ore.json",
    "content": "{\n  \"name\": \"Lead Ore\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 3,\n  \"icon\": \"indrev:lead_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:lead_ore\",\n      \"text\": \"$(#575d61)$(l)Lead ore$() is uncommon and can be found from the layer 0 to 32 in both stone and deepslate variants.$(br)$(#575d61)$(l)Lead$() is used in the crafting of more advanced machines.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/nikolite_ore.json",
    "content": "{\n  \"name\": \"Nikolite Ore\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 5,\n  \"icon\": \"indrev:nikolite_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:nikolite_ore\",\n      \"text\": \"$(#008f8c)$(l)Nikolite ore$() is common and can be found from the layer 0 to 16 in both stone and deepslate variants.$(br)$(#008f8c)$(l)Nikolite$() is the main component used in $(l)INDREV$()'s machines, from basic to advanced.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/silver_ore.json",
    "content": "{\n  \"name\": \"Silver Ore\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:silver_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:lead_ore\",\n      \"text\": \"$(#c6e9e3)$(l)Silver ore$() is uncommon and can be found from the layer 0 to 32 in both stone and deepslate variants.$(br)$(#c6e9e3)$(l)Silver$() is used in the crafting of more advanced machines.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/sulfur_crystals.json",
    "content": "{\n  \"name\": \"Sulfur Crystal\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 6,\n  \"icon\": \"indrev:sulfur_crystal\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:sulfur_crystal\",\n      \"text\": \"$(#f5da68)$(l)Sulfur Crystals$() are common and can be found from the layer 0 to 16 in the overworld or up to layer 100 in the nether.$(br)$(#f5da68)$(l)Sulfur$() is used in the purification process of the advanced ore processing chain.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/tin_ore.json",
    "content": "{\n  \"name\": \"Tin Ore\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:tin_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:tin_ore\",\n      \"text\": \"$(#999999)$(l)Tin ore$() is common and can be found from the layer 0 to 48 in both stone and deepslate variants.$(br)$(#999999)$(l)Tin$() is heavily used in the crafting of machines.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/natural_resources/tungsten_ore.json",
    "content": "{\n  \"name\": \"Tungsten Ore\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 4,\n  \"icon\": \"indrev:deepslate_tungsten_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:deepslate_tungsten_ore\",\n      \"text\": \"$(#686b5b)$(l)Tungsten ore$() is rare and can be found from the layer 0 to 16 just deepslate variant.$(br)$(#686b5b)$(l)Tungsten$() is used in the crafting of resistant items like the Modular Armor.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/temperature/coolers_and_fans.json",
    "content": "{\n  \"name\": \"Keeping it cool!\",\n  \"category\": \"indrev:temperature\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:cooler_cell\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Coolers and fans will make the machine's temperature cool faster stay on its most efficient state even while it happens.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fan\",\n      \"text\": \"$(o)Sir, can I have your autograph? Please please pleaseeeeeeee$()\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cooler_cell\",\n      \"text\": \"$(o)Lame... I'm too cool for this$()\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/heatsink\",\n      \"text\": \"$(o)What? Were you expecting a pun? Sorry, no puns here Sir.$()\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/temperature/info.json",
    "content": "{\n  \"name\": \"How does it work?\",\n  \"category\": \"indrev:temperature\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:fan\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)INDREV's$() scientists have done a good job at making machines smart enough not to explode and to benefit from temperature. Every machine has an optimal temperature which will never exceed.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"In order for the machine to stay on its optimal temperature, it needs to cool down often. While it's cooling down, it will $(l)not$() have the efficiency boost.$(br)Check out $(l:temperature/coolers_and_fans)$(t:Go to Keeping it Cool!)Keeping it cool!$() to have 100% efficiency all the time!\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/energy_reader.json",
    "content": "{\n  \"name\": \"Energy Reader\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 4,\n  \"icon\": \"indrev:energy_reader\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/energy_reader\",\n      \"text\": \"Tells you the current stored energy in machines and, in some machines, also tells the energy consumption per tick.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/gamer_axe.json",
    "content": "{\n  \"name\": \"Gamer Axe\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 6,\n  \"icon\": \"indrev:gamer_axe\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"The Gamer Axe is the final gaming tool. It mines fast and deals great amount of damage when modules are installed.$(br)It may seem like just a stick, but wait until you activate it...$(br2)Use Right Click to activate it.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/gamer_axe\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/hammer.json",
    "content": "{\n  \"name\": \"Hammer\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:hammer\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/hammer\",\n      \"text\": \"Used to make plates out of ingots when you still don't have a compressor!\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/mining_drills.json",
    "content": "{\n  \"name\": \"Mining Drills\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 3,\n  \"icon\": \"indrev:mining_drill_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/mining_drill_mk1\",\n      \"recipe2\": \"indrev:shaped/mining_drill_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/mining_drill_mk3\",\n      \"recipe2\": \"indrev:shaped/mining_drill_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/modular_armor.json",
    "content": "{\n  \"name\": \"Modular Armor\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 5,\n  \"icon\": \"indrev:modular_armor_helmet\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"The Modular Armor is, on it's own, not much, but when you install modules, it can become very powerful!$(br2)Color modules do not have recipes, you must find them in dungeons.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"Module levels can be configured any way you want, go into the $(l)Controls$() menu and add a keybind to \\\"Modular item configuration.\\\"\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/modular_armor_helmet\",\n      \"recipe2\": \"indrev:shaped/modular_armor_chest\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/modular_armor_legs\",\n      \"recipe2\": \"indrev:shaped/modular_armor_boots\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/modular_workbench\",\n      \"text\": \"The modular workbench is used to install modules onto Modular Armor parts. Notice that installing modules cost time and some energy!\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/modular_core.json",
    "content": "{\n  \"name\": \"Core of Modularity\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 7,\n  \"icon\": \"indrev:modular_core\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/modular_core\",\n      \"text\": \"The Core of Modularity is the base for every modular tool here. However, it must be activated before being usable. To activate it, you'll need at least one Laser, one Capsule and a lot of stored energy (100M).\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/capsule\",\n      \"text\": \"The Capsule will hold your Core of Modularity during the process of activation.$(br)It will emit a redstone signal when it's finished.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/laser_emitter_mk4\",\n      \"text\": \"Laser Emitters should be placed facing the capsule with $(l)AN EXACT 3 BLOCKS DISTANCE$(). They store up to 2,5M LF and require a redstone signal to be toggled on.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"This process takes time but it goes faster for each laser emitter active. You must pay attention to these details to avoid problems:$(br)- Pointing the laser emitter to something other than the capsule or and empty capsule will result in explosion.$(br)- If the laser emitters run out of power during the process, all progress will be lost.$(br)- Make sure to turn off your laser emitters before retrieving the activated core.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/screwdriver.json",
    "content": "{\n  \"name\": \"Screwdriver\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:screwdriver\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/screwdriver\",\n      \"text\": \"The Screwdriver is used to configure machines input and outputs for items and fluids.$(br)Can also be used on Lazuli Flux Container to configurate energy sides.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/tools/wrench.json",
    "content": "{\n  \"name\": \"Wrench\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:wrench\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/wrench\",\n      \"text\": \"The Wrench can be used to rotate blocks and break machines keeping their energy when using it while crouching.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/transportation/energy.json",
    "content": "{\n  \"name\": \"Energy\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:cable_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Cables can be used to transfer power between machines far apart from each other.$(br)Each cable can transfer different amount of powers, which are described on the tooltip when Shift is pressed.$(br)If your machines are running low on power, you might try upgrading your cables or improving your power generation.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cable_mk1\",\n      \"recipe2\": \"indrev:shaped/cable_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cable_mk3\",\n      \"recipe2\": \"indrev:shaped/cable_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/transportation/fluid_pipes.json",
    "content": "{\n  \"name\": \"Fluids\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:fluid_pipe_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Fluid pipes can be used to transfer fluids between tanks far apart.$(br)Check the tooltips to see the amount of fluid that can be transferred.$(br)Pipes only transfer every second rather than every tick.$(br2)Servos are required, read the $(l:transportation/servos)$(t:Go to Servos)Servos$() chapter.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fluid_pipe_mk1\",\n      \"recipe2\": \"indrev:shaped/fluid_pipe_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fluid_pipe_mk3\",\n      \"recipe2\": \"indrev:shaped/fluid_pipe_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/transportation/item_pipes.json",
    "content": "{\n  \"name\": \"Items\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:item_pipe_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Item pipes can be used to transfer items between inventories far apart.$(br)Check the tooltips to see the amount of items that can be transferred.$(br)Pipes only transfer every second rather than every tick.$(br)Item pipes have $(l)advanced filtering$(), Right Click the pipe to see it.$(br2)Servos are required, read the $(l:transportation/servos)$(t:Go to Servos)Servos$() chapter.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/item_pipe_mk1\",\n      \"recipe2\": \"indrev:shaped/item_pipe_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/item_pipe_mk3\",\n      \"recipe2\": \"indrev:shaped/item_pipe_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/transportation/servos.json",
    "content": "{\n  \"name\": \"Servos\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 3,\n  \"icon\": \"indrev:servo_retriever\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Item and fluid pipes require servos to function. There are two types of servo: Output and Retriever.$(br)Click on the pipe connected to your container to attach a servo. Use a wrench to remove it.$(br)$(l)Important:$() The Output and Retriever servos ARE INDEPENDENT. You do not need both for your pipes to work. They have different purposes.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"For example, if you have a single inventory that needs to output to multiple inventories you just need a single Output Servo on the dispensing end.$(br2)If you have multiple inventories that needs to output to a single inventory you just need a single Retriever Servo on the receiving end.$(br2)$(l)IMPORTANT:$() make sure the machine is configured. Read $(l:tools/screwdriver)$(t:Go to Screwdriver!)the Screwdriver chapter!$()\"\n    },\n    {\n      \"type\": \"text\",\n      \"title\": \"Modes\",\n      \"text\": \"Servos have multiple modes to define priorities.$(br)Click on the servo to change it.$(br)$(l)Nearest first:$() the servo will look for the closest container.$(br)$(l)Furthest first:$() the servo will look for the furthest container.$(br)$(l)Random:$() the servo will chose a random container.$(br)$(l)Round Robin$() the servo will look for the container with the least amount of the transferred fluid/item.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/servo_output\",\n      \"text\": \"The Output Servo will push items/fluids to nearby containers UNLESS they are also marked as output.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/servo_retriever\",\n      \"text\": \"The Retriever Servo will pull items/fluids from nearby UNLESS they are also marked as retriever.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/upgrades/enhancement_upgrades.json",
    "content": "{\n  \"name\": \"Enhancers\",\n  \"category\": \"indrev:upgrades\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:speed_enhancer\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"These upgrades will improve certain aspects of the machine like speed and energy usage.$(br2)The machine will use upgrades inserted in the 4 right-side slots on its interface.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/speed_enhancer\",\n      \"text\": \"This upgrade will increase the machine's processing speed at cost of energy.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/buffer_enhancer\",\n      \"text\": \"This upgrade will increase the machine's internal energy capacity.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/blast_furnace_enhancer\",\n      \"text\": \"This upgrade will make the Electric Furnace accept Blast Furnace recipes.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/smoker_enhancer\",\n      \"text\": \"This upgrade will make the Electric Furnace accept Smoker recipes.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/en_us/entries/upgrades/tier_upgrades.json",
    "content": "{\n  \"name\": \"Tier Upgrades\",\n  \"category\": \"indrev:upgrades\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:tier_upgrade_mk2\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"These upgrades will improve the machine's input and output capability as well as other aspects.$(br2)Right clicking on a machine below the upgrade's tier will upgrade it.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/tier_upgrade_mk2\",\n      \"text\": \"Upgrades MK1 machines to MK2.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/tier_upgrade_mk3\",\n      \"text\": \"Upgrades MK2 machines to MK3.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/tier_upgrade_mk4\",\n      \"text\": \"Upgrades MK3 machines to MK4.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/book.json",
    "content": "{\n  \"name\": \"item.patchouli.industrial_revolution_book.name\",\n  \"landing_text\": \"item.patchouli.industrial_revolution_book.landing\",\n  \"model\": \"indrev:guide_book\",\n  \"version\": 1,\n  \"subtitle\": \"Всё, что тебе, $(playername), положено знать о $(l)$(o)Industrial Revolution.\",\n  \"creative_tab\": \"indrev.indrev_group\",\n  \"show_progress\": false,\n  \"pause_game\": false\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/enhanced_ore_processing.json",
    "content": "{\n  \"name\": \"Улучшенная переработка руды\",\n  \"sortnum\": 6,\n  \"description\": \"Одна из многих особенностей, представленных Industrial Revolution — это способность даже учетверять руды.\",\n  \"icon\": \"indrev:copper_chunk\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/machines.json",
    "content": "{\n  \"name\": \"Станки\",\n  \"sortnum\": 1,\n  \"description\": \"Станки являются основным фокусом данного мода. Следующие страницы покажут тебе их рецепты и предоставят чуть больше информации о них.\",\n  \"icon\": \"indrev:electric_furnace_mk1\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/natural_resources.json",
    "content": "{\n  \"name\": \"Полезные ископаемые\",\n  \"sortnum\": 0,\n  \"description\": \"Информация о необходимых полезных ископаемых для изучения Industrial revolution.\",\n  \"icon\": \"indrev:tin_ore\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/temperature.json",
    "content": "{\n  \"name\": \"Температура\",\n  \"sortnum\": 4,\n  \"description\": \"В некоторых машин присутствуют такие характеристики, как температура, которую можно проверить внутри интерфейса. Этот раздел расскажет о ней больше.\",\n  \"icon\": \"indrev:fan\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/tools.json",
    "content": "{\n  \"name\": \"Инструменты\",\n  \"sortnum\": 5,\n  \"description\": \"Инструменты также являются немаловажными в этом моде.\",\n  \"icon\": \"indrev:hammer\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/transportation.json",
    "content": "{\n  \"name\": \"Транспортирование\",\n  \"sortnum\": 2,\n  \"description\": \"Industrial Revolution также предусматривает способы транспортировки энергии, предметов и жидкостей!\",\n  \"icon\": \"indrev:cable_mk1\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/categories/upgrades.json",
    "content": "{\n  \"name\": \"Улучшения\",\n  \"sortnum\": 3,\n  \"description\": \"Улучшения могут повысить характеристики машин.\",\n  \"icon\": \"indrev:speed_enhancer\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/enhanced_ore_processing/doubling.json",
    "content": "{\n  \"name\": \"Удваивание руд\",\n  \"category\": \"indrev:enhanced_ore_processing\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:pulverizer_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Удваивание руд — это первый шаг для улучшенной переработки руды.$(br)Чтобы начать, тебе понадобится $(l:machines/basic_machines#pulverizer)$(t:Перейди к Измельчителю)$(l)$(o)Измельчитель$().$(br)Измельчитель переработает одну руду в две пыли, чтобы затем их можно было переплавить в два слитка.\"\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/enhanced_ore_processing/quadrupling.json",
    "content": "{\n  \"name\": \"Учетверение руд\",\n  \"category\": \"indrev:enhanced_ore_processing\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:fluid_infuser_mk4\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Учетверение руды — это последний шаг для улучшенной переработки руды. Для этого, тебе понадобится $(l:machines/basic_machines#fluid_infuser)$(t:Перейди к Жидкостному настойнику)$(l)$(o)Жидкостный настойник$(), $(l:machines/basic_machines#smelter)$(t:Перейди к Производственной плавильной печи)$(l)$(o)Производственная плавильная печь$(), $(l:machines/basic_machines#condenser)$(t:Перейди к Конденсатору)$(l)$(o)Конденсатор$(), $(l:machines/basic_machines#pulverizer)$(t:Перейди к Измельчителю)$(l)$(o)Измельчитель$() и много Серной кислоты.$(br)$(#3ead36)Серную кислоту$() можно найти в болотных озера, а также её можно создать из $(#b3b332)Серной пыли$() + $(#88adf2)Воды$() в Жидкостном настойнике.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)1)$(). Помести руду в Жидкостный настойник со Серной кислотой. Он их очистит и выдаст очищенную форму руды.$(br)$(l)2)$(). Помести очищенные руды в Производственную плавильную печь. Она выплавит их в жидкие формы.$(br)$(l)3)$(). Перемести переплавленную руду в Конденсатор.$(br)$(l)4)$(). Измельчи рудный кусок, чтобы с него получить пыль.$(br)$(l)5)$() Переплавь пыль.$(br2)Готово! Теперь ты превращаешь одну руду в четыре слитка!\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/enhanced_ore_processing/tripling.json",
    "content": "{\n  \"name\": \"Утроение руд\",\n  \"category\": \"indrev:enhanced_ore_processing\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:smelter_mk4\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Утроение руды — это следующий шаг для улучшенной переработки руды. Для этого, тебе понадобится (l:machines/basic_machines#smelter)$(t:Перейди к Производственной плавильной печи) $(l)$(o)Производственная плавильная печь$(), $(l:machines/basic_machines#condenser)$(t:Перейди к Конденсатору)$(l)$(o)Конденсатор$() и $(l:machines/basic_machines#pulverizer)$(t:Перейди к Измельчителю)$(l)$(o)Измельчитель$().\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)1)$(). Помести руды в Производственную плавильную печь. Она выплавит их в жидкие формы.$(br)$(l)2)$(). Перемести переплавленную руду в Конденсатор. Он затвердит их в Рудный кусок.$(br)$(l)3)$(). Измельчи рудный кусок, чтобы с него получить пыль.$(br)$(l)4)$(). Переплавь пыль.$(br2)Готово! Теперь ты превращаешь одну руду в три слитка!\"\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/basic_machines.json",
    "content": "{\n  \"name\": \"Основные машины\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:electric_furnace_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Эти машины используются для превращения ресурсов в другие более сложные ресурсы.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"electric_furnace\",\n      \"recipe\": \"indrev:shaped/electric_furnace_mk1\",\n      \"text\": \"$(l)$(o)Электропечь$() делает то же, что и обычная печь, но с использованием Лазуритного флакса вместо угля.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"pulverizer\",\n      \"recipe\": \"indrev:shaped/pulverizer_mk1\",\n      \"text\": \"$(l)$(o)Измельчитель$() по большей части используется для обработки руды в две пыли.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"compressor\",\n      \"recipe\": \"indrev:shaped/compressor_mk1\",\n      \"text\": \"$(l)$(o)Компрессор$() по большей части используется для сжатия слитков в пластины.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"infuser\",\n      \"recipe\": \"indrev:shaped/solid_infuser_mk1\",\n      \"text\": \"$(l)$(o)Твердотельный настойник$() умеет смешивать два ингредиента в другой.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"fluid_infuser\",\n      \"recipe\": \"indrev:shaped/fluid_infuser_mk1\",\n      \"text\": \"$(l)$(o)Жидкостный настойник$() используется для Продвинутого вливания при помощи жидкости. В данный момент, обычно он используется для учетверения руды.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"condenser\",\n      \"recipe\": \"indrev:shaped/condenser_mk4\",\n      \"text\": \"$(l)$(o)Конденсатор$() используется для превращения переплавленных руд в куски, которых можно измельчить, а затем переплавить в слитки.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"smelter\",\n      \"recipe\": \"indrev:shaped/smelter_mk4\",\n      \"text\": \"$(l)$(o)Производственная плавильная печь$() в основном используется для переплавке руд в жидкие. Она обычно используется для улучшенной переработки руды.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"anchor\": \"recycler\",\n      \"recipe\": \"indrev:shaped/recycler_mk2\",\n      \"text\": \"$(l)$(o)Переработчик$() может обрабатывать органические ресурсы в биомассу, которую можно использовать для генерации энергии.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/batteries.json",
    "content": "{\n  \"name\": \"Лазуритовые флаксовые резервуары\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 3,\n  \"icon\": \"indrev:lazuli_flux_container_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Резервуары используются для хранения Лазуритного флакса (ЛФ).$(br)Ты можешь увидеть синие и оранжевые стрелки с обеих сторон. Синие стрелки изображают вход энергии, а оранжевые стрелки изображают выход энергии. Ты можешь настроить эти стрелки используя Гаечный ключ в Режиме конфигурации.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/lazuli_flux_container_mk1\",\n      \"recipe2\": \"indrev:shaped/lazuli_flux_container_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/lazuli_flux_container_mk3\",\n      \"recipe2\": \"indrev:shaped/lazuli_flux_container_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/cables.json",
    "content": "{\n  \"name\": \"Кабели\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:cable_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Кабели используются в машинах для передачи Лазуритного флакса на большие расстояния.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cable_mk1\",\n      \"recipe2\": \"indrev:shaped/cable_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cable_mk3\",\n      \"recipe2\": \"indrev:shaped/cable_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/chopper.json",
    "content": "{\n  \"name\": \"Лесоруб\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 5,\n  \"icon\": \"indrev:chopper_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/chopper_mk1\",\n      \"text\": \"$(l)Лесоруб$() — это машина предназначена для автоматической ферме дерева, а также она поддерживает улучшения.$(br)Заметь, что для её функционирования требуются определённые предметы: (положи их в квадрат 2х2).\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Топор:$() если имеется в инвентаре, будет извлекать любую древесину и листья в пределах досягаемости (листья не тратят прочность).$(br2)$(l)Саженцы:$() если имеются в инвентаре, будут посажены в каждый блок внутри его диапазона.$(br2)$(l)Костная мука:$() будет ускорять рост саженцев.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/factories.json",
    "content": "{\n  \"name\": \"Заводы\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 9,\n  \"icon\": \"indrev:electric_furnace_factory_mk4\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Заводы$() — это улучшенная версия машин и они могут обрабатывать до 5 предметов одновременно. Также они достаточно умны для разделения предметов, если ты этого захочешь.$(br2)Если учитывать их мощное состояние, невозможно поместить столько энергии в 1 блок, значит тебе нужно будет построить конструкцию.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Требуемые блоки:$()$(br2)$(li)$(l)9x$() Каркасы$(br)$(li)$(l)6x$() Силос$(br)$(li)$(l)3x$() Воздушные каналы$(br)$(li)$(l)3x$() Воздуховоды$(br)$(li)$(l)2x$() Корпуса$(br)$(li)$(l)1x$() Предупреждающие лампы$(br)$(li)$(l)1x$() Регулятор$()$(br2)Ты сможешь визуализировать положения блоков нажатием по заводу.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/frame\",\n      \"recipe2\": \"indrev:shaped/silo\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/intake\",\n      \"recipe2\": \"indrev:shaped/duct\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cabinet\",\n      \"recipe2\": \"indrev:shaped/warning_strobe\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/controller\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/farmer.json",
    "content": "{\n  \"name\": \"Фермер\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 7,\n  \"icon\": \"indrev:farmer_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/farmer_mk1\",\n      \"text\": \"$(l)Фермер$() — эта машина предназначена для земледелия, а также она поддерживает улучшения.$(br)Заметь, что для её функционирования требуются определённые предметы: (положи их в квадрат 2х2).\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Семена:$() если имеются они будут высаживаться в пределах фермера. Заметь, что он попытается сопоставить урожай и его семена (урожая), поэтому ты можешь использовать несколько посев.$(br2)$(l)Костная мука:$() если имеется, будет удобрять любые возможные посевы в пределах досягаемости.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/fisher.json",
    "content": "{\n  \"name\": \"Рыболов\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 8,\n  \"icon\": \"indrev:fisher_mk2\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fisher_mk2\",\n      \"text\": \"$(l)Рыболов$() — эта машина предназначена для рыбоводства, а также она поддерживает улучшения.$(br)Требуется лишь одно — Удочка. Учти, она поддерживает зачарования.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/generators.json",
    "content": "{\n  \"name\": \"Генераторы\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:coal_generator_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)$(o)Генераторы$() используют прочие ресурсы для создания энергии, так называемой Лазуритовый флакс для твоих машин.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/coal_generator_mk1\",\n      \"text\": \"$(l)$(o)Угольный генератор$() может использовать не только уголь, но и всё, что может генерировать энергию.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/solar_generator_mk1\",\n      \"recipe2\": \"indrev:shaped/solar_generator_mk3\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)$(o)Солнечный генератор$() производит энергию до тех пор, пока её охватывает солнечный свет.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/biomass_generator_mk3\",\n      \"text\": \"$(l)$(o)Генератор на биомассе$() сжигает $(#00ff00)Биомассу$() для производства энергии.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/heat_generator\",\n      \"text\": \"$(l)$(o)Тепловой генератор$() израсходует лаву для производства энергии. Хорошо сочетается с насосом.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/miner.json",
    "content": "{\n  \"name\": \"Станочная буровая машина\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 4,\n  \"icon\": \"indrev:mining_rig_mk4\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/mining_rig_mk4\",\n      \"text\": \"Когда внутри $(l)$(o)Станочной буровой машины$() размещён $(l:machines/miner#chunk_scanner)$(t:Сканер чанка) он начнёт добывать $(l)$(o)$(#0000ff)просканированный$() чанк за стоимость энергии. Учти, что он не ломает блоки.$(br)Расходы ЭВМ: $(#1111FF)64ЛФ/тик$(). Также каждое сверло увеличит использование энергии на $(#1111FF)256ЛФ/тик$().\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/drill_bottom\",\n      \"text\": \"Для работы Станочной буровой машина нужны Свёрла бурового станка. Для работы машины ему нужны головки бура, и их нужно разместить параллельно. Каждое сверло увеличивает скорость добычи.$(br)Снимок экрана функциональной системы:\"\n    },\n    {\n      \"type\": \"image\",\n      \"title\": \"Пример\",\n      \"images\": [\"indrev:textures/gui/mrc_setup.png\"],\n      \"text\": \"Учти, что Свёрла можно разместить связано или по диагонали к Станочной буровой машины.\"\n    },\n    {\n      \"anchor\": \"chunk_scanner\",\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/chunk_scanner\",\n      \"text\": \"$(l)$(o)Сканер чанка$() — это инструмент используемый для сбора географических данных о типе жильного чанка. После сканирования, Сканер предоставит результаты, требующиеся для работы Станочной буровой машины.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Примечание:$()$(br)Как только прогресс Станочной буровой машины ударит 100%, буровой станок продолжит искать ещё и камень, землю и гравий вместо руды.$(br2)$(br)В данный момент здесь перечислены не все жилы.\"\n    },\n    {\n      \"anchor\": \"vein_types\",\n      \"type\": \"text\",\n      \"text\": \"$(br)$(l:machines/miner#peat)$(t:Перейти к Торфу)Торф$()$(br)$(l:machines/miner#lignite)$(t:Перейти к Бурому углю)Бурый уголь$()$(br)$(l:machines/miner#bituminous)$(t:Перейти к Каменному углю)Каменный уголь$()$(br)$(l:machines/miner#anthracite)$(t:Перейти к Антрациту)Антрацит$()$(br)$(l:machines/miner#siderite)$(t:Перейти к Сидериту)Сидерит$()$(br)$(l:machines/miner#limonite)$(t:Перейти к Лимониту)Лимонит$()$(br)$(l:machines/miner#hematite)$(t:Перейти к Гематиту)Гематит$()$(br)$(l:machines/miner#magnetite)$(t:Перейти к Магнетиту)Магнетит$()$(br)$(l:machines/miner#chalcopryte)$(t:Перейти к Халькопириту)Халькопирит$()$(br)$(l:machines/miner#cuprite)$(t:Перейти к Куприту)Куприт$()$(br)$(l:machines/miner#cassiterite)$(t:Перейти к Касситериту)Касситерит$()$(br)$(l:machines/miner#stannite)$(t:Перейти к Станниту)Станнит$()$(br)$(l:machines/miner#cavalerite)$(t:Перейти к Калавериту)Калаверит$()$(br)$(l:machines/miner#quartz)$(t:Перейти к Кварцу)Кварц$()$(br)$(l:machines/miner#nikolite)$(t:Перейти к Николиту)Николит$().\"\n    },\n    {\n      \"anchor\": \"peat\",\n      \"type\": \"spotlight\",\n      \"title\": \"Торф\",\n      \"item\": \"minecraft:coal_ore\",\n      \"text\": \"Обычно состоит из $(#000000)$(l)Угольной руды$(). Это самая наименьшая и беднейшая угольная жила, следовательно ты будешь получать много камня, земли и немного гравия.$(br)Руду можно найти в любых биомах, но ещё в равнинах, тайге, лесах, болотах и ледяных биомах.\"\n    },\n    {\n      \"anchor\": \"lignite\",\n      \"type\": \"spotlight\",\n      \"title\": \"Бурый уголь\",\n      \"item\": \"minecraft:coal_ore\",\n      \"text\": \"Обычно состоит из $(#000000)$(l)Угольной руды$(). Это вторая наименьшая и беднейшая угольная жила, следовательно здесь всё ещё много камня, земли и гравия.$(br)Руду можно найти в любых биомах, но ещё в равнинах, тайге, лесах, болотах и (дальше не написано).\"\n    },\n    {\n      \"anchor\": \"bituminous\",\n      \"type\": \"spotlight\",\n      \"title\": \"Каменный уголь\",\n      \"item\": \"minecraft:coal_ore\",\n      \"text\": \"Обычно состоит из $(#000000)$(l)Угольной руды$(). Это довольно большая и богатая угольная жила, следовательно ты найдёшь больше угля параллельно с камнем, землёй и гравием, но если тебе повезёт, возможно ты тоже найдёшь несколько $(#00ffff)$(l)Алмазов$().$(br)Руду  можно найти во многих местах, но ещё в пустынях, холмах, столовых гор, саванах.\"\n    },\n    {\n      \"anchor\": \"anthracite\",\n      \"type\": \"spotlight\",\n      \"title\": \"Антрацит\",\n      \"item\": \"minecraft:coal_ore\",\n      \"text\": \"Обычно состоит из $(#000000)$(l)Угольной руды$(). Это самая большая и самая богатая угольная жила, следовательно там тоже много угля, параллельно с камнем, землёй, гравием и несколькими $(#00ffff)$(l)Алмазами$().$(br)Руду можно найти во многих местах, но ещё в пустынях, холмах, столовых гор, саванах.\"\n    },\n    {\n      \"anchor\": \"siderite\",\n      \"type\": \"spotlight\",\n      \"title\": \"Сидерит\",\n      \"item\": \"minecraft:iron_ore\",\n      \"text\": \"Обычно состоит из $(#666666)$(l)Железной руды$(). Это наименьшая и беднейшая железная жила, с кучей камня, землёй и немного гравия.$(br)Руду можно найти во многих местах, но ещё в холмах, равнинах, саванах и лесах.\"\n    },\n    {\n      \"anchor\": \"limonite\",\n      \"type\": \"spotlight\",\n      \"title\": \"Лимонит\",\n      \"item\": \"minecraft:iron_ore\",\n      \"text\": \"Обычно состоит из $(#666666)$(l)Железной руды$(). Это вторая наименьшая и беднейшая железная руда, здесь много камня, земли и гравия.$(br)Руду можно найти во многих местах, но ещё в холмах, равнинах и пустынях.\"\n    },\n    {\n      \"anchor\": \"hematite\",\n      \"type\": \"spotlight\",\n      \"title\": \"Гематит\",\n      \"item\": \"minecraft:iron_ore\",\n      \"text\": \"Обычно состоит из $(#666666)$(l)Железной руды$(). Это довольно большая и богатая железная жила, с кучей железа параллельно с камнем, землёй и немного гравия.$(br)Руду можно найти во многих местах, но ещё в тайге и столовых гор.\"\n    },\n    {\n      \"anchor\": \"magnetite\",\n      \"type\": \"spotlight\",\n      \"title\": \"Магнетит\",\n      \"item\": \"minecraft:iron_ore\",\n      \"text\": \"Обычно состоит из $(#666666)$(l)Железной руды$(). Это самая большая и самая богатая железная жила, с кучей железа, параллельно с камнем, землёй и немного гравия.$(br)Руду можно найти во многих местах, но ещё в холмах, столовых гор и пустынях.\"\n    },\n    {\n      \"anchor\": \"chalcopryte\",\n      \"type\": \"spotlight\",\n      \"title\": \"Халькопирит\",\n      \"item\": \"minecraft:copper_ore\",\n      \"text\": \"Обычно состоит из $(#FFA500)$(l)Медной руды$() и $(#666666)$(l)Железной руды$(). Это довольно богатая и очень изменчивая жила, с кучей $(#FFA500)$(l)медной$() и $(#666666)$(l)железной$() руды параллельно с камнем, землёй и гравием.$(br)Руду можно найти во многих местах, но ещё в равнинах, саванах и лесах.\"\n    },\n    {\n      \"anchor\": \"cuprite\",\n      \"type\": \"spotlight\",\n      \"title\": \"Куприт\",\n      \"item\": \"minecraft:copper_ore\",\n      \"text\": \"Обычно состоит из $(#FFA500)$(l)Медной руды$() Это единственная жила, содержащая только медь, она не очень богатая и очень изменчива по размеру.$(br)Руду можно найти во многих местах, но ещё в саванах и пустынях.\"\n    },\n    {\n      \"anchor\": \"cassiterite\",\n      \"type\": \"spotlight\",\n      \"title\": \"Касситерит\",\n      \"item\": \"indrev:tin_ore\",\n      \"text\": \"Обычно состоит из $(#999999)$(l)Оловянной руды$(). Это единственная жила, содержащая только медь, она не очень богатая и очень изменчива по размеру.$(br)Руду можно найти во многих местах, но ещё в равнинах, саванах и лесах.\"\n    },\n    {\n      \"anchor\": \"stannite\",\n      \"type\": \"spotlight\",\n      \"title\": \"Станнит\",\n      \"item\": \"indrev:tin_ore\",\n      \"text\": \"Обычно состоит из $(#999999)$(l)Оловянной руды$(), $(#FFA500)$(l)Медной руды$() и $(#666666)$(l)Железной руды$(), с кучей олова, параллельно с $(#666666)$(l)железом$(), $(#FFA500)$(l)медью$(), камнем, землёй и гравием.$(br)Руду можно найти во многих местах, но ещё в холмах, столовых гор и пустынях.\"\n    },\n    {\n      \"anchor\": \"nikolite\",\n      \"type\": \"spotlight\",\n      \"title\": \"Николит\",\n      \"item\": \"indrev:nikolite_ore\",\n      \"text\": \"Обычно состоит из $(#5555BB)$(l)Николитовой руды$(), $(#FF0000)$(l)Редстоуновой руды$() и $(#0000FF)$(l)Лазуритовой руды$(), с кучей камня, земли и гравием. Руду можно найти во многих местах, но ещё в тайге и ледяных биомах.\"\n    },\n    {\n      \"anchor\": \"cavalerite\",\n      \"type\": \"spotlight\",\n      \"title\": \"Калаверит\",\n      \"item\": \"minecraft:gold_ore\",\n      \"text\": \"$(l)Надземный мир$().$(br) Обычно состоит из $(#FFD700)$(l)Золотой руды$(). Это малая и довольно богатая золотая жила.$(br)Руду можно найти во многих местах, но ещё в тайге и столовых гор.\"\n    },\n    {\n      \"type\": \"spotlight\",\n      \"title\": \"Незерский калаверит\",\n      \"item\": \"minecraft:nether_gold_ore\",\n      \"text\": \"$(#ff0000)$(l)Незер$().$(br) Это довольно большая и богатая жила $(#FFD700)$(l)Незерской золотой руды$(), с кучей незерака, песка душ, гравием, а если тебе очень повезёт, то немного $(l)$(#000000)Древних обломков$()!\"\n    },\n    {\n      \"anchor\": \"quartz\",\n      \"type\": \"spotlight\",\n      \"title\": \"Кварц\",\n      \"item\": \"minecraft:nether_quartz_ore\",\n      \"text\": \"$(#ff0000)$(l)Незер$().$(br) Это довольно большая и богатая $(#dddddd)$(l)Кварцевая$() жила, с кучей незерака, песка душ, гравием.\"\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/pump.json",
    "content": "{\n  \"name\": \"Насос\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 10,\n  \"icon\": \"indrev:pump_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/pump_mk1\",\n      \"text\": \"$(l)Насос$() используется для выкачивания жидкостей в жидкостные трубы и резервуары.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Использование:$() Тебе нужно ввести сверху энергию и взять жидкость со стороны, с которой она обращена. После получения энергии, он опустит трубу в поисках жидкости для выкачивания.$(br2)Нет жёстких ограничений, за исключением физики жидкости. Пока есть жидкость, касающаяся трубы насоса, источник или нет, он будет искать источник жидкости для выкачивания. Таким образом, его можно использовать для выкачивания лавовых берегов озера из незера.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/machines/rancher.json",
    "content": "{\n  \"name\": \"Скотовод\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 6,\n  \"icon\": \"indrev:rancher_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/rancher_mk1\",\n      \"text\": \"$(l)Скотовод$() — это машина предназначена для автоматизации животноводства, а также она поддерживает улучшения.$(br)Заметь, что для её функционирования требуются определённые предметы: (положи их в квадрат 2х2).\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)Меч:$() если имеется в инвентаре, будет убивать животных, если их больше, чем 7 в пределах его досягаемости.$(br2)$(l)Пшеница/Семя/Морковь:$() если имеются в инвентаре, будут кормить соответственных животных в пределах его досягаемости.$(br2)$(l)Ведро:$() если имеется в инвентаре, будет доить коров.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/lead_ore.json",
    "content": "{\n  \"name\": \"Свинцовая руда\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 3,\n  \"icon\": \"indrev:lead_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:lead_ore\",\n      \"text\": \"$(#575d61)$(l)Свинцовая руда$() — это редка руда и её можно найти в слое от 0 до 32 как в каменных, так и в глубиносланцевых вариантах.$(br)$(#575d61)$(l)Свинец$() используется в создании более продвинутых машин.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/nikolite_ore.json",
    "content": "{\n  \"name\": \"Николитовая руда\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 5,\n  \"icon\": \"indrev:nikolite_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:nikolite_ore\",\n      \"text\": \"$(#008f8c)$(l)Николитовая руда$() — это редкая руда и её можно найти в слое от 0 до 16 как в каменных, так и в глубиносланцевых вариантах.$(br)$(#008f8c)$(l)Николит$() — это главный компонент, использующийся как в основных, так и в продвинутых $(l)INDREV$() машин.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/silver_ore.json",
    "content": "{\n  \"name\": \"Серебряная руда\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:silver_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:lead_ore\",\n      \"text\": \"$(#c6e9e3)$(l)Серебряная руда$() — это редка руда и её можно найти в слое от 0 до 32 как в каменных, так и в глубиносланцевых вариантах.$(br)$(#c6e9e3)$(l)Серебро$() используется в создании более продвинутых машин.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/sulfur_crystals.json",
    "content": "{\n  \"name\": \"Серный кристалл\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 6,\n  \"icon\": \"indrev:sulfur_crystal\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:sulfur_crystal\",\n      \"text\": \"$(#f5da68)$(l)Серные кристаллы$() — это распространённые кристаллы, их можно найти в слое от 0 до 16 в надземном мире или до сотого слоя в незере.$(br)$(#f5da68)$(l)Сера$() используется в процессе очистки цепной продвинутой переработки руды.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/tin_ore.json",
    "content": "{\n  \"name\": \"Оловянная руда\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:tin_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:tin_ore\",\n      \"text\": \"$(#999999)$(l)Оловянная руда$() — это распространённая руда, её можно найти в слое от 0 до 48 как в каменных, так и в глубиносланцевых вариантах.$(br)$(#999999)$(l)Олово$() интенсивно используется в создании машин.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/natural_resources/tungsten_ore.json",
    "content": "{\n  \"name\": \"Вольфрамовая руда\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 4,\n  \"icon\": \"indrev:deepslate_tungsten_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:deepslate_tungsten_ore\",\n      \"text\": \"$(#686b5b)$(l)Вольфрамовая руда$() — это редкая руда, её можно найти в слое от 0 до 16 только в глубинносланцевом варианте.$(br)$(#686b5b)$(l)Вольфрам$() используется в создании прочных предметов, таких как: Модульная броня.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/temperature/coolers_and_fans.json",
    "content": "{\n  \"name\": \"Охлаждение!\",\n  \"category\": \"indrev:temperature\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:cooler_cell\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Охладители и вентиляторы заставят быстрее охлаждать температуру машины, держа её в наиболее эффективном состоянии, даже когда это произойдёт они будут сохранять порыв охлаждения.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fan\",\n      \"text\": \"$(o)Могу я получить твой автограф? Прошу, прошу, прошу-у-у-у-у-у$().\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cooler_cell\",\n      \"text\": \"$(o)Неубедительно... Я слишком крут для этого$().\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/heatsink\",\n      \"text\": \"$(o)Что? Ты ожидал каламбур? Прости, здесь нет каламбуров.$()\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/temperature/info.json",
    "content": "{\n  \"name\": \"Как это работает?\",\n  \"category\": \"indrev:temperature\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:fan\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Учёные $(l)INDREV$() хорошо справились с работой в создании достаточно умных, не взрывающихся машин, извлекающие пользу от температуры. У каждого машины оптимальная, никогда не превышающаяся температура.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"Для того, чтобы машина оставалась на своей оптимальной температуре, ей понадобится частое охлаждение. Пока он охлаждается, у неё $(l)не$() будет повышенной эффективности.$(br)Проверь $(l:temperature/coolers_and_fans)$(t:Перейти к Охлаждению!)Охлаждение!$() для достижения постоянной 100% эффективности!\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/energy_reader.json",
    "content": "{\n  \"name\": \"Считыватель энергии\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 4,\n  \"icon\": \"indrev:energy_reader\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/energy_reader\",\n      \"text\": \"Расскажет о текущей запасённой энергии в машинах, а также расскажет о потребления энергии в тик.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/gamer_axe.json",
    "content": "{\n  \"name\": \"Геймерский топор\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 6,\n  \"icon\": \"indrev:gamer_axe\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Геймерский топор является финальным игровым инструментом. Он быстро копает и наносит большое количество урона при установленных модулях.$(br)Это может показаться просто палкой, но подожди, когда ты его активируешь...$(br2)Для активации нажми пкм.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/gamer_axe\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/hammer.json",
    "content": "{\n  \"name\": \"Молот\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:hammer\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/hammer\",\n      \"text\": \"Используется для изготовления пластин из слитков, пока у тебя по-прежнему нет Компрессора!\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/mining_drills.json",
    "content": "{\n  \"name\": \"Шахтёрские буры\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 3,\n  \"icon\": \"indrev:mining_drill_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/mining_drill_mk1\",\n      \"recipe2\": \"indrev:shaped/mining_drill_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/mining_drill_mk3\",\n      \"recipe2\": \"indrev:shaped/mining_drill_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/modular_armor.json",
    "content": "{\n  \"name\": \"Модульная броня\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 5,\n  \"icon\": \"indrev:modular_armor_helmet\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Модульная броня сама по себе не так уж так себе, но когда ты установишь модули, она может стать очень мощной!$(br2)Цветные модули без рецепта, ты можешь их найти в подземельях.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"Уровни модуля можно настроить любым для тебя удобным способом, войди в меню $(l)Управления$() и добавь связанную клавишу к \\\"Конфигурации модульного предмета.\\\"\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/modular_armor_helmet\",\n      \"recipe2\": \"indrev:shaped/modular_armor_chest\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/modular_armor_legs\",\n      \"recipe2\": \"indrev:shaped/modular_armor_boots\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/modular_workbench\",\n      \"text\": \"Модульный верстак используется для установки модулей к деталям Модульной брони. Учти, что установка модулей требует время и энергии!\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/modular_core.json",
    "content": "{\n  \"name\": \"Ядро модульности\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 7,\n  \"icon\": \"indrev:modular_core\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/modular_core\",\n      \"text\": \"Ядро модульности — это основа для каждого здесь модульного инструмента. Однако, до практичного использования его нужно активировать. Чтобы его активировать, тебе как минимум нужен один Лазер, одна капсула и много запасённой энергии (100 млн).\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/capsule\",\n      \"text\": \"Капсула будет сдерживать Ядро модульности во время процесса активации.$(br)После завершения процесса, она излучит редстоун-сигнал.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/laser_emitter_mk4\",\n      \"text\": \"Лазерных излучателей следует разместить лицом к капсуле на $(l)ДИСТАНЦИИ СТРОГО 3-Х БЛОКОВ$(). Они хранят до 2,5 млн ЛФ, а чтобы их подключить требуется редстоун-сигнал.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"Этот процесс требует длительного времени, но оно ускорится за счёт активированного Лазерного излучателя. Ты должен обратить внимание на эти детали для избежания проблем:$(br)- Направив лазерный излучатель на что-то другое, а не на капсулу и/или пустую капсулу приведёт к взрыву.$(br)- Если лазерные излучатели израсходуют энергию во время процесса, весь прогресс будет потерян.$(br)- Убедись, что выключил Лазерные излучатели, прежде чем получить активированное ядро.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/screwdriver.json",
    "content": "{\n  \"name\": \"Отвёртка\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:screwdriver\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/screwdriver\",\n      \"text\": \"Отвёртка используется для настройки входов/выходов машин для предметов и жидкостей.$(br)Также можно использовать в Лазуритовом флаксовом резервуаре для настройки входа/выхода энергии.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/tools/wrench.json",
    "content": "{\n  \"name\": \"Гаечный ключ\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:wrench\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/wrench\",\n      \"text\": \"Гаечный ключ используется для вращения блоков и ломании машин, сохранив свой запас энергии при использовании с Shift по машине.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/transportation/energy.json",
    "content": "{\n  \"name\": \"Энергия\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:cable_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Кабели можно использовать для передачи энергии между машинами на большом расстоянии друг от друга.$(br)Каждый кабель может передавать определённое количество энергии, которые описываются в подсказке при нажатии Shift.$(br)Если машины работают на малой энергопотреблении, ты можешь попробовать обновить кабели или улучшить свою генерацию энергии.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cable_mk1\",\n      \"recipe2\": \"indrev:shaped/cable_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cable_mk3\",\n      \"recipe2\": \"indrev:shaped/cable_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/transportation/fluid_pipes.json",
    "content": "{\n  \"name\": \"Жидкости\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:fluid_pipe_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Жидкостные трубы можно использовать для передачи жидкостей между резервуарами на большом расстоянии друг от друга.$(br)Чтобы посмотреть сколько можно передать количество жидкости, проверь подсказки.$(br)Трубы передают жидкость только в секундах, а не в тиках.$(br2)Понадобятся сервоприводы, прочти главу $(l:transportation/servos)$(t:Перейти к Сервоприводы)Сервоприводы$().\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fluid_pipe_mk1\",\n      \"recipe2\": \"indrev:shaped/fluid_pipe_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fluid_pipe_mk3\",\n      \"recipe2\": \"indrev:shaped/fluid_pipe_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/transportation/item_pipes.json",
    "content": "{\n  \"name\": \"Предметы\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:item_pipe_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Предметные трубы можно использовать для передачи предметов между инвентарями на большом расстоянии друг от друга.$(br)Чтобы посмотреть сколько можно передать количество предметов, проверь подсказки.$(br)Трубы передают жидкость только в секундах, а не в тиках.$(br)Предметные трубы имеют $(l)продвинутую фильтрацию$(), чтобы посмотреть нажми пкм трубой.$(br2)Понадобятся сервоприводы, прочти главу $(l:transportation/servos)$(t:Перейти к Сервоприводы)Сервоприводы$().\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/item_pipe_mk1\",\n      \"recipe2\": \"indrev:shaped/item_pipe_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/item_pipe_mk3\",\n      \"recipe2\": \"indrev:shaped/item_pipe_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/transportation/servos.json",
    "content": "{\n  \"name\": \"Сервоприводы\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 3,\n  \"icon\": \"indrev:servo_retriever\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Для функционирования предметных/жидкостных труб понадобятся сервоприводы. Есть 2 типа сервопривода: Сервопривод-извлекатель/Сервопривод вывода.$(br)Чтобы прикрепить сервопривод к трубе, присоединённой к инвентарю нажми Лкм. Для снятия сервопривода используй Гаечный ключ.$(br)$(l)Важно:$()Сервопривод-извлекатель и вывода — НЕЗАВИСИМЫ. Для работы трубы, тебе не нужны сразу оба сервопривода. У них разные назначения.\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"Например, если у тебя один инвентарь, который нужно вывести в несколько инвентарей, тебе просто понадобится один Сервопривод вывода на конце выдачи.$(br2)Если у тебя несколько инвентарей, которых нужно вывести в один инвентарь, тебе просто понадобится один Сервопривод-извлекатель в конце приёма.$(br2)$(l)ВАЖНО:$() удостоверься, что машина настроена. Прочти главу $(l:tools/wrench)$(t:Перейти к Гаечному ключу!)Гаечный ключ!$()\"\n    },\n    {\n      \"type\": \"text\",\n      \"title\": \"Режимы\",\n      \"text\": \"Для обозначения приоритетов у сервоприводов есть несколько режимов.$(br)Чтобы изменить режим нажми пкм держа сервопривод.$(br)$(l)Сначала ближайший:$() сервопривод ищет самый близкий контейнер.$(br)$(l)Сначала дальний:$() сервопривод ищет самый дальний контейнер.$(br)$(l)Случайный:$() сервопривод выбирает случайный контейнер.$(br)$(l)Циклически:$() сервопривод ищет контейнер с наименьшим числом переданной жидкости/предмета.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/servo_output\",\n      \"text\": \"Сервопривод вывода проталкивает предметы/жидкости в близлежащие контейнеры, ЕСЛИ они не отмечены как вывод.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/servo_retriever\",\n      \"text\": \"Сервопривод-извлекатель тянет предметы/жидкости из ближайших контейнеров, ЕСЛИ они не отмечены как извлечение.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/upgrades/enhancement_upgrades.json",
    "content": "{\n  \"name\": \"Усилители\",\n  \"category\": \"indrev:upgrades\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:speed_enhancer\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Эти улучшение улучшат некоторые аспекты машины, такие как скорость и использование энергии.$(br2)Машина использует внутри-вставленные улучшения в 4 слота, справа в интерфейсе.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/speed_enhancer\",\n      \"text\": \"Данное улучшение увеличит скорость обработки машины за стоимость энергии.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/buffer_enhancer\",\n      \"text\": \"Данное улучшение увеличит внутреннюю энергоёмкость машины.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/blast_furnace_enhancer\",\n      \"text\": \"Данное улучшение заставит Электропечь принимать рецепты от Доменной печи.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/smoker_enhancer\",\n      \"text\": \"Данное улучшение заставит Электропечь принимать рецепты от Коптильни.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/ru_ru/entries/upgrades/tier_upgrades.json",
    "content": "{\n  \"name\": \"Повышение уровня\",\n  \"category\": \"indrev:upgrades\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:tier_upgrade_mk2\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"Эти улучшения улучшают возможности ввода/вывода машины, но и другие аспекты.$(br2)Пкм по низкоуровневой машине, чтобы её улучшить.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/tier_upgrade_mk2\",\n      \"text\": \"Улучшает машины марк. 1 до марк. 2.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/tier_upgrade_mk3\",\n      \"text\": \"Улучшает машины марк. 2 до марк. 3.\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/tier_upgrade_mk4\",\n      \"text\": \"Улучшает машины марк. 3 до марк. 4.\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/enhanced_ore_processing.json",
    "content": "{\n  \"name\": \"高级矿物处理\",\n  \"sortnum\": 6,\n  \"description\": \"“工业革命”的一大亮点在于矿产的加倍甚至四倍处理。\",\n  \"icon\": \"indrev:copper_chunk\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/machines.json",
    "content": "{\n  \"name\": \"机器\",\n  \"sortnum\": 1,\n  \"description\": \"机器是本模组的核心内容。本节会介绍机器的合成配方及相关信息。\",\n  \"icon\": \"indrev:electric_furnace_mk1\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/natural_resources.json",
    "content": "{\n  \"name\": \"自然资源\",\n  \"sortnum\": 0,\n  \"description\": \"探索“工业革命”所必要的自然资源信息。\",\n  \"icon\": \"indrev:tin_ore\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/temperature.json",
    "content": "{\n  \"name\": \"温度\",\n  \"sortnum\": 4,\n  \"description\": \"一些机器具有温度属性，可在其界面内查看。本节将详细介绍。\",\n  \"icon\": \"indrev:fan\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/tools.json",
    "content": "{\n  \"name\": \"工具\",\n  \"sortnum\": 5,\n  \"description\": \"工具同样是本模组重要的一节。\",\n  \"icon\": \"indrev:hammer\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/transportation.json",
    "content": "{\n  \"name\": \"运输系统\",\n  \"sortnum\": 2,\n  \"description\": \"“工业革命”同样提供运输物体，液体与能量的解决方案！\",\n  \"icon\": \"indrev:cable_mk1\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/categories/upgrades.json",
    "content": "{\n  \"name\": \"升级\",\n  \"sortnum\": 3,\n  \"description\": \"升级可为机器提供增益效果。\",\n  \"icon\": \"indrev:speed_enhancer\"\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/enhanced_ore_processing/doubling.json",
    "content": "{\n  \"name\": \"双倍产矿\",\n  \"category\": \"indrev:enhanced_ore_processing\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:pulverizer_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"双倍产矿是高级矿物处理的第一步。$(br)你首先需要的是$(l:machines/basic_machines#pulverizer)$(t:跳转到“粉碎机”页面)$(l)$(o)粉碎机$()。$(br)粉碎机可以粉碎一个原矿并产出其对应的两个矿粉，而每个矿粉都可以被烧炼为一个锭。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/enhanced_ore_processing/quadrupling.json",
    "content": "{\n  \"name\": \"四倍产矿\",\n  \"category\": \"indrev:enhanced_ore_processing\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:fluid_infuser_mk4\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"高级矿物处理的最后一步！为此，你需要$(l:machines/basic_machines#fluid_infuser)$(t:跳转到“液体注入机”)$(l)$(o)液体注入机$()，三倍产矿需要的所有机器，以及充足的硫酸。$(br)$(#3ead36)硫酸$()可以在沼泽地的天然硫酸湖中获取，也可以在液体注入机中通过混合$(#b3b332)硫粉$()和$(#88adf2)水$()得到。\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)1)$()将矿物塞入液体注入机以获取精炼的矿石。$(br)$(l)2)$()接下来按照三倍产矿的操作即可获得四倍的产出啦！\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/enhanced_ore_processing/tripling.json",
    "content": "{\n  \"name\": \"三倍产矿\",\n  \"category\": \"indrev:enhanced_ore_processing\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:smelter_mk4\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"高级矿物处理再进一步则是三倍产矿。为此你需要一台$(l:machines/basic_machines#smelter)$(t:跳转到“工业熔炉”页面)$(l)$(o)工业熔炉$()，一台$(l:machines/basic_machines#condenser)$(t:跳转到“聚合机”页面)$(l)$(o)聚合机$()和一台$(l:machines/basic_machines#pulverizer)$(t:跳转到“粉碎机”页面)$(l)$(o)粉碎机$()。\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)1)$()将矿物放入工业熔炉，矿物会熔融为其对应的液体形式。$(br)$(l)2)$()将熔融的液体转移到至聚合机中，其则会固化为原矿碎块。$(br)$(l)3)$()将原矿碎块放入粉碎机中磨粉，又将会变为一个粉末。$(br)$(l)4)$()烧炼粉末。$(br2)锵锵~！你的一个矿就变成三个锭啦！\"\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/basic_machines.json",
    "content": "{\n    \"name\": \"基础机器\",\n    \"category\": \"indrev:machines\",\n    \"sortnum\": 1,\n    \"icon\": \"indrev:electric_furnace_mk1\",\n    \"pages\": [\n      {\n        \"type\": \"text\",\n        \"text\": \"这些机器可以将资源加工为更复杂的资源。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"anchor\": \"electric_furnace\",\n        \"recipe\": \"indrev:shaped/electric_furnace_mk1\",\n        \"text\": \"$(l)$(o)电炉$()就是普通的熔炉，不过用青金石通量运作而已。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"anchor\": \"pulverizer\",\n        \"recipe\": \"indrev:shaped/pulverizer_mk1\",\n        \"text\": \"$(l)$(o)粉碎机$()常用于矿物粉碎。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"anchor\": \"compressor\",\n        \"recipe\": \"indrev:shaped/compressor_mk1\",\n        \"text\": \"$(l)$(o)压缩机$()常用于将锭压成板。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"anchor\": \"infuser\",\n        \"recipe\": \"indrev:shaped/infuser_mk1\",\n        \"text\": \"$(l)$(o)固体注入机$()可以将两种材料混合为一种新材料。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"anchor\": \"fluid_infuser\",\n        \"recipe\": \"indrev:shaped/fluid_infuser_mk1\",\n        \"text\": \"$(l)$(o)液体注入机$()用于液体方面的制作和融合，目前的主要用途是四倍产矿。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"anchor\": \"condenser\",\n        \"recipe\": \"indrev:shaped/condenser_mk4\",\n        \"text\": \"$(l)$(o)聚合机$()用于将熔融的矿物聚合为矿物碎块。碎块可在经过磨粉工艺后重炼成锭。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"anchor\": \"smelter\",\n        \"recipe\": \"indrev:shaped/smelter_mk4\",\n        \"text\": \"$(l)$(o)工业熔炉$()主要用于熔化矿物，目前用于高级矿物处理。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"anchor\": \"recycler\",\n        \"recipe\": \"indrev:shaped/recycler_mk2\",\n        \"text\": \"$(l)$(o)回收机$()将有机物回收为生物质，其可用于发电。\"\n      }\n    ]\n  }"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/batteries.json",
    "content": "{\n    \"name\": \"青金石通量容器\",\n    \"category\": \"indrev:machines\",\n    \"sortnum\": 3,\n    \"icon\": \"indrev:lazuli_flux_container_mk1\",\n    \"pages\": [\n      {\n        \"type\": \"text\",\n        \"text\": \"这些容器可用于存储青金石通量。机身上可见的蓝色与橙色箭头标注了输入与输出接口，你可以使用螺丝刀进行接口的修改。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/lazuli_flux_container_mk1\",\n        \"recipe2\": \"indrev:shaped/lazuli_flux_container_mk2\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/lazuli_flux_container_mk3\",\n        \"recipe2\": \"indrev:shaped/lazuli_flux_container_mk4\"\n      }\n    ]\n  }"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/cables.json",
    "content": "{\n    \"name\": \"电缆\",\n    \"category\": \"machines\",\n    \"sortnum\": 2,\n    \"icon\": \"indrev:cable_mk1\",\n    \"pages\": [\n      {\n        \"type\": \"text\",\n        \"text\": \"这些线缆可用于远距离运输青金石通量。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/cable_mk1\",\n        \"recipe2\": \"indrev:shaped/cable_mk2\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/cable_mk3\",\n        \"recipe2\": \"indrev:shaped/cable_mk4\"\n      }\n    ]\n  }"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/chopper.json",
    "content": "{\n    \"name\": \"伐木机\",\n    \"category\": \"indrev:machines\",\n    \"sortnum\": 5,\n    \"icon\": \"indrev:chopper_mk1\",\n    \"pages\": [\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/chopper_mk1\",\n        \"text\": \"$(l)伐木机$()，顾名思义。作为伐木用的机器，同样支持安装各种增益升级。$(br)机器本身需要用于正常工作的用品，请将它们放在所示的2x2方格中。\"\n      },\n      {\n        \"type\": \"text\",\n        \"text\": \"$(l)斧：$()放入机器则会自动采伐工作范围内的木头和树叶（树叶不消耗耐久）$(br2)$(l)树苗：$()放入机器则会在工作范围内自动栽种。$(br2)$(l)骨粉：$()自动催熟工作范围内的树苗。\"\n      }\n    ]\n  }"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/factories.json",
    "content": "{\n  \"name\": \"工厂\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 9,\n  \"icon\": \"indrev:electric_furnace_factory_mk4\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)工厂$()是机器的升级版，最多可以批处理5个物品。还能智能平分物品到其各个槽位，悉听尊便。$(br2)这般强力自然不能塞进一个方块里，因此你需要组建多方块结构。\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)方块：$()$(br2)$(li)$(l)9x$()机器框架$(br)$(li)$(l)6x$()筒仓$(br)$(li)$(l)3x$()进风口$(br)$(li)$(l)3x$()通风管道$(br)$(li)$(l)2x$()柜子$(br)$(li)$(l)1x$()警示灯$(br)$(li)$(l)1x$()控制器$()$(br2)右击工厂方块本身可以查看多方块结构的构造。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/frame\",\n      \"recipe2\": \"indrev:shaped/silo\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/intake\",\n      \"recipe2\": \"indrev:shaped/duct\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cabinet\",\n      \"recipe2\": \"indrev:shaped/warning_strobe\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/controller\"\n    }\n  ]\n}\n"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/farmer.json",
    "content": "{\n  \"name\": \"耕作机\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 7,\n  \"icon\": \"indrev:farmer_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/farmer_mk1\",\n      \"text\": \"$(l)耕作机$()，顾名思义。作为耕作用的机器，同样支持安装各种增益升级。$(br)机器本身需要用于正常工作的用品，请将它们放在所示的2x2方格中。\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)种子：$()如果存在则会被放置在耕作机的工作范围内。耕作机本身会尝试匹配田中的作物，所以可以放入多种作物。$(br2)$(l)骨粉：$()如果存在则会试图催熟工作范围内的作物。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/fisher.json",
    "content": "{\n  \"name\": \"钓鱼机\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 8,\n  \"icon\": \"indrev:fishing_farm_mk2\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fishing_farm_mk2\",\n      \"text\": \"$(l)钓鱼机$()，顾名思义，钓鱼的机器。支持增益升级。$(br)只需要一个钓竿即可运行，支持附魔哦！\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/generators.json",
    "content": "{\n    \"name\": \"发电机\",\n    \"category\": \"indrev:machines\",\n    \"sortnum\": 0,\n    \"icon\": \"indrev:coal_generator_mk1\",\n    \"pages\": [\n      {\n        \"type\": \"text\",\n        \"text\": \"$(l)$(o)发电机$()通过产生名为青金石通量的能量来为你的机器供能。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/coal_generator_mk1\",\n        \"text\": \"$(l)$(o)煤炭发电机$()不只烧煤，任何熔炉里的能源都可以。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/solar_generator_mk1\",\n        \"recipe2\": \"indrev:shaped/solar_generator_mk3\"\n      },\n      {\n        \"type\": \"text\",\n        \"text\": \"$(l)$(o)太阳能发电机$()会在阳光直射下产生能量。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/biomass_generator_mk3\",\n        \"text\": \"$(l)$(o)生物质发电机$()通过燃烧$(#00ff00)生物质$()来产能。\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/heat_generator\",\n        \"text\": \"$(l)$(o)热能发电机$()通过吸收与其接触的$(#ff0000)熔岩$()或$(#ff0000)熔融下界合金$()来产能。\"\n      }\n    ]\n  }\n"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/miner.json",
    "content": "{\n    \"name\": \"自动挖矿钻机\",\n    \"category\": \"indrev:machines\",\n    \"sortnum\": 4,\n    \"icon\": \"indrev:mining_rig_mk4\",\n    \"pages\": [\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/miner_mk4\",\n        \"text\": \"将$(l)$(o)自动挖矿钻机$()放置在$(l:machines/miner#chunk_scanner)$(t:区块扫描仪)$(l)$(o)$(#0000ff)扫描过$()的区块中即可自动挖矿。它并不会破坏方块。计算机消耗64 LF/刻，而每一个钻头消耗256 LF/刻\"\n      },\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/drill_bottom\",\n        \"text\": \"自动挖矿钻机的运行需要钻头。钻头相对钻机本体斜向放置，且需安装钻机头部。每一个钻头都能加快运行速率。$(br)不废话，上图：\"\n      },\n      {\n        \"type\": \"image\",\n        \"title\": \"Example\",\n        \"images\": [\"indrev:textures/gui/mrc_setup.png\"],\n        \"text\": \"注意钻头相对钻机本体斜向放置，且钻机对准中间一格。\"\n      },\n      {\n        \"anchor\": \"chunk_scanner\",\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/chunk_scanner\",\n        \"text\": \"$(l)$(o)区块扫描仪$()可用于勘探当前区块的矿脉。扫描后获取的扫描报告即可放入矿机中使用。\"\n      },\n      {\n        \"type\": \"text\",\n        \"text\": \"$(l)注意：$()$(br)矿机采掘进度达到100%之后仅会继续产出石头，土，砂砾，而非矿物。$(br2)$(br)列表仅供参考，并未详细列举所有矿脉种类。\"\n      },\n      {\n        \"anchor\": \"vein_types\",\n        \"type\": \"text\",\n        \"text\": \"$(br)$(l:machines/miner#peat)$(t:查阅泥煤)泥煤$()$(br)$(l:machines/miner#lignite)$(t:查阅褐煤)褐煤$()$(br)$(l:machines/miner#bituminous)$(t:查阅沥青)沥青$()$(br)$(l:machines/miner#anthracite)$(t:查阅白煤)白煤$()$(br)$(l:machines/miner#siderite)$(t:查阅菱铁)菱铁$()$(br)$(l:machines/miner#limonite)$(t:查阅褐铁)褐铁$()$(br)$(l:machines/miner#hematite)$(t:查阅赤铁)赤铁$()$(br)$(l:machines/miner#magnetite)$(t:查阅磁铁)磁铁$()$(br)$(l:machines/miner#chalcopryte)$(t:查阅黄铜)黄铜$()$(br)$(l:machines/miner#cuprite)$(t:查阅赤铜)赤铜$()$(br)$(l:machines/miner#cassiterite)$(t:查阅锡石)锡石$()$(br)$(l:machines/miner#stannite)$(t:查阅黄锡)黄锡$()$(br)$(l:machines/miner#cavalerite)$(t:查阅碲金)碲金$()$(br)$(l:machines/miner#quartz)$(t:查阅石英)石英$()$(br)$(l:machines/miner#nikolite)$(t:查阅蓝石)蓝石$()\"\n      },\n      {\n        \"anchor\": \"peat\",\n        \"type\": \"spotlight\",\n        \"title\": \"泥煤\",\n        \"item\": \"minecraft:coal_ore\",\n        \"text\": \"主要为$(#000000)$(l)煤矿石$()最贫的煤矿脉，你会获得大量的石头，土，沙砾等。$(br)这些矿脉随处可见，但在平原、冻原、森林、沼泽和冰冷的生物群系中更常见。\"\n      },\n      {\n        \"anchor\": \"lignite\",\n        \"type\": \"spotlight\",\n        \"title\": \"褐煤\",\n        \"item\": \"minecraft:coal_ore\",\n        \"text\": \"主要为$(#000000)$(l)煤矿石$()，次贫的煤矿脉，你还是会获得大量的石头，土，沙砾等。$(br)这些矿脉随处可见，但在平原、冻原、森林、沼泽生物群系中更常见。\"\n      },\n      {\n        \"anchor\": \"bituminous\",\n        \"type\": \"spotlight\",\n        \"title\": \"沥青\",\n        \"item\": \"minecraft:coal_ore\",\n        \"text\": \"主要为$(#000000)$(l)煤矿石$()丰富而庞大的煤矿脉，也就是说除了石头，土，沙砾以外你还会找到很多很多煤炭。走运的话找到$(#00ffff)$(l)钻石$()也说不定哦。$(br)这些矿脉随处可见，但在沙漠，丘陵，平顶山和热带草原更常见。\"\n      },\n      {\n        \"anchor\": \"anthracite\",\n        \"type\": \"spotlight\",\n        \"title\": \"白煤\",\n        \"item\": \"minecraft:coal_ore\",\n        \"text\": \"主要为$(#000000)$(l)煤矿石$()丰度最高的煤矿脉，大量的煤炭以外也有少数石头，土，沙砾等。$(#00ffff)$(l)钻石$()也有呢。$(br)这些矿脉随处可见，但在沙漠，丘陵，平顶山和热带草原更常见。\"\n      },\n      {\n        \"anchor\": \"siderite\",\n        \"type\": \"spotlight\",\n        \"title\": \"菱铁\",\n        \"item\": \"minecraft:iron_ore\",\n        \"text\": \"主要为$(#666666)$(l)铁矿石$()，最贫的铁矿脉，你会获得大量的石头，土，沙砾等。$(br)这些矿脉随处可见，但在丘陵，平原，热带草原和森林更常见。\"\n      },\n      {\n        \"anchor\": \"limonite\",\n        \"type\": \"spotlight\",\n        \"title\": \"褐铁\",\n        \"item\": \"minecraft:iron_ore\",\n        \"text\": \"主要为$(#666666)$(l)铁矿石$()，次贫的铁矿脉，你还是会获得大量的石头，土，沙砾等。$(br)这些矿脉随处可见，但在丘陵，平原和沙漠更常见。\"\n      },\n      {\n        \"anchor\": \"hematite\",\n        \"type\": \"spotlight\",\n        \"title\": \"赤铁\",\n        \"item\": \"minecraft:iron_ore\",\n        \"text\": \"主要为$(#666666)$(l)铁矿石$()，丰富而庞大的铁矿脉，也就是说除了石头，土，沙砾以外你还会找到很多很多铁矿。$(br)这些矿脉随处可见，但在冻原和平顶山更常见。\"\n      },\n      {\n        \"anchor\": \"magnetite\",\n        \"type\": \"spotlight\",\n        \"title\": \"磁铁\",\n        \"item\": \"minecraft:iron_ore\",\n        \"text\": \"主要为$(#666666)$(l)铁矿石$()，丰度最高的铁矿脉，大量的铁矿以外也有少数石头，土，沙砾等。$(br)这些矿脉随处可见，但在丘陵，平顶山和沙漠更常见。\"\n      },\n      {\n        \"anchor\": \"chalcopryte\",\n        \"type\": \"spotlight\",\n        \"title\": \"黄铜\",\n        \"item\": \"minecraft:copper_ore\",\n        \"text\": \"主要为$(#FFA500)$(l)铜矿石$()和$(#666666)$(l)铁矿石$()，丰度较高，含量多变，$(#FFA500)$(l)铜$()和$(#666666)$(l)铁$()矿石众多，也有石头，土，沙砾等。$(br)这些矿脉随处可见，但在平原，热带草原和森林更常见。\"\n      },\n      {\n        \"anchor\": \"cuprite\",\n        \"type\": \"spotlight\",\n        \"title\": \"赤铜\",\n        \"item\": \"minecraft:copper_ore\",\n        \"text\": \"主要为$(#FFA500)$(l)铜矿石$()，少数只包含铜矿的矿脉，丰度不高，含量多变。$(br)这些矿脉随处可见，但在热带草原和沙漠更常见。\"\n      },\n      {\n        \"anchor\": \"cassiterite\",\n        \"type\": \"spotlight\",\n        \"title\": \"锡石\",\n        \"item\": \"indrev:tin_ore\",\n        \"text\": \"主要为$(#999999)$(l)锡矿石$()，少数只包含锡矿的矿脉，丰度不高，含量多变。$(br)这些矿脉随处可见，但在平原，热带草原和森林更常见。\"\n      },\n      {\n        \"anchor\": \"stannite\",\n        \"type\": \"spotlight\",\n        \"title\": \"黄锡\",\n        \"item\": \"indrev:tin_ore\",\n        \"text\": \"主要为$(#999999)$(l)锡矿石$()，$(#FFA500)$(l)铜矿石$()和$(#666666)$(l)铁矿石$()，锡矿众多，也含有少数石头，土，沙砾等。$(br)这些矿脉随处可见，但在丘陵，平顶山和沙漠更常见。\"\n      },\n      {\n        \"anchor\": \"nikolite\",\n        \"type\": \"spotlight\",\n        \"title\": \"蓝石\",\n        \"item\": \"indrev:nikolite_ore\",\n        \"text\": \"主要为$(#5555BB)$(l)蓝石矿石$()，$(#FF0000)$(l)红石矿石$()和$(#0000FF)$(l)青金石矿石$()，也有少数石头，土，沙砾等，这些矿脉随处可见，但在冻原和冰冷的生物群系更常见。\"\n      },\n      {\n        \"anchor\": \"cavalerite\",\n        \"type\": \"spotlight\",\n        \"title\": \"碲金\",\n        \"item\": \"minecraft:gold_ore\",\n        \"text\": \"$(l)主世界$()$(br)主要为$(#FFD700)$(l)金矿石$()，虽小却丰度颇高。$(br)这些矿脉随处可见，但在冻原和平顶山更常见。\"\n      },\n      {\n        \"type\": \"spotlight\",\n        \"title\": \"碲金 (下界)\",\n        \"item\": \"minecraft:nether_gold_ore\",\n        \"text\": \"$(#ff0000)$(l)下界$()$(br)庞大且丰度较高，有较多$(#FFD700)$(l)下界金矿石$()，下界岩，灵魂沙，沙砾等。走运的话也可能有$(l)$(#000000)远古残骸$()出土哦！\"\n      },\n      {\n        \"anchor\": \"quartz\",\n        \"type\": \"spotlight\",\n        \"title\": \"石英\",\n        \"item\": \"minecraft:nether_quartz_ore\",\n        \"text\": \"$(#ff0000)$(l)下界$()$(br)丰度极高且庞大的$(#dddddd)$(l)石英$()矿脉，也有下界岩，灵魂沙，沙砾等。\"\n      }\n    ]\n  }\n"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/pump.json",
    "content": "{\n  \"name\": \"液泵\",\n  \"category\": \"indrev:machines\",\n  \"sortnum\": 10,\n  \"icon\": \"indrev:pump_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/pump_mk1\",\n      \"text\": \"$(l)液泵$()可以从世界中汲取液体并输入流体管道或储罐之中。\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"$(l)使用方法：$()在其上方供能，其会延伸一个管道，伸入液面后即可收集液体。无论液体状态如何，只要管道和液面接触，其会自动搜寻最近的液体源方块，因此适用于下界岩浆湖的清空。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/machines/rancher.json",
    "content": "{\n    \"name\": \"放牧机\",\n    \"category\": \"machines\",\n    \"sortnum\": 6,\n    \"icon\": \"indrev:rancher_mk1\",\n    \"pages\": [\n      {\n        \"type\": \"crafting\",\n        \"recipe\": \"indrev:shaped/rancher_mk1\",\n        \"text\": \"$(l)放牧机$()可以自动放牧动物，同样支持机器升级。$(br)机器本身需要用于正常工作的用品，请将它们放在所示的2x2方格中。\"\n      },\n      {\n        \"type\": \"text\",\n        \"text\": \"$(l)剑:$()放入机器时会在工作范围内动物多于7只时自动杀死多余动物。$(br2)$(l)小麦/种子/胡萝卜：$()放入机器时会喂养工作范围内的动物。$(br2)$(l)桶：$()放入机器时会自动为牛挤奶。\"\n      }\n    ]\n  }"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/lead_ore.json",
    "content": "{\n  \"name\": \"铅矿石\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 3,\n  \"icon\": \"indrev:lead_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:lead_ore\",\n      \"text\": \"$(#575d61)$(l)铅矿石$()较不常见，在y轴0到32层生成普通或深层矿石。$(br)$(#575d61)$(l)铅$()常用于合成高级机器。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/nikolite_ore.json",
    "content": "{\n  \"name\": \"蓝石矿石\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 5,\n  \"icon\": \"indrev:nikolite_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:nikolite_ore\",\n      \"text\": \"$(#008f8c)$(l)蓝石矿石$()较为常见，在y轴0到16层生成普通或深层矿石。$(br)$(#008f8c)$(l)蓝石$()是$(l)“工业革命”$()的主要机械元件。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/silver_ore.json",
    "content": "{\n  \"name\": \"银矿石\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:silver_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:lead_ore\",\n      \"text\": \"$(#c6e9e3)$(l)银矿石$()较不常见，在y轴0到32层生成普通或深层矿石。$(br)$(#c6e9e3)$(l)银$()常用于合成高级机器。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/sulfur_crystals.json",
    "content": "{\n  \"name\": \"硫晶体\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 6,\n  \"icon\": \"indrev:sulfur_crystal\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:sulfur_crystal\",\n      \"text\": \"$(#f5da68)$(l)硫晶体$()较为常见，在主世界y周0到16层或下界100层以下生成。$(br)$(#f5da68)$(l)硫$()常用于高级矿物处理工序中的净化工艺。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/tin_ore.json",
    "content": "{\n  \"name\": \"锡矿石\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:tin_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:tin_ore\",\n      \"text\": \"$(#999999)$(l)锡矿石$()较为常见，在y轴0到48层生成普通或深层矿石。$(br)$(#999999)$(l)锡$()广泛应用于多种机器合成。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/natural_resources/tungsten_ore.json",
    "content": "{\n  \"name\": \"钨矿石\",\n  \"category\": \"indrev:natural_resources\",\n  \"sortnum\": 4,\n  \"icon\": \"indrev:deepslate_tungsten_ore\",\n  \"pages\": [\n    {\n      \"type\": \"spotlight\",\n      \"item\": \"indrev:deepslate_tungsten_ore\",\n      \"text\": \"$(#686b5b)$(l)钨矿石$()较为稀有，在y轴0到16层仅生成深层矿石。$(br)$(#686b5b)$(l)钨$()常用于合成如模块盔甲等强韧的物品。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/temperature/coolers_and_fans.json",
    "content": "{\n  \"name\": \"时刻保持高“冷”~\",\n  \"category\": \"indrev:temperature\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:cooler_cell\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"制冷液和风扇可降低机器的温度以使机器在特定的最适高效温度范围运行。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fan\",\n      \"text\": \"$(o)先生，我可是你的死忠“饭”！我可以要签名吗？签个名吧球球您了——$()\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cooler_cell\",\n      \"text\": \"$(o)冷酷无情就是我——$()\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/heatsink\",\n      \"text\": \"$(o)还想再来一个双关呢？两个就已经够冷了！$()\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/temperature/info.json",
    "content": "{\n  \"name\": \"机器如何运作？\",\n  \"category\": \"indrev:temperature\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:fan\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"“工业革命”的科学家™们用最前沿最先进的高精尖理论知识打造出了这些智能高效而，呃，不会爆炸的机器。$(br)并且，机器在其最适温度下运转效率最高。只要机器持续运作，达到这个温度时就会停止升温。\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"想要让机器达到最适温度，你需要时刻进行进行冷却，此时的运作速率$(l)并不会$()提升。更多请参阅$(l:temperature/coolers_and_fans)$(t:时刻保持高“冷”~)以时刻保持高效！\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/energy_reader.json",
    "content": "{\n  \"name\": \"能量示数器\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 4,\n  \"icon\": \"indrev:energy_reader\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/energy_reader\",\n      \"text\": \"查看当前机器存储的能量与能耗。对电缆也有用。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/gamer_axe.json",
    "content": "{\n  \"name\": \"头号玩家斧\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 6,\n  \"icon\": \"indrev:gamer_axe\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"头号玩家斧头，彰显你头号玩家的最终实力！可以高速砍伐，安装模块后还可以造成巨量伤害，可谓居家不二之选！$(br)跟棍子没两样？先右键启动试试……\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/gamer_axe\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/hammer.json",
    "content": "{\n  \"name\": \"锤子\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:hammer\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/hammer\",\n      \"text\": \"没有压缩机时，用这个把锭敲成板吧！\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/mining_drills.json",
    "content": "{\n  \"name\": \"采矿钻头\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 3,\n  \"icon\": \"indrev:mining_drill_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/mining_drill_mk1\",\n      \"recipe2\": \"indrev:shaped/mining_drill_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/mining_drill_mk3\",\n      \"recipe2\": \"indrev:shaped/mining_drill_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/modular_armor.json",
    "content": "{\r\n  \"name\": \"模块盔甲\",\r\n  \"category\": \"indrev:tools\",\r\n  \"sortnum\": 5,\r\n  \"icon\": \"indrev:modular_armor_helmet\",\r\n  \"pages\": [\r\n    {\r\n      \"type\": \"text\",\r\n      \"text\": \"模块盔甲本身……没那么出彩，但加装模块以后可以变得很强！$(br2)颜色模块没有合成配方，你只能在地牢里找到颜色模块。\"\r\n    },\r\n    {\r\n      \"type\": \"text\",\r\n      \"text\": \"模块的具体强度可以随意配置。请前往$(l)按键绑定$()菜单中设置“模块化物品设置”\"\r\n    },\r\n    {\r\n      \"type\": \"crafting\",\r\n      \"recipe\": \"indrev:shaped/modular_armor_helmet\",\r\n      \"recipe2\": \"indrev:modular_armor_chest\"\r\n    },\r\n    {\r\n      \"type\": \"crafting\",\r\n      \"recipe\": \"indrev:shaped/modular_armor_legs\",\r\n      \"recipe2\": \"indrev:modular_armor_boots\"\r\n    },\r\n    {\r\n      \"type\": \"crafting\",\r\n      \"recipe\": \"indrev:shaped/modular_workbench\",\r\n      \"text\": \"模块化工作台用于给模块化盔甲安装模块。请注意安装模块会消耗时间和能量！\"\r\n    }\r\n  ]\r\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/modular_core.json",
    "content": "{\n  \"name\": \"模块化核心\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 7,\n  \"icon\": \"indrev:modular_core\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/modular_core\",\n      \"text\": \"模块化核心，顾名思义，就是所有模块化物品的核心部件。当然，模块化核心需要激活才能使用。激活它至少需要一个激光发射器，一个微仓，和充足的能量（1亿）。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/capsule\",\n      \"text\": \"激活过程中，模块化核心须被放置在微仓内。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/laser_emitter_mk4\",\n      \"text\": \"激光发射器需要面向微仓放置在$(l)$(n)3格开外整$()$()。它们可以存储250万LF，且需要红石信号激活。\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"充能需要时间，但每一束激光都可以加快进程。$(br)$(n)$(o)必读！激光操作须知：$()$()$(br)- 激光打在$(n)非空微仓$()以外的地方会造成爆炸。$(br)- 充能完成之前掉电的话，所有进程都会被重置。$(br)- 在拿取核心之前记得关掉激光哦。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/screwdriver.json",
    "content": "{\n  \"name\": \"螺丝刀\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:screwdriver\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/screwdriver\",\n      \"text\": \"螺丝刀用于配置机器的流体/物品输入输出。$(br)也用于配置青金石通量容器的能量输入输出。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/tools/wrench.json",
    "content": "{\n  \"name\": \"扳手\",\n  \"category\": \"indrev:tools\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:wrench\",\n  \"pages\": [\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/wrench\",\n      \"text\": \"扳手可用于旋转或拆卸机器。扳手拆除的机器会保存电量。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/transportation/energy.json",
    "content": "{\n  \"name\": \"能量传输\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:cable_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"线缆可用于远距离跨机器传输能量。$(br)按住Shift查阅物品说明以查看每一种线缆的传输上限。$(br)机器没电了？改进产能和升级线缆都是好主意！\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cable_mk1\",\n      \"recipe2\": \"indrev:shaped/cable_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/cable_mk3\",\n      \"recipe2\": \"indrev:shaped/cable_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/transportation/fluid_pipes.json",
    "content": "{\n  \"name\": \"流体传输\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 2,\n  \"icon\": \"indrev:fluid_pipe_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"流体管道可用于远距离转运流体。$(br)物品说明附注明传输上限。$(br)管道每秒进行一次传输，而非每游戏刻。$(br2)管道需要伺服机构才能正常运作，请参阅$(l:transportation/servos)$(t:查阅伺服机构)伺服机构$()一章。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fluid_pipe_mk1\",\n      \"recipe2\": \"indrev:shaped/fluid_pipe_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/fluid_pipe_mk3\",\n      \"recipe2\": \"indrev:shaped/fluid_pipe_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/transportation/item_pipes.json",
    "content": "{\n  \"name\": \"物品传输\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:item_pipe_mk1\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"物品管道可用于远距离转运物品。$(br)物品说明附注明传输上限。$(br)管道每秒进行一次传输，而非每游戏刻。$(br)物品管道附有$(l)高级过滤$()功能，右击配置。$(br2)管道需要伺服机构才能正常运作，请参阅$(l:transportation/servos)$(t:查阅伺服机构)伺服机构$()一章。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/item_pipe_mk1\",\n      \"recipe2\": \"indrev:shaped/item_pipe_mk2\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/item_pipe_mk3\",\n      \"recipe2\": \"indrev:shaped/item_pipe_mk4\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/transportation/servos.json",
    "content": "{\n  \"name\": \"伺服机构\",\n  \"category\": \"indrev:transportation\",\n  \"sortnum\": 3,\n  \"icon\": \"indrev:servo_retriever\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"流体和物品管道需要两种伺服机构运作：推送机构和收取机构。$(br)右击一个已经连接容器的管道以安装伺服机构。使用扳手拆卸。$(br)$(l)注意：$()收取推送机构是相互独立的！二者用途各不相同，无需同时安装。\"\n    },\n    {\n      \"type\": \"text\",\n      \"text\": \"例如，假设你想从某一容器中提取物品并输向多个容器，你需要在这一容器的连接的管道上放置推送机构。$(br2)但如果你想从多个容器中抽取物品并抽向某一容器，则在这一容器的管道上放置收取机构。$(br2)$(l)注意：$()请提前配置好机器的输入输出！请查阅$(l:tools/screwdriver)$(t:查阅螺丝刀)“螺丝刀”条目。$()\"\n    },\n    {\n      \"type\": \"text\",\n      \"title\": \"Modes\",\n      \"text\": \"伺服机构存在检索优先级属性。$(br)右击伺服机构进行改动。$(br)$(l)近处优先：$()伺服机构会优先考虑近端的容器。$(br)$(l)远处优先：$()伺服机构会优先考虑远端的容器。$(br)$(l)随机：$()伺服机构会随机选取容器。$(br)$(l)轮询：$()伺服机构会优先考虑物品/液体存量最少的容器。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/servo_output\",\n      \"text\": \"推送机构会向相连的容器（容器输出侧除外）输送物品。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/servo_retriever\",\n      \"text\": \"收取机构会向相连的容器（容器输入侧除外）提取物品。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/upgrades/enhancement_upgrades.json",
    "content": "{\n  \"name\": \"机器增益\",\n  \"category\": \"indrev:upgrades\",\n  \"sortnum\": 0,\n  \"icon\": \"indrev:speed_enhancer\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"机器增益可提升机器的运行速度、改善能耗等等。$(br2)在机器界面右侧4个方格放入增益部件。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/speed_enhancer\",\n      \"text\": \"这个增益以能耗为代价增加机器的运行速率。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/buffer_enhancer\",\n      \"text\": \"这个增益可增加机器的能量容量。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/blast_furnace_enhancer\",\n      \"text\": \"这个增益允许电炉处理高炉配方。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/smoker_enhancer\",\n      \"text\": \"这个增益允许电炉处理烟熏炉配方。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/patchouli_books/indrev/zh_cn/entries/upgrades/tier_upgrades.json",
    "content": "{\n  \"name\": \"等级升级\",\n  \"category\": \"indrev:upgrades\",\n  \"sortnum\": 1,\n  \"icon\": \"indrev:tier_upgrade_mk2\",\n  \"pages\": [\n    {\n      \"type\": \"text\",\n      \"text\": \"等级升级可提升机器的输入输出能力等等。$(br2)手持等级升级部件右击低一等的机器即可升级。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/tier_upgrade_mk2\",\n      \"text\": \"将MK1机器升为MK2。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/tier_upgrade_mk3\",\n      \"text\": \"将MK2机器升为MK3。\"\n    },\n    {\n      \"type\": \"crafting\",\n      \"recipe\": \"indrev:shaped/tier_upgrade_mk4\",\n      \"text\": \"将MK3机器升为MK4。\"\n    }\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/bronze_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"bronze_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:bronze_dusts\"\n  },\n  \"result\": \"indrev:bronze_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/copper_ingot_from_ore.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"copper_ingots\",\n  \"ingredient\": {\n    \"tag\": \"minecraft:copper_ores\"\n  },\n  \"result\": \"minecraft:copper_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/copper_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"copper_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:copper_dusts\"\n  },\n  \"result\": \"minecraft:copper_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/electrum_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"electrum_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:electrum_dusts\"\n  },\n  \"result\": \"indrev:electrum_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/gold_ingot.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"ingredient\": {\n    \"tag\": \"c:gold_dusts\"\n  },\n  \"result\": \"minecraft:gold_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/iron_ingot_from_dust.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"iron_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:iron_dusts\"\n  },\n  \"result\": \"minecraft:iron_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/lead_ingot_from_ore.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"lead_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:lead_ores\"\n  },\n  \"result\": \"indrev:lead_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/lead_ingot_from_raw_ore.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"lead_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:raw_lead_ores\"\n  },\n  \"result\": \"indrev:lead_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/lead_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"lead_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:lead_dusts\"\n  },\n  \"result\": \"indrev:lead_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/netherite_scrap.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"ingredient\": {\n    \"tag\": \"c:netherite_scrap_dusts\"\n  },\n  \"result\": \"minecraft:netherite_scrap\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/silver_ingot_from_ore.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"silver_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:silver_ores\"\n  },\n  \"result\": \"indrev:silver_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/silver_ingot_from_raw_ores.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"silver_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:raw_silver_ores\"\n  },\n  \"result\": \"indrev:silver_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/silver_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"silver_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:silver_dusts\"\n  },\n  \"result\": \"indrev:silver_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/steel_ingot.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"ingredient\": {\n    \"tag\": \"c:steel_dusts\"\n  },\n  \"result\": \"indrev:steel_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/tin_ingot_from_ore.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"tin_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:tin_ores\"\n  },\n  \"result\": \"indrev:tin_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/tin_ingot_from_raw_ores.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"tin_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:raw_tin_ores\"\n  },\n  \"result\": \"indrev:tin_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/tin_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"tin_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:tin_dusts\"\n  },\n  \"result\": \"indrev:tin_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/tungsten_ingot_from_ore.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"tungsten_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:tungsten_ores\"\n  },\n  \"result\": \"indrev:tungsten_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/tungsten_ingot_from_raw_ores.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"tungsten_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:raw_tungsten_ores\"\n  },\n  \"result\": \"indrev:tungsten_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/blasting/tungsten_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:blasting\",\n  \"group\": \"tungsten_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:tungsten_dusts\"\n  },\n  \"result\": \"indrev:tungsten_ingot\",\n  \"cookingTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/bronze_plate_from_compressor.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"tag\": \"c:bronze_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:bronze_plate\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/carbon_fiber_plate.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"item\": \"indrev:soot\",\n    \"count\": 9\n  },\n  \"output\": {\n    \"item\": \"indrev:carbon_fiber_plate\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/copper_plate_from_compressor.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"tag\": \"c:copper_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:copper_plate\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/electrum_plate_from_compressor.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"tag\": \"c:electrum_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:electrum_plate\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/empty_upgrade.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"tag\": \"c:tin_plates\",\n    \"count\": 4\n  },\n  \"output\": {\n    \"item\": \"indrev:empty_enhancer\",\n    \"count\": 1\n  },\n  \"processTime\": 300\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/gold_plate_from_compressor.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"item\": \"minecraft:gold_ingot\"\n  },\n  \"output\": {\n    \"item\": \"indrev:gold_plate\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/iron_plate_from_compressor.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"item\": \"minecraft:iron_ingot\"\n  },\n  \"output\": {\n    \"item\": \"indrev:iron_plate\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/lead_plate_from_compressor.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"tag\": \"c:lead_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:lead_plate\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/plank_block.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"item\": \"indrev:planks\",\n    \"count\": 8\n  },\n  \"output\": {\n    \"item\": \"indrev:plank_block\",\n    \"count\": 1\n  },\n  \"processTime\": 300\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/planks.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"item\": \"indrev:sawdust\",\n    \"count\": 4\n  },\n  \"output\": {\n    \"item\": \"indrev:planks\",\n    \"count\": 1\n  },\n  \"processTime\": 300\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/silver_plate_from_compressor.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"tag\": \"c:silver_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:silver_plate\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/steel_plate.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"tag\": \"c:steel_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:steel_plate\",\n    \"count\": 1\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/tin_plate_from_compressor.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"tag\": \"c:tin_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:tin_plate\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/compressing/tungsten_plate_from_compressor.json",
    "content": "{\n  \"type\": \"indrev:compress\",\n  \"ingredients\": {\n    \"tag\": \"c:tungsten_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:tungsten_plate\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/condensing/copper_chunk_from_molten_copper.json",
    "content": "{\n  \"type\": \"indrev:condenser\",\n  \"output\": {\n    \"item\": \"indrev:copper_chunk\",\n    \"count\": 1\n  },\n  \"fluidInput\": {\n    \"fluid\": \"indrev:molten_copper_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/condensing/gold_chunk_from_molten_gold.json",
    "content": "{\n  \"type\": \"indrev:condenser\",\n  \"output\": {\n    \"item\": \"indrev:gold_chunk\",\n    \"count\": 1\n  },\n  \"fluidInput\": {\n    \"fluid\": \"indrev:molten_gold_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/condensing/iron_chunk_from_molten_iron.json",
    "content": "{\n  \"type\": \"indrev:condenser\",\n  \"output\": {\n    \"item\": \"indrev:iron_chunk\",\n    \"count\": 1\n  },\n  \"fluidInput\": {\n    \"fluid\": \"indrev:molten_iron_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/condensing/lead_chunk_from_molten_lead.json",
    "content": "{\n  \"type\": \"indrev:condenser\",\n  \"output\": {\n    \"item\": \"indrev:lead_chunk\",\n    \"count\": 1\n  },\n  \"fluidInput\": {\n    \"fluid\": \"indrev:molten_lead_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/condensing/netherite_chunk_from_molten_netherite.json",
    "content": "{\n  \"type\": \"indrev:condenser\",\n  \"output\": {\n    \"item\": \"indrev:netherite_scrap_chunk\",\n    \"count\": 1\n  },\n  \"fluidInput\": {\n    \"fluid\": \"indrev:molten_netherite_still\",\n    \"amount\": 250\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/condensing/silver_chunk_from_molten_silver.json",
    "content": "{\n  \"type\": \"indrev:condenser\",\n  \"output\": {\n    \"item\": \"indrev:silver_chunk\",\n    \"count\": 1\n  },\n  \"fluidInput\": {\n    \"fluid\": \"indrev:molten_silver_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/condensing/tin_chunk_from_molten_tin.json",
    "content": "{\n  \"type\": \"indrev:condenser\",\n  \"output\": {\n    \"item\": \"indrev:tin_chunk\",\n    \"count\": 1\n  },\n  \"fluidInput\": {\n    \"fluid\": \"indrev:molten_tin_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/distiller/salt_from_water.json",
    "content": "{\n  \"type\": \"indrev:distiller\",\n  \"output\": {\n    \"item\": \"indrev:salt\",\n    \"count\": 1\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 27000\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/electrolysis/electrolyze_water.json",
    "content": "{\n  \"type\": \"indrev:electrolysis\",\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 81000\n  },\n  \"fluidOutput\": [\n    {\n      \"fluid\": \"indrev:hydrogen_still\",\n      \"amount\": 40500\n    },\n    {\n      \"fluid\": \"indrev:oxygen_still\",\n      \"amount\": 40500\n    }\n  ],\n  \"processTime\": 300\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/clay.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:sand\",\n      \"count\": 1\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 81000\n  },\n  \"output\": {\n    \"item\": \"minecraft:clay\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/coolant_fluid.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:lapis_lazuli\"\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 81000\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:coolant_still\",\n    \"amount\": 81000\n  },\n  \"output\": {\n    \"item\": \"empty\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/copper_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"tag\": \"minecraft:copper_ores\"\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"indrev:sulfuric_acid_still\",\n    \"amount\": 81000\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:toxic_mud_still\",\n    \"amount\": 1000\n  },\n  \"output\": {\n    \"item\": \"indrev:copper_purified_ore\",\n    \"count\": 1\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/gold_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"tag\": \"minecraft:gold_ores\"\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"indrev:sulfuric_acid_still\",\n    \"amount\": 81000\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:toxic_mud_still\",\n    \"amount\": 1000\n  },\n  \"output\": {\n    \"item\": \"indrev:gold_purified_ore\",\n    \"count\": 1\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_black_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:black_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:black_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_blue_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:blue_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:blue_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_brown_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:brown_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:brown_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_cyan_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:cyan_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:cyan_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_gray_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:gray_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:gray_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_green_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:green_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:green_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_light_blue_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:light_blue_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:light_blue_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_light_gray_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:light_gray_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:light_gray_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_lime_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:lime_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:lime_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_magenta_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:magenta_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:magenta_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_orange_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:orange_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:orange_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_pink_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:pink_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:pink_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_purple_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:purple_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:purple_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_red_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:red_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:red_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_white_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:white_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:white_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/harden_yellow_concrete_powder.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": {\n    \"item\": \"minecraft:yellow_concrete_powder\"\n  },\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 8100\n  },\n  \"output\": {\n    \"item\": \"minecraft:yellow_concrete\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/iron_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"tag\": \"minecraft:iron_ores\"\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"indrev:sulfuric_acid_still\",\n    \"amount\": 81000\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:toxic_mud_still\",\n    \"amount\": 1000\n  },\n  \"output\": {\n    \"item\": \"indrev:iron_purified_ore\",\n    \"count\": 1\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/lead_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:lead_ores\"\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"indrev:sulfuric_acid_still\",\n    \"amount\": 81000\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:toxic_mud_still\",\n    \"amount\": 1000\n  },\n  \"output\": {\n    \"item\": \"indrev:lead_purified_ore\",\n    \"count\": 1\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/netherite_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:ancient_debris\"\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"indrev:sulfuric_acid_still\",\n    \"amount\": 81000\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:toxic_mud_still\",\n    \"amount\": 1000\n  },\n  \"output\": {\n    \"item\": \"indrev:netherite_scrap_purified_ore\",\n    \"count\": 1\n  },\n  \"processTime\": 700\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/paper.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:sawdust\",\n      \"count\": 3\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 27000\n  },\n  \"output\": {\n    \"item\": \"minecraft:paper\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/silver_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:silver_ores\"\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"indrev:sulfuric_acid_still\",\n    \"amount\": 81000\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:toxic_mud_still\",\n    \"amount\": 1000\n  },\n  \"output\": {\n    \"item\": \"indrev:silver_purified_ore\",\n    \"count\": 1\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/sulfuric_acid.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:sulfur_dusts\"\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"minecraft:water\",\n    \"amount\": 81000\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:sulfuric_acid_still\",\n    \"amount\": 81000\n  },\n  \"output\": {\n    \"item\": \"empty\"\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/tin_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:tin_ores\"\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"indrev:sulfuric_acid_still\",\n    \"amount\": 81000\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:toxic_mud_still\",\n    \"amount\": 1000\n  },\n  \"output\": {\n    \"item\": \"indrev:tin_purified_ore\",\n    \"count\": 1\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/tungsten_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:tungsten_ores\"\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"indrev:sulfuric_acid_still\",\n    \"amount\": 81000\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:toxic_mud_still\",\n    \"amount\": 1000\n  },\n  \"output\": {\n    \"item\": \"indrev:tungsten_purified_ore\",\n    \"count\": 1\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/fluid_infusing/wither_proof_obsidian.json",
    "content": "{\n  \"type\": \"indrev:fluid_infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:obsidian\"\n    }\n  ],\n  \"fluidInput\": {\n    \"fluid\": \"indrev:molten_iron_still\",\n    \"amount\": 81000\n  },\n  \"output\": {\n    \"item\": \"indrev:wither_proof_obsidian\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/allium.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:allium\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:allium\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/azure_bluet.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:azure_bluet\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:azure_bluet\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/big_dripleaf.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:big_dripleaf\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:big_dripleaf\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}\n"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/blue_orchid.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:blue_orchid\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:blue_orchid\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/bronze_dust.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:tin_dusts\"\n    },\n    {\n      \"tag\": \"c:copper_dusts\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:bronze_dust\",\n    \"count\": 2\n  },\n  \"processTime\": 300\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/brown_mushroom.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:brown_mushroom\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:brown_mushroom\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/cornflower.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:cornflower\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:cornflower\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/crimson_fungus.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:crimson_fungus\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:crimson_fungus\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/crimson_roots.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:crimson_roots\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:crimson_roots\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/dandelion.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:dandelion\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:dandelion\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/dead_bush.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:dead_bush\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:dead_bush\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/electrum_dust.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:gold_dusts\"\n    },\n    {\n      \"tag\": \"c:silver_dusts\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:electrum_dust\",\n    \"count\": 2\n  },\n  \"processTime\": 300\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/enriched_nikolite.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    {\n      \"tag\": \"c:diamond_dusts\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:enriched_nikolite_dust\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/enriched_nikolite_ingot.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    },\n    {\n      \"item\": \"indrev:nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:enriched_nikolite_ingot\",\n    \"count\": 1\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/fern.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:fern\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:fern\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/glow_lichen.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:glow_lichen\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:glow_lichen\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}\n"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/grass.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:grass\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:grass\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/kelp.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:kelp\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:kelp\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/large_fern.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:large_fern\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:large_fern\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/lilac.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:lilac\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:lilac\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/lily_of_the_valley.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:lily_of_the_valley\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:lily_of_the_valley\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/lily_pad.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:lily_pad\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:lily_pad\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/nether_sprouts.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:nether_sprouts\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:nether_sprouts\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/nikolite_ingot.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    {\n      \"item\": \"minecraft:iron_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:nikolite_ingot\",\n    \"count\": 1\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/orange_tulip.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:orange_tulip\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:orange_tulip\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/oxeye_daisy.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:oxeye_daisy\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:oxeye_daisy\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/pink_tulip.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:pink_tulip\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:pink_tulip\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/poppy.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:poppy\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:poppy\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/red_mushroom.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:red_mushroom\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:red_mushroom\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/red_tulip.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:red_tulip\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:red_tulip\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/sea_pickle.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:sea_pickle\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:sea_pickle\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/seagrass.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:seagrass\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:seagrass\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/small_dripleaf.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:small_dripleaf\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:small_dripleaf\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}\n"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/spore_blossom.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:spore_blossom\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:spore_blossom\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}\n"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/steel_dust.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:coal_dusts\"\n    },\n    {\n      \"tag\": \"c:iron_dusts\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:steel_dust\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/tall_grass.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:tall_grass\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:tall_grass\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/twisting_vines.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:twisting_vines\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:twisting_vines\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/untanned_leather.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:rotten_flesh\"\n    },\n    {\n      \"item\": \"minecraft:rotten_flesh\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:untanned_leather\",\n    \"count\": 1\n  },\n  \"processTime\": 350\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/vine.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:vine\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:vine\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/warped_fungus.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:warped_fungus\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:warped_fungus\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/warped_roots.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:warped_roots\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:warped_roots\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/weeping_vines.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:weeping_vines\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:weeping_vines\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/infusing/white_tulip.json",
    "content": "{\n  \"type\": \"indrev:infuse\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:white_tulip\"\n    },\n    {\n      \"item\": \"minecraft:bone_meal\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"minecraft:white_tulip\",\n    \"count\": 2\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/laser.json",
    "content": "{\n  \"type\": \"indrev:laser\",\n  \"ingredients\": {\n    \"item\": \"indrev:modular_core\"\n  },\n  \"output\": [\n    {\n      \"item\": \"indrev:modular_core_activated\"\n    }\n  ],\n  \"processTime\": 100000000\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_auto_feeder.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_auto_feeder\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_breathing.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_breathing\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_charger.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"item\": \"indrev:portable_charger\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_charger\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_efficiency.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_efficiency\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_elytra.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"item\": \"indrev:reinforced_elytra\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_elytra\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_feather_falling.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"item\": \"minecraft:slime_ball\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_feather_falling\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_fire_aspect.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"item\": \"minecraft:blaze_rod\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_fire_aspect\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_fire_resistance.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    {\n      \"tag\": \"c:tungsten_plates\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_fire_resistance\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_fortune.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"item\": \"minecraft:emerald\"\n    },\n    {\n      \"item\": \"minecraft:diamond\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_fortune\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_jump_boost.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_jump_boost\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_looting.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_looting\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_night_vision.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_night_vision\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_piglin_tricker.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"item\": \"minecraft:gold_block\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_piglin_tricker\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_protection.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_protection\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_range.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"tag\": \"c:steel_plates\"\n    },\n    {\n      \"tag\": \"c:electrum_plates\"\n    },\n    {\n      \"tag\": \"c:electrum_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_range\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_sharpness.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"item\": \"minecraft:quartz_block\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_sharpness\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_silk_touch.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n       \"tag\": \"c:silver_plates\"\n    },\n    {\n       \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_silk_touch\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_solar_panel.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"item\": \"indrev:solar_generator_mk3\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_solar_panel\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/modules/module_speed.json",
    "content": "{\n  \"type\": \"indrev:modules\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"tag\": \"c:silver_plates\"\n    },\n    {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:module_speed\"\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/blaze_power.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"minecraft:blaze_rod\"\n  },\n  \"output\":\n    {\n      \"item\": \"minecraft:blaze_powder\",\n      \"count\": 4\n    },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/bone_meal.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"minecraft:bone\"\n  },\n  \"output\":\n    {\n      \"item\": \"minecraft:bone_meal\",\n      \"count\": 5\n    },\n  \"processTime\": 200,\n  \"extra\": {\n\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/bronze_dust_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:bronze_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:bronze_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/coal_dust.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"minecraft:coal\"\n  },\n  \"output\": {\n    \"item\": \"indrev:coal_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/coal_dust_from_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:coal_ores\"\n  },\n  \"output\": {\n    \"item\": \"minecraft:coal\",\n    \"count\": 2\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/cobblestone_pulverizer.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"minecraft:stone\"\n  },\n  \"output\": {\n    \"item\": \"minecraft:cobblestone\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/copper_dust_from_chunk.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:copper_chunk\"\n  },\n  \"output\": {\n    \"item\": \"indrev:copper_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/copper_dust_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:copper_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:copper_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/copper_dust_from_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:copper_ores\"\n  },\n  \"output\": {\n    \"item\": \"indrev:copper_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/copper_dust_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:copper_purified_ore\"\n  },\n  \"output\": {\n    \"item\": \"indrev:copper_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/copper_dust_from_raw.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:raw_copper_ores\",\n    \"count\": 2\n  },\n  \"output\": {\n    \"item\": \"indrev:copper_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/diamond_dust.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"minecraft:diamond\"\n  },\n  \"output\": {\n    \"item\": \"indrev:diamond_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/diamond_dust_from_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:diamond_ores\"\n  },\n  \"output\": {\n    \"item\": \"minecraft:diamond\",\n    \"count\": 2\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/electrum_dust_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:electrum_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:electrum_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/gold_dust_from_chunk.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:gold_chunk\"\n  },\n  \"output\": {\n    \"item\": \"indrev:gold_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/gold_dust_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:gold_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:gold_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/gold_dust_from_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:gold_ores\"\n  },\n  \"output\": {\n    \"item\": \"indrev:gold_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/gold_dust_from_raw.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:raw_gold_ores\",\n    \"count\": 2\n  },\n  \"output\": {\n    \"item\": \"indrev:gold_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/gravel_pulverizer.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"minecraft:cobblestone\"\n  },\n  \"output\": {\n    \"item\": \"minecraft:gravel\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/iron_dust_from_chunk.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:iron_chunk\"\n  },\n  \"output\": {\n    \"item\": \"indrev:iron_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/iron_dust_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:iron_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:iron_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/iron_dust_from_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:iron_ores\"\n  },\n  \"output\": {\n    \"item\": \"indrev:iron_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/iron_dust_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:iron_purified_ore\"\n  },\n  \"output\": {\n    \"item\": \"indrev:iron_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/iron_dust_from_raw.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:raw_iron_ores\",\n    \"count\": 2\n  },\n  \"output\": {\n    \"item\": \"indrev:iron_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/lead_dust_from_chunk.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:lead_chunk\"\n  },\n  \"output\": {\n    \"item\": \"indrev:lead_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/lead_dust_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:lead_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:lead_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/lead_dust_from_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:lead_ores\"\n  },\n  \"output\": {\n    \"item\": \"indrev:lead_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/lead_dust_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:lead_purified_ore\"\n  },\n  \"output\": {\n    \"item\": \"indrev:lead_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/lead_dust_from_raw.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:raw_lead_ores\",\n    \"count\": 2\n  },\n  \"output\": {\n    \"item\": \"indrev:lead_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/netherite_dust.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"minecraft:ancient_debris\"\n  },\n  \"output\":\n  {\n    \"item\": \"indrev:netherite_scrap_dust\",\n    \"count\": 2\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/netherite_dust_from_chunk.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:netherite_scrap_chunk\"\n  },\n  \"output\":\n  {\n    \"item\": \"indrev:netherite_scrap_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/nikolite.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:nikolite_ores\"\n  },\n  \"output\": {\n    \"item\": \"indrev:nikolite_dust\",\n    \"count\": 7\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/sand_pulverizer.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"minecraft:gravel\"\n  },\n  \"output\": {\n    \"item\": \"minecraft:sand\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/silver_dust_from_chunk.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:silver_chunk\"\n  },\n  \"output\": {\n    \"item\": \"indrev:silver_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/silver_dust_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:silver_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:silver_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/silver_dust_from_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:silver_ores\"\n  },\n  \"output\": {\n    \"item\": \"indrev:silver_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/silver_dust_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:silver_purified_ore\"\n  },\n  \"output\": {\n    \"item\": \"indrev:silver_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/silver_dust_from_raw.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:raw_silver_ores\",\n    \"count\": 2\n  },\n  \"output\": {\n    \"item\": \"indrev:silver_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/sulfur_dust.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:sulfurs\"\n  },\n  \"output\": [\n    {\n      \"item\": \"indrev:sulfur_dust\",\n      \"count\": 2\n    },\n    {\n      \"item\": \"indrev:sulfur_dust\",\n      \"count\": 1,\n      \"chance\": 0.8\n    }\n  ],\n  \"processTime\": 200,\n  \"extra\": {\n\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/sulfur_dust_from_gunpowder.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"minecraft:gunpowder\"\n  },\n  \"output\": {\n    \"item\": \"indrev:sulfur_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/tin_dust_from_chunk.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:tin_chunk\"\n  },\n  \"output\": {\n    \"item\": \"indrev:tin_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/tin_dust_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:tin_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:tin_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/tin_dust_from_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:tin_ores\"\n  },\n  \"output\": {\n    \"item\": \"indrev:tin_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/tin_dust_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:tin_purified_ore\"\n  },\n  \"output\": {\n    \"item\": \"indrev:tin_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/tin_dust_from_raw.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:raw_tin_ores\",\n    \"count\": 2\n  },\n  \"output\": {\n    \"item\": \"indrev:tin_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/tungsten_dust_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:tungsten_ingots\"\n  },\n  \"output\": {\n    \"item\": \"indrev:tungsten_dust\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/tungsten_dust_from_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"tag\": \"c:tungsten_ores\"\n  },\n  \"output\": {\n    \"item\": \"indrev:tungsten_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/pulverizer/tungsten_dust_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:pulverize\",\n  \"ingredients\": {\n    \"item\": \"indrev:tungsten_purified_ore\"\n  },\n  \"output\": {\n    \"item\": \"indrev:tungsten_dust\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_baked_potato.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:baked_potato\",\n      \"count\": 3\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 6\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_bamboo.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:bamboo\",\n      \"count\": 4\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 400\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_beetroot.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:beetroot\",\n      \"count\": 2\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_beetrot_seeds.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:beetroot_seeds\",\n      \"count\": 2\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_bread.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:bread\",\n      \"count\": 1\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 3\n  },\n  \"processTime\": 350\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_brown_mushroom.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:brown_mushroom\",\n      \"count\": 2\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 50\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_cactus.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:cactus\",\n      \"count\": 3\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 2\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_carrot.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:carrot\",\n      \"count\": 2\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_cookie.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:cookie\",\n      \"count\": 1\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 6\n  },\n  \"processTime\": 450\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_dirt.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:dirt\",\n      \"count\": 8\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 100\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_flowers.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"tag\": \"minecraft:flowers\",\n      \"count\": 3\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 50\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_grass.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:grass\",\n      \"count\": 4\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 100\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_grass_block.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:grass_block\",\n      \"count\": 6\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 100\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_hay_block.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:hay_block\",\n      \"count\": 1\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 18\n  },\n  \"processTime\": 1000\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_logs.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:logs\"\n  },\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 4\n  },\n  \"processTime\": 500\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_melon.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:melon\",\n      \"count\": 1\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 18\n  },\n  \"processTime\": 1000\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_melon_seeds.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:melon_seeds\",\n      \"count\": 2\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_melon_slice.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:melon_slice\",\n      \"count\": 1\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 2\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_nether_wart.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:nether_wart\",\n      \"count\": 1\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 3\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_planks.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:planks\"\n  },\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 100\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_potato.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:potato\",\n      \"count\": 2\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 3\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_red_mushroom.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:red_mushroom\",\n      \"count\": 2\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 50\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_saplings.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:saplings\"\n  },\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_wheat.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:wheat\",\n      \"count\": 1\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 2\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/recycling/recycle_wheat_seeds.json",
    "content": "{\n  \"type\": \"indrev:recycle\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:wheat_seeds\",\n      \"count\": 2\n    }\n  ],\n  \"output\": {\n    \"item\": \"indrev:biomass\",\n    \"count\": 1\n  },\n  \"processTime\": 150\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/sawmill/acacia_planks.json",
    "content": "{\n  \"type\": \"indrev:sawmill\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:acacia_logs\"\n  },\n  \"output\": [\n    {\n      \"item\": \"minecraft:acacia_planks\",\n      \"count\": 6\n    },\n    {\n      \"item\": \"minecraft:stick\",\n      \"count\": 2,\n      \"chance\": 0.5\n    },\n    {\n      \"item\": \"indrev:sawdust\",\n      \"count\": 3\n    }\n  ],\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/sawmill/birch_planks.json",
    "content": "{\n  \"type\": \"indrev:sawmill\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:birch_logs\"\n  },\n  \"output\": [\n    {\n      \"item\": \"minecraft:birch_planks\",\n      \"count\": 6\n    },\n    {\n      \"item\": \"minecraft:stick\",\n      \"count\": 2,\n      \"chance\": 0.5\n    },\n    {\n      \"item\": \"indrev:sawdust\",\n      \"count\": 3\n    }\n  ],\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/sawmill/crimson_planks.json",
    "content": "{\n  \"type\": \"indrev:sawmill\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:crimson_stems\"\n  },\n  \"output\": [\n    {\n      \"item\": \"minecraft:crimson_planks\",\n      \"count\": 6\n    },\n    {\n      \"item\": \"minecraft:stick\",\n      \"count\": 2,\n      \"chance\": 0.5\n    },\n    {\n      \"item\": \"indrev:sawdust\",\n      \"count\": 3\n    }\n  ],\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/sawmill/dark_oak_planks.json",
    "content": "{\n  \"type\": \"indrev:sawmill\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:dark_oak_logs\"\n  },\n  \"output\": [\n    {\n      \"item\": \"minecraft:dark_oak_planks\",\n      \"count\": 6\n    },\n    {\n      \"item\": \"minecraft:stick\",\n      \"count\": 2,\n      \"chance\": 0.5\n    },\n    {\n      \"item\": \"indrev:sawdust\",\n      \"count\": 3\n    }\n  ],\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/sawmill/jungle_planks.json",
    "content": "{\n  \"type\": \"indrev:sawmill\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:jungle_logs\"\n  },\n  \"output\": [\n    {\n      \"item\": \"minecraft:jungle_planks\",\n      \"count\": 6\n    },\n    {\n      \"item\": \"minecraft:stick\",\n      \"count\": 2,\n      \"chance\": 0.5\n    },\n    {\n      \"item\": \"indrev:sawdust\",\n      \"count\": 3\n    }\n  ],\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/sawmill/oak_planks.json",
    "content": "{\n  \"type\": \"indrev:sawmill\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:oak_logs\"\n  },\n  \"output\": [\n    {\n      \"item\": \"minecraft:oak_planks\",\n      \"count\": 6\n    },\n    {\n      \"item\": \"minecraft:stick\",\n      \"count\": 2,\n      \"chance\": 0.5\n    },\n    {\n      \"item\": \"indrev:sawdust\",\n      \"count\": 3\n    }\n  ],\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/sawmill/spruce_planks.json",
    "content": "{\n  \"type\": \"indrev:sawmill\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:spruce_logs\"\n  },\n  \"output\": [\n    {\n      \"item\": \"minecraft:spruce_planks\",\n      \"count\": 6\n    },\n    {\n      \"item\": \"minecraft:stick\",\n      \"count\": 2,\n      \"chance\": 0.5\n    },\n    {\n      \"item\": \"indrev:sawdust\",\n      \"count\": 3\n    }\n  ],\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/sawmill/warped_planks.json",
    "content": "{\n  \"type\": \"indrev:sawmill\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:warped_stems\"\n  },\n  \"output\": [\n    {\n      \"item\": \"minecraft:warped_planks\",\n      \"count\": 6\n    },\n    {\n      \"item\": \"minecraft:stick\",\n      \"count\": 2,\n      \"chance\": 0.5\n    },\n    {\n      \"item\": \"indrev:sawdust\",\n      \"count\": 3\n    }\n  ],\n  \"processTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/battery.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" I \",\n    \"INI\",\n    \"INI\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    \"I\": {\n      \"tag\": \"c:tin_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:battery\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/biomass_generator_mk3.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"ICI\",\n    \"NMN\",\n    \"EBE\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk3\"\n    },\n    \"I\": {\n      \"item\": \"indrev:biomass\"\n    },\n    \"E\": {\n      \"tag\": \"c:electrum_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:biomass_generator_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/blast_furnace_enhancer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" E \",\n    \"NCN\",\n    \" B \"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"B\": {\n      \"item\": \"minecraft:blast_furnace\"\n    },\n    \"E\": {\n      \"item\": \"indrev:empty_enhancer\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:blast_furnace_enhancer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/bronze_axe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \"SI\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:bronze_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:bronze_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/bronze_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:bronze_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:bronze_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/bronze_boots.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:bronze_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:bronze_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/bronze_chestplate.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"SSS\",\n    \"SSS\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:bronze_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:bronze_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/bronze_helmet.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:bronze_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:bronze_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/bronze_hoe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \" I\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:bronze_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:bronze_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/bronze_ingot_from_nugget.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:bronze_nuggets\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:bronze_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/bronze_leggings.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:bronze_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:bronze_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/bronze_pickaxe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \" I \",\n    \" I \"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:bronze_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:bronze_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/bronze_shovel.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"I\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:bronze_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:bronze_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/bronze_sword.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"S\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:bronze_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:bronze_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/buffer_enhancer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NEN\",\n    \" C \",\n    \" B \"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"E\": {\n      \"item\": \"indrev:empty_enhancer\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:buffer_enhancer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/cabinet.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BBB\",\n    \"BDB\",\n    \"BBB\"\n  ],\n  \"key\": {\n    \"B\": {\n      \"tag\": \"c:iron_plates\"\n    },\n    \"D\": {\n      \"item\": \"minecraft:barrel\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:cabinet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/cable_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"INI\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    \"I\": {\n      \"tag\": \"c:gold_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:cable_mk1\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/cable_mk2.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BBB\",\n    \"BNB\",\n    \"BBB\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"indrev:nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:cable_mk1\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:cable_mk2\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/cable_mk3.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"MMM\",\n    \"MEM\",\n    \"MMM\"\n  ],\n  \"key\": {\n    \"E\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    },\n    \"M\": {\n      \"item\": \"indrev:cable_mk2\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:cable_mk3\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/cable_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"HHH\",\n    \"HEH\",\n    \"HHH\"\n  ],\n  \"key\": {\n    \"E\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"H\": {\n      \"item\": \"indrev:cable_mk3\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:cable_mk4\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/capsule.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"PMP\",\n    \"M M\",\n    \"LML\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"minecraft:glass\"\n    },\n    \"P\": {\n      \"tag\": \"c:tungsten_plates\"\n    },\n    \"L\": {\n      \"tag\": \"c:lead_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:capsule\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/carbon_fiber_boots_frame.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"item\": \"indrev:carbon_fiber_rod\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:carbon_fiber_boots_frame\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/carbon_fiber_chest_frame.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"SSS\",\n    \"SSS\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"item\": \"indrev:carbon_fiber_rod\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:carbon_fiber_chest_frame\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/carbon_fiber_helmet_frame.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"item\": \"indrev:carbon_fiber_rod\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:carbon_fiber_helmet_frame\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/carbon_fiber_legs_frame.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"item\": \"indrev:carbon_fiber_rod\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:carbon_fiber_legs_frame\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/carbon_fiber_rod.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"H\",\n    \"H\",\n    \"H\"\n  ],\n  \"key\": {\n    \"H\": {\n      \"item\": \"indrev:carbon_fiber_plate\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:carbon_fiber_rod\",\n    \"count\": 1\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/charge_pad_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"ECE\",\n    \"SLS\",\n    \"SES\"\n  ],\n  \"key\": {\n    \"L\": {\n      \"item\": \"indrev:lazuli_flux_container_mk4\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"S\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"E\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:charge_pad_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/chopper_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"GCG\",\n    \"NMN\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"N\": {\n      \"tag\": \"c:bronze_plates\"\n    },\n    \"G\": {\n      \"item\": \"minecraft:golden_axe\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:chopper_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/circuit_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BNB\",\n    \"NIN\",\n    \"BNB\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    \"I\": {\n      \"tag\": \"c:gold_plates\"\n    },\n    \"B\": {\n      \"tag\": \"c:copper_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:circuit_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/circuit_mk2.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SES\",\n    \"ECE\",\n    \"SES\"\n  ],\n  \"key\": {\n    \"E\": {\n      \"item\": \"indrev:nikolite_ingot\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"S\": {\n      \"tag\": \"c:silver_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:circuit_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/circuit_mk3.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"MEM\",\n    \"ECE\",\n    \"MEM\"\n  ],\n  \"key\": {\n    \"E\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk2\"\n    },\n    \"M\": {\n      \"tag\": \"c:electrum_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:circuit_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/circuit_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"RER\",\n    \"ECE\",\n    \"RER\"\n  ],\n  \"key\": {\n    \"E\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk3\"\n    },\n    \"R\": {\n      \"tag\": \"c:lead_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:circuit_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/coal_generator_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NCN\",\n    \"IMI\",\n    \"IBI\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"I\": {\n      \"tag\": \"c:copper_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:heat_coil\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:coal_generator_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/compressor_factory_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NCN\",\n    \"IFI\",\n    \"IBI\"\n  ],\n  \"key\": {\n    \"F\": {\n      \"item\": \"indrev:compressor_mk4\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"I\": {\n      \"tag\": \"c:lead_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:compressor_factory_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/compressor_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SCS\",\n    \"NMN\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"N\": {\n      \"tag\": \"c:copper_plates\"\n    },\n    \"S\": {\n      \"item\": \"minecraft:stone\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:compressor_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/condenser_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" C \",\n    \"SMS\",\n    \"SBS\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"S\": {\n      \"tag\": \"c:lead_plates\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:condenser_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/controller.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"IBI\",\n    \"BNB\",\n    \"IBI\"\n  ],\n  \"key\": {\n    \"I\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"B\": {\n      \"tag\": \"c:silver_plates\"\n    },\n    \"N\": {\n      \"item\": \"minecraft:redstone\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:controller\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/cooler_cell.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" T \",\n    \"TCT\",\n    \" T \"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:coolant_bucket\"\n    },\n    \"T\": {\n      \"tag\": \"c:tin_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:cooler_cell\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/copper_axe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \"SI\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:copper_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:copper_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/copper_boots.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:copper_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:copper_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/copper_chestplate.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"SSS\",\n    \"SSS\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:copper_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:copper_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/copper_helmet.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:copper_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:copper_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/copper_hoe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \" I\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:copper_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:copper_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/copper_ingot_from_nuggets.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:copper_nuggets\"\n    }\n  },\n  \"result\": {\n    \"item\": \"minecraft:copper_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/copper_leggings.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:copper_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:copper_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/copper_pickaxe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \" I \",\n    \" I \"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:copper_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:copper_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/copper_shovel.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"I\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:copper_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:copper_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/copper_sword.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"S\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:copper_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:copper_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/damage_upgrade.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" C \",\n    \"SES\",\n    \"N N\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"S\": {\n      \"item\": \"minecraft:diamond_sword\"\n    },\n    \"E\": {\n      \"item\": \"indrev:empty_enhancer\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:damage_enhancer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/data_card_writer_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" C \",\n    \"IFI\",\n    \"IBI\"\n  ],\n  \"key\": {\n    \"F\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"I\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:data_card_writer_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/diamond_drill_head.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"ISI\",\n    \"III\",\n    \" I \"\n  ],\n  \"key\": {\n    \"I\": {\n      \"item\": \"minecraft:diamond\"\n    },\n    \"S\": {\n      \"item\": \"indrev:iron_drill_head\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:diamond_drill_head\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/drain_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"GBG\",\n    \"IMI\"\n  ],\n  \"key\": {\n    \"G\": {\n      \"item\": \"minecraft:glass\"\n    },\n    \"I\": {\n      \"tag\": \"c:iron_plates\"\n    },\n    \"B\": {\n      \"item\": \"minecraft:bucket\"\n    },\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:drain_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/drill_bottom.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SLS\",\n    \"SLS\",\n    \" S \"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"L\": {\n      \"tag\": \"c:lead_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:drill_bottom\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/duct.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BB \",\n    \" B \",\n    \" B \"\n  ],\n  \"key\": {\n    \"B\": {\n      \"tag\": \"c:lead_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:duct\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/electric_furnace_factory_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NCN\",\n    \"IFI\",\n    \"IBI\"\n  ],\n  \"key\": {\n    \"F\": {\n      \"item\": \"indrev:electric_furnace_mk4\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"I\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:electric_furnace_factory_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/electric_furnace_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" C \",\n    \"IFI\",\n    \"IBI\"\n  ],\n  \"key\": {\n    \"F\": {\n      \"item\": \"minecraft:furnace\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"I\": {\n      \"tag\": \"c:bronze_plates\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:electric_furnace_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/electrum_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:electrum_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:electrum_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/electrum_block_from_nugget.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:electrum_nuggets\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:electrum_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/energy_reader.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" C \",\n    \"TPT\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"T\": {\n      \"tag\": \"c:tin_plates\"\n    },\n    \"P\": {\n      \"tag\": \"c:copper_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:energy_reader\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/fan.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" T \",\n    \"T T\",\n    \" T \"\n  ],\n  \"key\": {\n    \"T\": {\n      \"tag\": \"c:tin_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:fan\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/farmer_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NCN\",\n    \"IFI\",\n    \"IBI\"\n  ],\n  \"key\": {\n    \"F\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"I\": {\n      \"tag\": \"c:bronze_plates\"\n    },\n    \"N\": {\n      \"item\": \"minecraft:golden_hoe\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:farmer_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/fisher_mk2.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"PCP\",\n    \"NMN\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk2\"\n    },\n    \"N\": {\n      \"tag\": \"c:silver_plates\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"P\": {\n      \"item\": \"indrev:planks\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:fisher_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/fisher_mk3.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NCN\",\n    \"SMS\",\n    \"SBS\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:fisher_mk2\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk3\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    },\n    \"S\": {\n      \"tag\": \"c:electrum_plates\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:fisher_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/fisher_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NCN\",\n    \"LML\",\n    \"LBL\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:fisher_mk3\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"L\": {\n      \"tag\": \"c:lead_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:fisher_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/fluid_infuser_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"GCG\",\n    \"NMN\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"N\": {\n      \"tag\": \"c:bronze_plates\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"G\": {\n      \"item\": \"indrev:tank\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:fluid_infuser_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/fluid_pipe_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"III\"\n  ],\n  \"key\": {\n    \"I\": {\n      \"item\": \"minecraft:iron_ingot\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:fluid_pipe_mk1\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/fluid_pipe_mk2.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BBB\",\n    \"BNB\",\n    \"BBB\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"indrev:nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:fluid_pipe_mk1\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:fluid_pipe_mk2\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/fluid_pipe_mk3.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BBB\",\n    \"BNB\",\n    \"BBB\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    },\n    \"B\": {\n      \"item\": \"indrev:fluid_pipe_mk2\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:fluid_pipe_mk3\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/fluid_pipe_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BBB\",\n    \"BNB\",\n    \"BBB\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:fluid_pipe_mk3\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:fluid_pipe_mk4\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/frame.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"III\",\n    \"III\",\n    \"III\"\n  ],\n  \"key\": {\n    \"I\": {\n      \"tag\": \"c:steel_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:frame\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/gamer_axe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"CSC\",\n    \"ENE\",\n    \"SBS\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"E\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"N\": {\n      \"item\": \"minecraft:netherite_axe\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:gamer_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/hammer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"III\",\n    \"ISI\",\n    \" S \"\n  ],\n  \"key\": {\n    \"I\": {\n      \"item\": \"minecraft:iron_ingot\"\n    },\n    \"S\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:hammer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/heat_coil.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" I \",\n    \" I \",\n    \" I \"\n  ],\n  \"key\": {\n    \"I\": {\n      \"tag\": \"c:copper_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:heat_coil\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/heat_generator.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"UCU\",\n    \"NMN\",\n    \"LBL\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"U\": {\n      \"item\": \"indrev:heat_coil\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"L\": {\n      \"tag\": \"c:lead_plates\"\n    },\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:heat_generator_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/heatsink.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"TCT\",\n    \"FCF\",\n    \"TCT\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:coolant_bucket\"\n    },\n    \"T\": {\n      \"tag\": \"c:tin_plates\"\n    },\n    \"F\": {\n      \"item\": \"indrev:fan\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:heatsink\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/intake.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"IVI\",\n    \"IBI\",\n    \"III\"\n  ],\n  \"key\": {\n    \"I\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"B\": {\n      \"tag\": \"c:lead_plates\"\n    },\n    \"V\": {\n      \"item\": \"indrev:fan\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:intake\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/iron_drill_head.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"ISI\",\n    \"III\",\n    \" I \"\n  ],\n  \"key\": {\n    \"I\": {\n      \"item\": \"minecraft:iron_ingot\"\n    },\n    \"S\": {\n      \"item\": \"indrev:stone_drill_head\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:iron_drill_head\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/item_pipe_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NIN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"minecraft:iron_ingot\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:gold_nugget\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:item_pipe_mk1\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/item_pipe_mk2.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BBB\",\n    \"BNB\",\n    \"BBB\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"indrev:nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:item_pipe_mk1\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:item_pipe_mk2\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/item_pipe_mk3.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BBB\",\n    \"BNB\",\n    \"BBB\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    },\n    \"B\": {\n      \"item\": \"indrev:item_pipe_mk2\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:item_pipe_mk3\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/item_pipe_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BBB\",\n    \"BNB\",\n    \"BBB\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:item_pipe_mk3\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:item_pipe_mk4\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/laser_emitter_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"PCP\",\n    \"BMB\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"P\": {\n      \"tag\": \"c:tungsten_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:laser_emitter_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lazuli_flux_container_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"PCP\",\n    \"BMB\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"N\": {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"P\": {\n      \"tag\": \"c:bronze_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lazuli_flux_container_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lazuli_flux_container_mk2.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"PCP\",\n    \"BLB\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk2\"\n    },\n    \"N\": {\n      \"item\": \"indrev:nikolite_ingot\"\n    },\n    \"L\": {\n      \"item\": \"indrev:lazuli_flux_container_mk1\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"P\": {\n      \"tag\": \"c:silver_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lazuli_flux_container_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lazuli_flux_container_mk3.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"PCP\",\n    \"BLB\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk3\"\n    },\n    \"L\": {\n      \"item\": \"indrev:lazuli_flux_container_mk2\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"P\": {\n      \"tag\": \"c:electrum_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lazuli_flux_container_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lazuli_flux_container_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"PCP\",\n    \"BLB\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"P\": {\n      \"tag\": \"c:lead_plates\"\n    },\n    \"L\": {\n      \"item\": \"indrev:lazuli_flux_container_mk3\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lazuli_flux_container_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lead_axe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \"SI\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:lead_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lead_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lead_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:lead_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lead_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lead_boots.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:lead_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lead_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lead_chestplate.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"SSS\",\n    \"SSS\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:lead_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lead_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lead_helmet.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:lead_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lead_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lead_hoe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \" I\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:lead_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lead_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lead_ingot_from_nuggets.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:lead_nuggets\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lead_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lead_leggings.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:lead_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lead_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lead_pickaxe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \" I \",\n    \" I \"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:lead_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lead_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lead_shovel.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"I\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:lead_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lead_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/lead_sword.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"S\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:lead_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:lead_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/machine_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"INI\",\n    \"I I\",\n    \"INI\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    \"I\": {\n      \"tag\": \"c:iron_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:machine_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/mining_drill_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NPN\",\n    \"OCO\",\n    \"OBO\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"N\": {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    \"P\": {\n      \"item\": \"indrev:stone_drill_head\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"O\": {\n      \"tag\": \"c:bronze_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:mining_drill_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/mining_drill_mk2.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NPN\",\n    \"OCO\",\n    \"OBO\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk2\"\n    },\n    \"N\": {\n      \"item\": \"indrev:nikolite_ingot\"\n    },\n    \"P\": {\n      \"item\": \"indrev:iron_drill_head\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"O\": {\n      \"tag\": \"c:silver_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:mining_drill_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/mining_drill_mk3.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NPN\",\n    \"OCO\",\n    \"OBO\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk3\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    },\n    \"P\": {\n      \"item\": \"indrev:diamond_drill_head\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"O\": {\n      \"tag\": \"c:electrum_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:mining_drill_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/mining_drill_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NPN\",\n    \"OCO\",\n    \"OBO\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"P\": {\n      \"item\": \"indrev:netherite_drill_head\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"O\": {\n      \"tag\": \"c:lead_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:mining_drill_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/mining_rig_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"GCG\",\n    \"NMN\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"N\": {\n      \"tag\": \"c:lead_plates\"\n    },\n    \"G\": {\n      \"item\": \"indrev:netherite_drill_head\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:mining_rig_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/modular_armor_boots.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"ICI\",\n    \"NSN\",\n    \"BMB\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"S\": {\n      \"item\": \"indrev:steel_boots\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:netherite_ingot\"\n    },\n    \"M\": {\n      \"item\": \"indrev:modular_core_activated\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:modular_armor_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/modular_armor_chest.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"ICI\",\n    \"NSN\",\n    \"BMB\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"S\": {\n      \"item\": \"indrev:steel_chestplate\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:netherite_ingot\"\n    },\n    \"M\": {\n      \"item\": \"indrev:modular_core_activated\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:modular_armor_chest\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/modular_armor_helmet.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"ICI\",\n    \"NSN\",\n    \"BMB\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"S\": {\n      \"item\": \"indrev:steel_helmet\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:netherite_ingot\"\n    },\n    \"M\": {\n      \"item\": \"indrev:modular_core_activated\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:modular_armor_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/modular_armor_legs.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"ICI\",\n    \"NSN\",\n    \"BMB\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"S\": {\n      \"item\": \"indrev:steel_leggings\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:netherite_ingot\"\n    },\n    \"M\": {\n      \"item\": \"indrev:modular_core_activated\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:modular_armor_legs\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/modular_core.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NCN\",\n    \"PNP\",\n    \"PCP\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"P\": {\n      \"tag\": \"c:tungsten_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:modular_core\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/modular_workbench.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"ICI\",\n    \"GMG\",\n    \"BNB\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"I\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"G\": {\n      \"item\": \"minecraft:glass\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:modular_workbench_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/netherite_drill_head.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"ISI\",\n    \"III\",\n    \" I \"\n  ],\n  \"key\": {\n    \"I\": {\n      \"item\": \"minecraft:netherite_ingot\"\n    },\n    \"S\": {\n      \"item\": \"indrev:diamond_drill_head\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:netherite_drill_head\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/ore_data_card.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" S \",\n    \"IRI\",\n    \" I \"\n  ],\n  \"key\": {\n    \"I\": {\n      \"tag\": \"c:iron_plates\"\n    },\n    \"S\": {\n      \"item\": \"minecraft:glass\"\n    },\n    \"R\": {\n      \"item\": \"minecraft:redstone\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:ore_data_card\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/paper.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:sawdust\"\n    },\n    {\n      \"item\": \"indrev:sawdust\"\n    },\n    {\n      \"item\": \"indrev:sawdust\"\n    },\n    {\n      \"item\": \"minecraft:water_bucket\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"minecraft:paper\",\n    \"count\": 3\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/portable_charger.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NCN\",\n    \"SBS\",\n    \"BEB\"\n  ],\n  \"key\": {\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"S\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"E\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"N\": {\n      \"item\": \"indrev:nikolite_dust\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:portable_charger\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/pulverizer_factory_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NCN\",\n    \"IFI\",\n    \"IBI\"\n  ],\n  \"key\": {\n    \"F\": {\n      \"item\": \"indrev:pulverizer_mk4\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"I\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:pulverizer_factory_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/pulverizer_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"FCF\",\n    \"NMN\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"N\": {\n      \"tag\": \"c:copper_plates\"\n    },\n    \"F\": {\n      \"item\": \"minecraft:flint\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:pulverizer_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/pump_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"FCF\",\n    \"NMN\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"N\": {\n      \"tag\": \"c:bronze_plates\"\n    },\n    \"F\": {\n      \"item\": \"minecraft:bucket\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:pump_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/rancher_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"LCF\",\n    \"NMN\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"N\": {\n      \"tag\": \"c:bronze_plates\"\n    },\n    \"F\": {\n      \"item\": \"minecraft:feather\"\n    },\n    \"L\": {\n      \"item\": \"minecraft:milk_bucket\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:rancher_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/raw_lead_block.json",
    "content": "{\n  \"type\": \"crafting_shaped\",\n  \"pattern\": [\n    \"###\",\n    \"###\",\n    \"###\"\n  ],\n  \"key\": {\n    \"#\": {\n      \"tag\": \"c:raw_lead_ores\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:raw_lead_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/raw_silver_block.json",
    "content": "{\n  \"type\": \"crafting_shaped\",\n  \"pattern\": [\n    \"###\",\n    \"###\",\n    \"###\"\n  ],\n  \"key\": {\n    \"#\": {\n      \"tag\": \"c:raw_silver_ores\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:raw_silver_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/raw_tin_block.json",
    "content": "{\n  \"type\": \"crafting_shaped\",\n  \"pattern\": [\n    \"###\",\n    \"###\",\n    \"###\"\n  ],\n  \"key\": {\n    \"#\": {\n      \"tag\": \"c:raw_tin_ores\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:raw_tin_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/raw_tungsten_block.json",
    "content": "{\n  \"type\": \"crafting_shaped\",\n  \"pattern\": [\n    \"###\",\n    \"###\",\n    \"###\"\n  ],\n  \"key\": {\n    \"#\": {\n      \"tag\": \"c:raw_tungsten_ores\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:raw_tungsten_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/recycler_mk2.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"DCD\",\n    \"NMN\",\n    \"SBS\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk2\"\n    },\n    \"N\": {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    \"D\": {\n      \"item\": \"minecraft:composter\"\n    },\n    \"S\": {\n      \"tag\": \"c:silver_plates\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:recycler_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/reinforced_elytra.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" M \",\n    \"MCM\",\n    \" M \"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"minecraft:elytra\"\n    },\n    \"M\": {\n      \"tag\": \"c:steel_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:reinforced_elytra\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/sawmill_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SCS\",\n    \"NMN\",\n    \"SBS\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"N\": {\n      \"tag\": \"c:bronze_plates\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"S\": {\n      \"tag\": \"c:silver_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:sawmill_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/screwdriver.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"  C\",\n    \" S \",\n    \"S  \"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"minecraft:iron_nugget\"\n    },\n    \"S\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:screwdriver\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/servo_output.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"III\",\n    \"IOI\"\n  ],\n  \"key\": {\n    \"I\": {\n      \"item\": \"minecraft:iron_nugget\"\n    },\n    \"O\": {\n      \"tag\": \"c:copper_nuggets\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:servo_output\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/servo_retriever.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"III\",\n    \"IOI\"\n  ],\n  \"key\": {\n    \"I\": {\n      \"item\": \"minecraft:iron_nugget\"\n    },\n    \"O\": {\n      \"item\": \"indrev:nikolite_dust\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:servo_retriever\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silo.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"BIB\",\n    \"I I\",\n    \"BIB\"\n  ],\n  \"key\": {\n    \"I\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"B\": {\n      \"tag\": \"c:tungsten_plates\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silo\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silver_axe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \"SI\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:silver_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silver_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silver_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:silver_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silver_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silver_boots.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:silver_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silver_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silver_chestplate.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"SSS\",\n    \"SSS\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:silver_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silver_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silver_helmet.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:silver_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silver_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silver_hoe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \" I\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:silver_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silver_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silver_ingot_from_nuggets.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:silver_nuggets\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silver_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silver_leggings.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:silver_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silver_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silver_pickaxe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \" I \",\n    \" I \"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:silver_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silver_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silver_shovel.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"I\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:silver_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silver_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/silver_sword.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"S\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:silver_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:silver_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/slaughter_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NCN\",\n    \"IFI\",\n    \"IBI\"\n  ],\n  \"key\": {\n    \"F\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"I\": {\n      \"tag\": \"c:bronze_plates\"\n    },\n    \"N\": {\n      \"item\": \"minecraft:golden_sword\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:slaughter_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/smelter_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" C \",\n    \"NMN\",\n    \"SBS\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"minecraft:blast_furnace\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"S\": {\n      \"tag\": \"c:steel_ingots\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:smelter_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/smoker_enhancer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" C \",\n    \"BEB\",\n    \"N N\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"B\": {\n      \"tag\": \"minecraft:logs\"\n    },\n    \"E\": {\n      \"item\": \"indrev:empty_enhancer\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:smoker_enhancer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/solar_generator_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"GCG\",\n    \"NMN\",\n    \"IBI\"\n  ],\n  \"key\": {\n    \"G\": {\n      \"item\": \"minecraft:glass\"\n    },\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"I\": {\n      \"tag\": \"c:bronze_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:nikolite_dust\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:solar_generator_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/solar_generator_mk3.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" C \",\n    \"NSN\",\n    \"EBE\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"item\": \"indrev:solar_generator_mk1\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk3\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"E\": {\n      \"tag\": \"c:electrum_plates\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:solar_generator_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/solid_infuser_factory_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NCN\",\n    \"IFI\",\n    \"IBI\"\n  ],\n  \"key\": {\n    \"F\": {\n      \"item\": \"indrev:solid_infuser_mk4\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"I\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:solid_infuser_factory_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/solid_infuser_mk1.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"GCG\",\n    \"NMN\",\n    \"NBN\"\n  ],\n  \"key\": {\n    \"M\": {\n      \"item\": \"indrev:machine_block\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"N\": {\n      \"tag\": \"c:copper_plates\"\n    },\n    \"B\": {\n      \"item\": \"indrev:battery\"\n    },\n    \"G\": {\n      \"item\": \"minecraft:glass\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:solid_infuser_mk1\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/speed_enhancer.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" E \",\n    \"OCO\",\n    \" O \"\n  ],\n  \"key\": {\n    \"O\": {\n      \"item\": \"indrev:cooler_cell\"\n    },\n    \"C\": {\n      \"item\": \"indrev:circuit_mk1\"\n    },\n    \"E\": {\n      \"item\": \"indrev:empty_enhancer\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:speed_enhancer\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/steel_axe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \"SI\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:steel_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:steel_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/steel_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:steel_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:steel_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/steel_boots.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:steel_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:steel_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/steel_chestplate.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"SSS\",\n    \"SSS\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:steel_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:steel_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/steel_helmet.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:steel_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:steel_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/steel_hoe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \" I\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:steel_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:steel_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/steel_ingot_from_nuggets.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:steel_nuggets\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:steel_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/steel_leggings.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:steel_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:steel_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/steel_pickaxe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \" I \",\n    \" I \"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:steel_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:steel_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/steel_shovel.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"I\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:steel_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:steel_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/steel_sword.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"S\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:steel_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:steel_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/stone_drill_head.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"I I\",\n    \"III\",\n    \" I \"\n  ],\n  \"key\": {\n    \"I\": {\n      \"item\": \"minecraft:stone\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:stone_drill_head\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tank.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"III\",\n    \"GBG\",\n    \"III\"\n  ],\n  \"key\": {\n    \"G\": {\n      \"item\": \"minecraft:glass\"\n    },\n    \"I\": {\n      \"tag\": \"c:steel_plates\"\n    },\n    \"B\": {\n      \"item\": \"minecraft:bucket\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tank\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tier_upgrade_mk2.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" P \",\n    \"PCP\",\n    \"NPN\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk2\"\n    },\n    \"P\": {\n      \"tag\": \"c:silver_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:nikolite_ingot\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tier_upgrade_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tier_upgrade_mk3.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" P \",\n    \"PCP\",\n    \"NPN\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk3\"\n    },\n    \"P\": {\n      \"tag\": \"c:electrum_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_dust\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tier_upgrade_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tier_upgrade_mk4.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" P \",\n    \"PCP\",\n    \"NPN\"\n  ],\n  \"key\": {\n    \"C\": {\n      \"item\": \"indrev:circuit_mk4\"\n    },\n    \"P\": {\n      \"tag\": \"c:lead_plates\"\n    },\n    \"N\": {\n      \"item\": \"indrev:enriched_nikolite_ingot\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tier_upgrade_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tin_axe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \"SI\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:tin_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tin_axe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tin_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:tin_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tin_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tin_boots.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:tin_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tin_boots\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tin_chestplate.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S S\",\n    \"SSS\",\n    \"SSS\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:tin_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tin_chestplate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tin_helmet.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:tin_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tin_helmet\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tin_hoe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SS\",\n    \" I\",\n    \" I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:tin_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tin_hoe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tin_ingot_from_nuggets.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:tin_nuggets\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tin_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tin_leggings.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \"S S\",\n    \"S S\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:tin_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tin_leggings\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tin_pickaxe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"SSS\",\n    \" I \",\n    \" I \"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:tin_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tin_pickaxe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tin_shovel.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"I\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:tin_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tin_shovel\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tin_sword.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"S\",\n    \"S\",\n    \"I\"\n  ],\n  \"key\": {\n    \"S\": {\n      \"tag\": \"c:tin_ingots\"\n    },\n    \"I\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tin_sword\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tungsten_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:tungsten_ingots\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tungsten_block\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/tungsten_ingot_from_nuggets.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"NNN\",\n    \"NNN\",\n    \"NNN\"\n  ],\n  \"key\": {\n    \"N\": {\n      \"tag\": \"c:tungsten_nuggets\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:tungsten_ingot\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/warning_strobe.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \"N\",\n    \"B\"\n  ],\n  \"key\": {\n    \"B\": {\n      \"tag\": \"c:iron_plates\"\n    },\n    \"N\": {\n      \"item\": \"minecraft:redstone\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:warning_strobe\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shaped/wrench.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shaped\",\n  \"pattern\": [\n    \" CC\",\n    \" SC\",\n    \"S  \"\n  ],\n  \"key\": {\n    \"C\": {\n      \"tag\": \"c:copper_plates\"\n    },\n    \"S\": {\n      \"item\": \"minecraft:stick\"\n    }\n  },\n  \"result\": {\n    \"item\": \"indrev:wrench\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/bronze_ingot_from_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:bronze_blocks\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:bronze_ingot\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/bronze_nugget.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:bronze_ingots\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:bronze_nugget\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/copper_ingot_from_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:copper_blocks\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"minecraft:copper_ingot\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/copper_nugget.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:copper_ingots\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:copper_nugget\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/copper_plate_from_hammer.json",
    "content": "{\n  \"type\": \"indrev:selfremainder\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:copper_ingots\"\n    },\n    {\n      \"item\": \"indrev:hammer\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:copper_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/electrum_ingot_from_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:electrum_blocks\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:electrum_ingot\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/electrum_nugget.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:electrum_ingots\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:electrum_nugget\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/gold_plate_from_hammer.json",
    "content": "{\n  \"type\": \"indrev:selfremainder\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:gold_ingot\"\n    },\n    {\n      \"item\": \"indrev:hammer\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:gold_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/guide_book.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"group\": \"books\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:book\"\n    },\n    {\n      \"item\": \"indrev:nikolite_dust\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:guide_book\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/iron_plate_from_hammer.json",
    "content": "{\n  \"type\": \"indrev:selfremainder\",\n  \"ingredients\": [\n    {\n      \"item\": \"minecraft:iron_ingot\"\n    },\n    {\n      \"item\": \"indrev:hammer\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:iron_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/lead_ingot_from_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:lead_blocks\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:lead_ingot\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/lead_nugget.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:lead_ingots\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:lead_nugget\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/planks.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:plank_block\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:planks\",\n    \"count\": 8\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/raw_lead.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:raw_lead_block\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:raw_lead\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/raw_silver.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:raw_silver_block\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:raw_silver\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/raw_tin.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:raw_tin_block\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:raw_tin\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/raw_tungsten.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:raw_tungsten_block\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:raw_tungsten\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/silver_ingot_from_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:silver_blocks\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:silver_ingot\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/silver_nugget.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:silver_ingots\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:silver_nugget\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/steel_ingot_from_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:steel_blocks\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:steel_ingot\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/steel_nugget.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:steel_ingots\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:steel_nugget\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/tin_ingot_from_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:tin_blocks\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:tin_ingot\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/tin_nugget.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:tin_ingots\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:tin_nugget\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/tin_plate_from_hammer.json",
    "content": "{\n  \"type\": \"indrev:selfremainder\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:tin_ingots\"\n    },\n    {\n      \"item\": \"indrev:hammer\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:tin_plate\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/tungsten_ingot_from_block.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:tungsten_blocks\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:tungsten_ingot\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/shapeless/tungsten_nugget.json",
    "content": "{\n  \"type\": \"minecraft:crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"tag\": \"c:tungsten_ingots\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:tungsten_nugget\",\n    \"count\": 9\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_copper_from_block.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:copper_blocks\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_copper_still\",\n    \"amount\": 81000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_copper_from_dust.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:copper_dusts\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_copper_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_copper_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:copper_ingots\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_copper_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_copper_from_nugget.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:copper_nuggets\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_copper_still\",\n    \"amount\": 1000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_copper_from_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:copper_ores\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_copper_still\",\n    \"amount\": 36000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_copper_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"indrev:copper_purified_ore\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_copper_still\",\n    \"amount\": 45000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_gold_from_block.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"minecraft:gold_block\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_gold_still\",\n    \"amount\": 81000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_gold_from_dust.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:gold_dusts\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_gold_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_gold_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"minecraft:gold_ingot\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_gold_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_gold_from_nugget.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"minecraft:gold_nugget\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_gold_still\",\n    \"amount\": 1000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_gold_from_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:gold_ores\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_gold_still\",\n    \"amount\": 36000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_gold_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"indrev:gold_purified_ore\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_gold_still\",\n    \"amount\": 45000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_iron_from_block.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"minecraft:iron_block\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_iron_still\",\n    \"amount\": 81000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_iron_from_dust.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:iron_dusts\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_iron_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_iron_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"minecraft:iron_ingot\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_iron_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_iron_from_nugget.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"minecraft:iron_nugget\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_iron_still\",\n    \"amount\": 1000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_iron_from_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"minecraft:iron_ores\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_iron_still\",\n    \"amount\": 36000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_iron_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"indrev:iron_purified_ore\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_iron_still\",\n    \"amount\": 45000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_lead_from_block.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:lead_blocks\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_lead_still\",\n    \"amount\": 81000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_lead_from_dust.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:lead_dusts\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_lead_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_lead_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:lead_ingots\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_lead_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_lead_from_nugget.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:lead_nuggets\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_lead_still\",\n    \"amount\": 1000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_lead_from_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:lead_ores\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_lead_still\",\n    \"amount\": 36000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_lead_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"indrev:lead_purified_ore\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_lead_still\",\n    \"amount\": 45000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_netherite_from_dust.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:netherite_scrap_dusts\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_netherite_still\",\n    \"amount\": 250\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_netherite_from_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"minecraft:ancient_debris\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_netherite_still\",\n    \"amount\": 750\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_netherite_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"indrev:netherite_scrap_purified_ore\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_netherite_still\",\n    \"amount\": 1000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_silver_from_block.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:silver_blocks\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_silver_still\",\n    \"amount\": 81000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_silver_from_dust.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:silver_dusts\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_silver_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_silver_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:silver_ingots\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_silver_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_silver_from_nugget.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:silver_nuggets\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_silver_still\",\n    \"amount\": 1000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_silver_from_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:silver_ores\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_silver_still\",\n    \"amount\": 36000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_silver_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"indrev:silver_purified_ore\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_silver_still\",\n    \"amount\": 45000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_tin_from_block.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:tin_blocks\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_tin_still\",\n    \"amount\": 81000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_tin_from_dust.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:tin_dusts\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_tin_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_tin_from_ingot.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:tin_ingots\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_tin_still\",\n    \"amount\": 9000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_tin_from_nugget.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:tin_nuggets\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_tin_still\",\n    \"amount\": 1000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_tin_from_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"tag\": \"c:tin_ores\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_tin_still\",\n    \"amount\": 36000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelter/molten_tin_from_purified_ore.json",
    "content": "{\n  \"type\": \"indrev:smelter\",\n  \"ingredients\": {\n    \"item\": \"indrev:tin_purified_ore\"\n  },\n  \"fluidOutput\": {\n    \"fluid\": \"indrev:molten_tin_still\",\n    \"amount\": 45000\n  },\n  \"processTime\": 600\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/bronze_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"bronze_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:bronze_dusts\"\n  },\n  \"result\": \"indrev:bronze_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/copper_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"copper_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:copper_dusts\"\n  },\n  \"result\": \"minecraft:copper_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/electrum_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"electrum_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:electrum_dusts\"\n  },\n  \"result\": \"indrev:electrum_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/gold_ingot.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"ingredient\": {\n    \"tag\": \"c:gold_dusts\"\n  },\n  \"result\": \"minecraft:gold_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/iron_ingot_from_dust.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"iron_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:iron_dusts\"\n  },\n  \"result\": \"minecraft:iron_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/lead_ingot_from_ore.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"lead_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:lead_ores\"\n  },\n  \"result\": \"indrev:lead_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/lead_ingot_from_raw_ore.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"lead_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:raw_lead_ores\"\n  },\n  \"result\": \"indrev:lead_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/lead_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"lead_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:lead_dusts\"\n  },\n  \"result\": \"indrev:lead_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/leather.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"ingredient\": {\n    \"item\": \"indrev:untanned_leather\"\n  },\n  \"result\": \"minecraft:leather\",\n  \"cookingTime\": 250\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/netherite_scrap.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"ingredient\": {\n    \"tag\": \"c:netherite_scrap_dusts\"\n  },\n  \"result\": \"minecraft:netherite_scrap\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/silver_ingot_from_ore.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"silver_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:silver_ores\"\n  },\n  \"result\": \"indrev:silver_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/silver_ingot_from_raw_ores.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"silver_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:raw_silver_ores\"\n  },\n  \"result\": \"indrev:silver_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/silver_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"silver_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:silver_dusts\"\n  },\n  \"result\": \"indrev:silver_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/steel_ingot.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"ingredient\": {\n    \"tag\": \"c:steel_dusts\"\n  },\n  \"result\": \"indrev:steel_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/tin_ingot_from_ore.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"tin_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:tin_ores\"\n  },\n  \"result\": \"indrev:tin_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/tin_ingot_from_raw_ores.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"tin_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:raw_tin_ores\"\n  },\n  \"result\": \"indrev:tin_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/tin_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"tin_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:tin_dusts\"\n  },\n  \"result\": \"indrev:tin_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/tungsten_ingot_from_ore.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"tungsten_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:tungsten_ores\"\n  },\n  \"result\": \"indrev:tungsten_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/tungsten_ingot_from_raw_ores.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"tungsten_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:raw_tungsten_ores\"\n  },\n  \"result\": \"indrev:tungsten_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/smelting/tungsten_ingot_from_smelting.json",
    "content": "{\n  \"type\": \"minecraft:smelting\",\n  \"group\": \"tungsten_ingots\",\n  \"ingredient\": {\n    \"tag\": \"c:tungsten_dusts\"\n  },\n  \"result\": \"indrev:tungsten_ingot\",\n  \"cookingTime\": 200\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/chopper_mk1_to_mk2.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:chopper_mk1\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:chopper_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/chopper_mk2_to_mk3.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:chopper_mk2\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:chopper_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/chopper_mk3_to_mk4.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:chopper_mk3\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:chopper_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/compressor_mk1_to_mk2.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:compressor_mk1\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:compressor_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/compressor_mk2_to_mk3.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:compressor_mk2\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:compressor_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/compressor_mk3_to_mk4.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:compressor_mk3\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:compressor_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/electric_furnace_mk1_to_mk2.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:electric_furnace_mk1\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:electric_furnace_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/electric_furnace_mk2_to_mk3.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:electric_furnace_mk2\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:electric_furnace_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/electric_furnace_mk3_to_mk4.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:electric_furnace_mk3\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:electric_furnace_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/electrolytic_separator_mk1_to_mk2.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:electrolytic_separator_mk1\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:electrolytic_separator_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/electrolytic_separator_mk2_to_mk3.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:electrolytic_separator_mk2\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:electrolytic_separator_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/electrolytic_separator_mk3_to_mk4.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:electrolytic_separator_mk3\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:electrolytic_separator_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/farmer_mk1_to_mk2.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:farmer_mk1\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:farmer_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/farmer_mk2_to_mk3.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:farmer_mk2\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:farmer_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/farmer_mk3_to_mk4.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:farmer_mk3\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:farmer_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/fluid_infuser_mk1_to_mk2.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:fluid_infuser_mk1\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:fluid_infuser_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/fluid_infuser_mk2_to_mk3.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:fluid_infuser_mk2\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:fluid_infuser_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/fluid_infuser_mk3_to_mk4.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:fluid_infuser_mk3\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:fluid_infuser_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/pulverizer_mk1_to_mk2.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:pulverizer_mk1\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:pulverizer_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/pulverizer_mk2_to_mk3.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:pulverizer_mk2\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:pulverizer_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/pulverizer_mk3_to_mk4.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:pulverizer_mk3\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:pulverizer_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/rancher_mk1_to_mk2.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:rancher_mk1\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:rancher_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/rancher_mk2_to_mk3.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:rancher_mk2\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:rancher_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/rancher_mk3_to_mk4.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:rancher_mk3\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:rancher_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/sawmill_mk1_to_mk2.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:sawmill_mk1\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:sawmill_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/sawmill_mk2_to_mk3.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:sawmill_mk2\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:sawmill_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/sawmill_mk3_to_mk4.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:sawmill_mk3\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:sawmill_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/slaughter_mk1_to_mk2.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:slaughter_mk1\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:slaughter_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/slaughter_mk2_to_mk3.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:slaughter_mk2\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:slaughter_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/slaughter_mk3_to_mk4.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:slaughter_mk3\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:slaughter_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/solid_infuser_mk1_to_mk2.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:solid_infuser_mk1\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk2\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:solid_infuser_mk2\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/solid_infuser_mk2_to_mk3.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:solid_infuser_mk2\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk3\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:solid_infuser_mk3\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/recipes/upgrade/solid_infuser_mk3_to_mk4.json",
    "content": "{\n  \"type\": \"crafting_shapeless\",\n  \"ingredients\": [\n    {\n      \"item\": \"indrev:solid_infuser_mk3\"\n    },\n    {\n      \"item\": \"indrev:tier_upgrade_mk4\"\n    }\n  ],\n  \"result\": {\n    \"item\": \"indrev:solid_infuser_mk4\"\n  }\n}"
  },
  {
    "path": "src/main/resources/data/indrev/tags/items/coolers.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:fan\",\n    \"indrev:cooler_cell\",\n    \"indrev:heatsink\",\n    \"indrev:heat_coil\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/blocks/mineable/axe.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:biomass_composter\",\n    \"indrev:plank_block\",\n    \"indrev:planks\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/blocks/mineable/pickaxe.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:compressor_factory_mk4\",\n    \"indrev:coal_generator_mk1\",\n    \"indrev:drill_middle\",\n    \"indrev:biomass_generator_mk3\",\n    \"indrev:duct\",\n    \"indrev:lazuli_flux_container_mk4\",\n    \"indrev:modular_workbench_mk4\",\n    \"indrev:lazuli_flux_container_mk3\",\n    \"indrev:lazuli_flux_container_mk2\",\n    \"indrev:raw_tungsten_block\",\n    \"indrev:lazuli_flux_container_mk1\",\n    \"indrev:solid_infuser_creative\",\n    \"indrev:farmer_creative\",\n    \"indrev:bronze_block\",\n    \"indrev:electric_furnace_creative\",\n    \"indrev:machine_block\",\n    \"indrev:charge_pad_mk4\",\n    \"indrev:raw_tin_block\",\n    \"indrev:raw_silver_block\",\n    \"indrev:lazuli_flux_container_creative\",\n    \"indrev:recycler_mk2\",\n    \"indrev:dirt_oxygenator_mk1\",\n    \"indrev:deepslate_silver_ore\",\n    \"indrev:compressor_creative\",\n    \"indrev:lead_block\",\n    \"indrev:electric_furnace_mk1\",\n    \"indrev:electric_furnace_mk2\",\n    \"indrev:electric_furnace_mk3\",\n    \"indrev:electric_furnace_mk4\",\n    \"indrev:pulverizer_mk2\",\n    \"indrev:pulverizer_mk1\",\n    \"indrev:pulverizer_mk4\",\n    \"indrev:pulverizer_mk3\",\n    \"indrev:rancher_creative\",\n    \"indrev:steam_turbine_energy_output\",\n    \"indrev:data_card_writer_mk4\",\n    \"indrev:capsule\",\n    \"indrev:drill_bottom\",\n    \"indrev:steel_block\",\n    \"indrev:drain_mk1\",\n    \"indrev:raw_lead_block\",\n    \"indrev:tungsten_ore\",\n    \"indrev:lead_ore\",\n    \"indrev:electrolytic_separator_mk4\",\n    \"indrev:electrolytic_separator_mk3\",\n    \"indrev:electrolytic_separator_mk2\",\n    \"indrev:electrolytic_separator_mk1\",\n    \"indrev:cable_mk1\",\n    \"indrev:cable_mk3\",\n    \"indrev:cable_mk2\",\n    \"indrev:cable_mk4\",\n    \"indrev:tin_ore\",\n    \"indrev:pulverizer_factory_mk4\",\n    \"indrev:deepslate_tungsten_ore\",\n    \"indrev:heat_generator_mk4\",\n    \"indrev:steam_turbine_steam_input_valve\",\n    \"indrev:silver_block\",\n    \"indrev:solar_power_plant_tower\",\n    \"indrev:frame\",\n    \"indrev:solid_infuser_factory_mk4\",\n    \"indrev:nikolite_ore\",\n    \"indrev:fluid_pipe_mk4\",\n    \"indrev:fluid_pipe_mk3\",\n    \"indrev:fluid_pipe_mk2\",\n    \"indrev:fluid_pipe_mk1\",\n    \"indrev:solar_generator_mk3\",\n    \"indrev:solar_generator_mk1\",\n    \"indrev:compressor_mk1\",\n    \"indrev:compressor_mk3\",\n    \"indrev:compressor_mk2\",\n    \"indrev:compressor_mk4\",\n    \"indrev:item_pipe_mk1\",\n    \"indrev:item_pipe_mk2\",\n    \"indrev:item_pipe_mk3\",\n    \"indrev:item_pipe_mk4\",\n    \"indrev:steam_turbine_mk4\",\n    \"indrev:condenser_mk4\",\n    \"indrev:tank\",\n    \"indrev:chopper_mk1\",\n    \"indrev:tin_block\",\n    \"indrev:chopper_mk4\",\n    \"indrev:chopper_mk3\",\n    \"indrev:chopper_mk2\",\n    \"indrev:tungsten_block\",\n    \"indrev:sulfur_crystal\",\n    \"indrev:gas_generator_mk4\",\n    \"indrev:cabinet\",\n    \"indrev:slaughter_mk1\",\n    \"indrev:slaughter_mk4\",\n    \"indrev:slaughter_mk2\",\n    \"indrev:slaughter_mk3\",\n    \"indrev:farmer_mk3\",\n    \"indrev:farmer_mk2\",\n    \"indrev:farmer_mk4\",\n    \"indrev:farmer_mk1\",\n    \"indrev:fluid_valve\",\n    \"indrev:steam_turbine_casing\",\n    \"indrev:electrolytic_separator_creative\",\n    \"indrev:fluid_infuser_creative\",\n    \"indrev:intake\",\n    \"indrev:solar_receiver\",\n    \"indrev:pulverizer_creative\",\n    \"indrev:silver_ore\",\n    \"indrev:deepslate_tin_ore\",\n    \"indrev:drill_top\",\n    \"indrev:controller\",\n    \"indrev:slaughter_creative\",\n    \"indrev:heliostat\",\n    \"indrev:silo\",\n    \"indrev:solid_infuser_mk3\",\n    \"indrev:solid_infuser_mk4\",\n    \"indrev:solid_infuser_mk1\",\n    \"indrev:solid_infuser_mk2\",\n    \"indrev:electrum_block\",\n    \"indrev:steam_turbine_rotor\",\n    \"indrev:fluid_infuser_mk4\",\n    \"indrev:fluid_infuser_mk3\",\n    \"indrev:fluid_infuser_mk2\",\n    \"indrev:fluid_infuser_mk1\",\n    \"indrev:deepslate_lead_ore\",\n    \"indrev:sawmill_creative\",\n    \"indrev:rancher_mk4\",\n    \"indrev:rancher_mk2\",\n    \"indrev:rancher_mk3\",\n    \"indrev:rancher_mk1\",\n    \"indrev:steam_turbine_pressure_valve\",\n    \"indrev:warning_strobe\",\n    \"indrev:sawmill_mk2\",\n    \"indrev:sawmill_mk3\",\n    \"indrev:sawmill_mk1\",\n    \"indrev:sawmill_mk4\",\n    \"indrev:smelter_mk4\",\n    \"indrev:resistant_glass\",\n    \"indrev:deepslate_nikolite_ore\",\n    \"indrev:laser_emitter_mk4\",\n    \"indrev:mining_rig_mk4\",\n    \"indrev:fisher_mk4\",\n    \"indrev:fisher_mk2\",\n    \"indrev:fisher_mk3\",\n    \"indrev:wither_proof_obsidian\",\n    \"indrev:chopper_creative\",\n    \"indrev:electric_furnace_factory_mk4\",\n    \"indrev:pump_mk1\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/blocks/wither_immune.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:wither_proof_obsidian\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/fluids/lava.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:molten_netherite_still\",\n    \"indrev:molten_netherite_flowing\",\n    \"indrev:molten_iron_still\",\n    \"indrev:molten_iron_flowing\",\n    \"indrev:molten_copper_still\",\n    \"indrev:molten_copper_flowing\",\n    \"indrev:molten_gold_still\",\n    \"indrev:molten_gold_flowing\",\n    \"indrev:molten_tin_still\",\n    \"indrev:molten_tin_flowing\",\n    \"indrev:molten_lead_still\",\n    \"indrev:molten_lead_flowing\",\n    \"indrev:molten_silver_still\",\n    \"indrev:molten_silver_flowing\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/data/minecraft/tags/fluids/water.json",
    "content": "{\n  \"replace\": false,\n  \"values\": [\n    \"indrev:coolant_still\",\n    \"indrev:coolant_flowing\",\n    \"indrev:sulfuric_acid_still\",\n    \"indrev:sulfuric_acid_flowing\",\n    \"indrev:toxic_mud_still\",\n    \"indrev:toxic_mud_flowing\"\n  ]\n}"
  },
  {
    "path": "src/main/resources/fabric.mod.json",
    "content": "{\n  \"schemaVersion\": 1,\n  \"id\": \"indrev\",\n  \"version\": \"${version}\",\n  \"name\": \"Industrial Revolution\",\n  \"description\": \"Every other industrial mod ever\",\n  \"authors\": [\n    \"Gabriel Henrique de Oliveira\"\n  ],\n  \"contributors\": [\n  ],\n  \"contact\": {\n    \"homepage\": \"https://github.com/GabrielOlvH/Industrial-Revolution\",\n    \"sources\": \"https://github.com/GabrielOlvH/Industrial-Revolution\"\n  },\n  \"license\": \"Apache-2.0\",\n  \"icon\": \"assets/indrev/icon.png\",\n  \"environment\": \"*\",\n  \"entrypoints\": {\n    \"main\": [\n      {\n        \"adapter\": \"kotlin\",\n        \"value\": \"me.steven.indrev.IndustrialRevolution\"\n      }\n    ],\n    \"client\": [\n      {\n        \"adapter\": \"kotlin\",\n        \"value\": \"me.steven.indrev.IndustrialRevolutionClient\"\n      }\n    ],\n    \"rei\": [\n      {\n        \"adapter\": \"kotlin\",\n        \"value\": \"me.steven.indrev.compat.rei.REIPlugin\"\n      }\n    ],\n    \"fabric-datagen\": [\n      {\n        \"adapter\": \"kotlin\",\n        \"value\": \"me.steven.indrev.datagen.IndustrialRevolutionDatagen\"\n      }\n    ]\n  },\n  \"custom\": {\n    \"dashloader:customobject\":  [\n      \"me.steven.indrev.compat.dashloader.models.DashCableModel\",\n      \"me.steven.indrev.compat.dashloader.models.DashFluidPipeModel\",\n      \"me.steven.indrev.compat.dashloader.models.DashItemPipeModel\",\n      \"me.steven.indrev.compat.dashloader.models.DashLazuliFluxContainerModel\",\n      \"me.steven.indrev.compat.dashloader.models.DashMachineModel\",\n      \"me.steven.indrev.compat.dashloader.models.DashMinerModel\",\n      \"me.steven.indrev.compat.dashloader.models.DashTankModel\"\n    ]\n  },\n  \"mixins\": [\n    \"indrev.mixins.json\"\n  ],\n  \"accessWidener\": \"indrev.accesswidener\",\n  \"depends\": {\n    \"fabricloader\": \">=0.7.1\",\n    \"fabric\": \">=0.32.0\",\n    \"fabric-language-kotlin\": \">=1.6.0\",\n    \"minecraft\": \">=1.18.2\",\n    \"libgui\": \"*\"\n  }\n}\n"
  },
  {
    "path": "src/main/resources/indrev.accesswidener",
    "content": "accessWidener\tv1\tnamed\n\n# Used for compat between cooking recipes and IR machines\naccessible field net/minecraft/recipe/AbstractCookingRecipe input Lnet/minecraft/recipe/Ingredient;\n\n# Used to simulate enchantments\naccessible field net/minecraft/predicate/item/EnchantmentPredicate enchantment Lnet/minecraft/enchantment/Enchantment;\naccessible field net/minecraft/predicate/item/EnchantmentPredicate levels Lnet/minecraft/predicate/NumberRange$IntRange;\n\n# Used to get the fluid when draining it\naccessible field net/minecraft/block/FluidBlock fluid Lnet/minecraft/fluid/FlowableFluid;\n\n# Used by the jetpack\naccessible field net/minecraft/entity/LivingEntity jumping Z\n\n# Used by crafting machines\naccessible field net/minecraft/recipe/RecipeManager recipes Ljava/util/Map;\naccessible method net/minecraft/recipe/RecipeManager getAllOfType (Lnet/minecraft/recipe/RecipeType;)Ljava/util/Map;\n\n# Used to override vanilla's property syncing\naccessible field net/minecraft/screen/ScreenHandler properties Ljava/util/List;\naccessible field net/minecraft/screen/ScreenHandler trackedPropertyValues Lit/unimi/dsi/fastutil/ints/IntList;\n\n# Used to calculate random results\naccessible field net/minecraft/util/collection/WeightedList entries Ljava/util/List;\n\n# Used by the rancher\naccessible method net/minecraft/entity/passive/AnimalEntity eat (Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;Lnet/minecraft/item/ItemStack;)V"
  },
  {
    "path": "src/main/resources/indrev.mixins.json",
    "content": "{\n  \"required\": true,\n  \"package\": \"me.steven.indrev.mixin\",\n  \"compatibilityLevel\": \"JAVA_8\",\n  \"plugin\": \"me.steven.indrev.mixin.aprilfools.AprilFoolsMixinConfigPlugin\",\n  \"mixins\": [\n    \"common.MixinAbstractCookingRecipe\",\n    \"common.MixinEnchantmentHelper\",\n    \"common.MixinEntity\",\n    \"common.MixinItemPredicate\",\n    \"common.MixinItemStack\",\n    \"common.MixinLivingEntity\",\n    \"common.MixinPiglinBrain\",\n    \"common.MixinPlayerEntity\",\n    \"common.MixinPlayerInventory\",\n    \"common.MixinServerPlayerEntity\",\n    \"common.MixinServerWorld\"\n  ],\n  \"client\": [\n    \"client.MixinBuiltChunk\",\n    \"client.MixinClientPlayerInteractionManager\",\n    \"client.MixinGameRenderer\",\n    \"client.MixinItemRenderer\",\n    \"client.MixinLivingEntityClient\",\n    \"client.MixinMinecraftClient\",\n    \"client.MixinWItemSlot\"\n  ],\n  \"injectors\": {\n    \"defaultRequire\": 1\n  }\n}\n"
  }
]